Ryan Shang

生死看淡,不服就干

0%

Vue.js学习笔记

库和框架

  • 别人写好的内容由我们主动调用
  • 常见库:jQuery Underscore Zepto Animate.css

框架

  • 拥有完整的解决方案,我们写好内容交给框架调用
  • 常见框架:vue angular react backbone

架构模式

MVC模式 单项数据绑定 backbone

  • Model 模型
  • View 视图
  • Controller 控制器

MVVM模式 双向数据绑定 vue,angular

  • Model 模型
  • View 视图
  • ViewModel 视图模型

vue

vue的优点

  • 只关注视图层,不需要操作DOM
  • 轻量级,压缩后只有不到20k
  • 渐进式框架,根据需求选择

vue的兼容性

​ Vue.js 不支持 IE8 及其以下版本,因为 Vue.js 使用了 IE8 不能模拟的 ECMAScript 5 特性。

vue的安装

1
2
# 最新稳定版
$ npm install vue

{ { } }

​ { { } }取值表达式,通过{ { } }来进行取值,默认可以不写this,支持表达式、赋值运算、计算和三元表达式 。尽量少写逻辑运算(computed)

指令 v-开头的行内属性

  • v-model 实现双向数据绑定(看到表单元素加v-model),忽略掉value,checked,selected,将数据绑定的视图上,视图修改后会影响数据的变化。

  • v-html 把html字符串渲染成html标签

  • v-text 把数据渲染成文本,{ { } }是v-text简写写法

  • v-for 循环(数组,对象,字符串,数字)

  • v-on 事件 v-on:click->@click 绑定给DOM元素,函数需要定义在methods中,不能和data中的内容重名,this指向实例,不能使用箭头函数,事件源对象如果不写括号,可以自动传入,否则只能手动传入$event

    • 修饰符 .number数字 .lazy离开修饰符

    • 按键修饰符 .enter .ctrl .keyCode

    • 事件相关

      • @事件.stop stopPropagation,cancelBubble=trued 阻止事件传播
      • @事件.capture xxx.addEventLister(‘click’,fn,true)
      • @事件.prevent preventDefault,returnValue=false
      • @事件.once jQuery once; on ‘click’ off ‘click’
      • @事件.self e.srcElement&&e.target 判断事件源绑定事件
  • v-if 操作DOM

  • v-else-if 操作DOM

  • v-else 操作DOM

  • v-show 样式上的显示和隐藏 频繁的控制显示和隐藏

  • v-once 数据只渲染一次,数据再变化是不会导致视图刷新

  • v-cloak 相应比较慢的时候,会看到{ { } },出现闪烁的效果,防止闪烁效果出现

  • v-bind 动态绑定属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <div id="app">
    <!--:class绑定的样式和class绑定的不冲突-->
    <!--1.{className:isActive}-->
    <div class="x" :class="{z:flag,y:true}">啦啦啦</div>
    <div class="x" :class="['y','z']">哈哈哈</div>
    <div class="x" :class="[class1,{z:true}]">呵呵呵</div>
    <div class="x" :class="{true:'y',false:'z'}[true]">不怎么用</div>
    <div v-for="(a,index) in 10" :class="{x:index%2===0}">{{a}}</div>
    <!--动态绑定样式-->
    <div style="font-size: 30px;" :style="{backgroundColor:'red',color:'pink'}">嘿嘿嘿</div>
    <div :style="[sty1,sty2,{fontSize:'30px'}]">略略略</div>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    let vm = new Vue({
    el:'#app',
    data:{
    flag:true,
    class1:'y',
    sty1:{backgroundColor:'red'},
    sty2:{color:'pink'}
    }
    });

data

保存变量

1
<div id="app">{{val}}</div>
1
2
3
4
5
6
let vm = new Vue({
el:'#app',
data:{
val:'Hello world!'
}
});

methods

定义需要执行的使用的函数方法

1
<div id="app">{{'123'| my}}</div>
1
2
3
4
5
6
7
8
let vm = new Vue({
el:'#app',
filters:{
my(data,param1,param2){
//do something
}
}
});

filters 过滤器

对数据的显示效果进行处理

1
<div id="app">{{'123'| my}}</div>
1
2
3
4
5
6
7
8
let vm = new Vue({
el:'#app',
filters:{
my(data,param1,param2){
//do something
}
}
});

computed 计算“属性”,不是方法

计算属性的值,不需要在data中再次声明

1
2
3
4
<div id="app">
全选<input type="checkbox" v-model="checkAll"><br>
<input type="checkbox" v-for="product in products" v-model="product.isSelected">
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let vm = new Vue({
el: '#app',
data: {
products: [{isSelected: true}, {isSelected: true}, {isSelected: true}]
},
computed: {
checkAll: {
get() {
return this.products.every(item => item.isSelected);
},
set(val) { //val是给checkAll复制的时候传递过来的
this.products.forEach(item => {
item.isSelected = val;
});
}
}
}
});

watch 监测属性变化

监测属性变化,属性变化则执行对应方法

1
2
3
4
<div id="app">
<input type="text" v-model="a">
{{msg}}
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let vm = new Vue({
el: '#app',
data: {
a: '',
msg: ''
},
watch: { //只有值变化时才会触发,支持异步,其他情况我们更善于使用computed
a(newVal, oldVal) { //方法名要和观察对象一致
setTimeout(() => {
if (newVal.length < 3) {
this.msg = '太少';
return;
}
if (newVal.length > 6) {
this.msg = '太多';
return;
}
this.msg = '';
}, 1000);
}
}
});

生命周期:

beforeCreate()

​ 触发时机:初始化内部方法和生命周期方法后

created()

​ 触发时机:注入数据,完成双向数据绑定后

​ 一般作用:获取ajax,初始化操作

beforeMount()

​ 触发时机:挂载实例和编译模板后,要保证有编译的元素才能执行

mounted()

​ 触发时机:真实DOM渲染完成

​ 一般作用:DOM操作

​ tips:真实DOM的渲染是异步操作,需要等待DOM渲染完成后来获取。如果数据变化后,想获取真实DOM中的内容,需要等待页面渲染完成后再去获取,所有的DOM操作,最好放在nextTick中执行

beforeUpdate()

​ 触发时机:页面依赖的数据需要变化时

​ 一般作用:不用,用watch代替

updated()

​ 触发时机:页面依赖的数据变化,虚拟DOM重新渲染后

​ 一般作用:不用,用watch代替

beforeDestroy()

​ 触发时机:实例销毁前,销毁的是监听

​ 一般作用:清除定时器,清除事件绑定

destroyed()

​ 触发时机:实例销毁后

实例常用方法

  • this.$mount 挂载实例的另一种方法
  • this.$data 实例上的数据
  • this.$watch 监控
  • this.$el 当前el元素
  • this.$set 后加的属性实现响应式变化
  • this.$options 实例上的其他属性
  • this.$refs ref放在DOM上用来获取DOM元素;放在组件上获取的是组件的实例,并不是组件的DOM元素
  • this.$nextTick 将回调延迟到下次 DOM 更新循环之后执行

组件

概念

​ vue把一个自定义标签看作一个组件,vue可以赋予自定义标签一些意义

优点

  • 提高开发效率
  • 方便重复利用
  • 便于协同开发
  • 更容易被管理和维护

用途

  • 页面级组件:一个页面是一个组件
  • 基础组件:将可用的部分抽离出来

用法

全局组件:可以声明一次在任何地方使用,写插件的时候用全局属性多一点

1
2
3
4
5
6
7
8
Vue.component('my-component', { //一个对象可以看成一个组件
template: '<div>Component {{msg}}</div>',
data() { //组件中数据必须是函数类型,返回一个实例作为组件的值
return {
msg: 'Test'
};
}
});

局部组件:必须告诉这个组件属于哪个实例

​ 局部组件创建步骤:

​ 1. 创建组件

​ 2. 注册组件

​ 3. 引用组件

​ 组件是相互独立的,不能直接跨作用域,实力也是一个组件,组件中拥有生命周期函数

​ 子组件不能直接使用父组件的使用(组件之间数据交互)

​ 组件理论上可以无限嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let component = {
template: '<div>Component {{msg}}</div>',
data() {
return {
msg: 'Test'
}
}
};

let vm = new Vue({
el: '#app',
data: {
msg: 'Test' //子组件获取不到
},
components: {
component
}
});

命名

  • 组件名不用大写,多个单词用-连接
  • 只要组件名和定义名字相同是可以的(首字母可以大写)
  • html采用短横线隔开命名法,js中采用驼峰命名法

嵌套组件

​ 如果要在一个组件中使用另一个组件,先保证使用的组件是真实存在的,在需要引用这个组件的实例上通过components注册这个组件,组件需要在父级的模板中通过标签的形式引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let grandson = {template: '<div>Grandson</div>'};
let son = {
template: '<div>Son<grandson></grandson></div>',
components: {
grandson
}
};
let parent = {
template: '<div>Parent<son></son></div>',
components: {
son
}
};

let vm = new Vue({
el: '#app',
data: {},
template:'<parent></parent>',
components:{
parent
}
});

组件间数据传输——父传子(props)

​ 给子组件上添加一个属性,绑定对应的父组件,然后给子组件中增加props属性,把在子组件上添加的属性写进去。

1
2
3
4
5
<div id="app">
父亲:{{money}}
<!--当前组件的属性=父级的值-->
<child :m="money"></child><!--给儿子加了一个m属性,属性对应的数据是属于父亲的-->
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let vm = new Vue({
el: '#app',
data: {money: 400},
components: {
child: {
props: {
m: { //校验属性的类型,如果不带冒号,肯定是字符串,:m='1'数字 :m='true'布尔
type: [String, Boolean, Number, Function, Object, Array],
// default: 0, //可以给m赋特殊值,如果不传默认会调用default
required: true, //此属性是必须传递,但是不能和default同用
validator(val) {
return val > 300; //自定义校验器(用的不多)
}
} //对象的形式可以校验
},
// props:['m'], //this.m=100; 会在当前子组件上声明一个m属性值是父亲的
template: '<div>儿子:{{m}}</div>'
}
}
});

组件间数据传输——子传父(emit)

​ 父组件绑定好一些事件,子组件触发这个事件,将这个参数传进去,单项数据流,父组件数据刷新,子组件数据刷新

1
2
3
4
5
<div id="app">
父亲:{{money}}
<!--child.on('child-msg',things)-->
<child :m="money" @child-msg="things"></child>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let vm = new Vue({
el: '#app',
data: {
money: 200
},
methods: {
things(val) {
this.money = val;
}
},
components: {
child: {
props: ['m'],
template: '<div>儿子:{{m}} <br/><button @click="getMoney">多来点</button></div>',
methods: {
getMoney() {
this.$emit('child-msg', this.m * 2); //触发自己的自定义事件,都让父亲的方法执行
}
}
}
}
});

语法糖:

1
2
3
4
5
6
<div id="app">
父亲:{{money}}
<!--<child :m="money" @update:m="val => this.money = val;"></child>-->
<child :m.sync="money"></child>
<!--写的时候还是按照原有的写法即可-->
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let vm = new Vue({
el: '#app',
data: {
money: 200
},
components: {
child: {
props: ['m'],
template: '<div>儿子:{{m}} <br/><button @click="getMoney">多来点</button></div>',
methods: {
getMoney() {
this.$emit('update:m', this.m * 2);
}
}
}
}
});

组件间数据传输——兄弟组件(EventBus)

​ 通过新建一个vue实例传递事件

1
2
3
4
<div id="app">
<brother1></brother1>
<brother2></brother2>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
let EventBus = new Vue();

let brother1 = {
template: '<div>{{color}}<button @click="change">变绿</button></div>',
data() {
return {
color: '绿色',
old: '绿色'
}
},
created() {
EventBus.$on('changeRed', val => { //页面一加载兄弟1,长个耳朵听
this.color = val;
});
},
methods: {
change() {
EventBus.$emit('changeGreen', this.old);
}
}
};
let brother2 = {
template: '<div>{{color}}<button @click="change">变红</button></div>',
data() {
return {
color: '红色',
old: '红色'
}
},
created() {
EventBus.$on('changeGreen', val => {
this.color = val;
});
},
methods: {
change() {
EventBus.$emit('changeRed', this.old);
}
}
};
let vm = new Vue({
el: '#app',
data: {},
components: {
brother1, brother2
}
});

插槽slot

​ 插槽slot可以在组件中通过给DOM元素赋slot属性,在模板中使用slot标签配合name属性把slot标签替换为对应slot属性的DOM元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
<!--这里放的内容均属父级当前模板的,只有属性名属于组件的-->
<modal>
<a href="http://www.ryanshang.com">Ryan</a>
<p slot="content">亲,确认删除么?</p>
<h1 slot="title" @click="fn">是否删除?</h1>
<a href="http://www.ryanshang.com">Ryan</a>
</modal>
<!--<modal><span>是否确认?</span></modal>-->
<!--<modal></modal>-->
</div>
<!--模板中只能有一个根元素-->
<!--可以通过元素属性定制模板-->
<template id="modal">
<div>
<!--slot的作用:定制模板-->
<!--slot可以放置一些默认内容,如果传递了内容则替换掉-->
<!--如果没有名字的标签默认会放到default中-->
<slot name="title">默认标题</slot>
<slot name="content">默认内容</slot>
<slot name="default">这是一个默认标题</slot>
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let modal={
template:'#modal', //把teplate内容放到html中的template标签中,通过id名关联
};

let vm = new Vue({
el:'#app',
data:{},
components:{
modal
},
methods:{
fn(){alert(1);}
}
});

keep-alive

​ 一般用作缓存,如果已缓存则不会再执行created、mounted钩子函数

​ 子组件和父组件同时拥有mounted方法,先走子组件

1
2
3
4
5
6
7
<div id="app">
<input type="radio" v-model="radio" value="home">home
<input type="radio" v-model="radio" value="list">list
<keep-alive>
<component :is="radio"></component>
</keep-alive>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
let home={
template:'<div>home</div>',
mounted(){
alert('home');
},
beforeDestroy(){
alert('销毁');
}
};
let list={
template:'<div>list</div>',
mounted(){
alert('list');
},
beforeDestroy(){
alert('销毁');
}
};

let vm = new Vue({
el:'#app',
components:{
home,list
},
data:{
radio:home
},
mounted(){
alert('vm');
}
});

router

​ 访问不同的路径,返回不同的结果,常用于单页面应用(spa,single page application),单页面应用常用开发模式:

  • hash模式:开发时使用,不会导致404,但是不支持SEO
  • h5的history.pushStatus:上线使用

使用

1
2
3
4
5
6
<div id="app">
<router-link to="/home" tag="button">home</router-link>
<router-link to="/list" tag="button">list</router-link>
<router-view></router-view>
<!--router-view是一个全局组件,可以直接使用-->
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let home = {
template: '<div>首页</div>'
};

let list = {
template: '<div>列表页</div>'
};

let routes = [ //路由的映射表,配置路径和组件的关系
{path: '/home', component: home}, //配置的关系就是页面级组件
{path: '/list', component: list} //路径必须加斜线
];

let router = new VueRouter({ //引入vue-router自带VueRouter类
//mode:'history', //h5模式
routes,
linkActiveClass:'active'
});

let vm = new Vue({
el: '#app',
router
});

多级路由

1
2
3
4
5
6
7
8
9
10
11
12
<div id="app">
<router-link to="/home">首页</router-link>
<router-link to="/detail">详情</router-link>
<router-view></router-view>
</div>
<template id="detail">
<div>
<router-link to="/detail/profile">个人信息</router-link>
<router-link to="/detail/about">关于</router-link>
<router-view></router-view>
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
let home = {
template: '<div>home</div>'
};

let detail = {
template: '#detail'
};

let profile = {
template: '<div>profile</div>'
};

let about = {
template: '<div>about</div>'
};

let routes = [
{
path: '/home',
component: home
},
{
path: '/detail',
component: detail,
children: [ //child中路径永远不带/,带/表示是1级路由
{path: 'profile', component: profile},
{path: 'about', component: about},
]
}
];

let router = new VueRouter({
routes
});

let vm = new Vue({
el: '#app',
data: {},
router
});

路由参数

1
2
3
4
5
6
7
<div id="app">
<!--如果使用对象作为to的属性值,并且使用了参数,必须给路由起名,并通过名字跳转-->
<router-link :to="{name:'pro',params:{c:1,a:2}}">商品1</router-link>
<router-link to="/article/2/b">商品2</router-link>
<router-link to="/article/3/c">商品3</router-link>
<router-view></router-view>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
let article = {
template: '<div>第 {{$route.params.c}} 篇文章</div>',
watch: { //路径参数发生变化,通过监控参数变化来发送ajax
$route() { //路径每次变化,$route每次重新赋值
alert('发送ajax请求');
}
}
};
// /article/1/a 匹配出一个对象
// /article/:c/:a => {c:1,a:'a'} = this.$route.params
let routes = [
{
path: '/article/:c/:a',
component: article,
name: 'pro'
}
];

let router = new VueRouter({
routes
});

let vm = new Vue({
el: '#app',
data: {},
router
});