数据绑定

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../vue.js"></script>
<title>Demo1</title>
</head>
<body>
<div id="root">
<!-- 插值语法,使用两个大括号 -->
<!-- {{XXX}}中的 XXX 要写 JS 表达式,且 XXX 可以自动读取到 data 中的所有属性 -->
<h1>hello:{{name.toUpperCase()}} !</h1>

<!-- 指令语法,用于解析标签(标签属性、标签体内容、绑定事件...) -->
<h4>
<!-- 属性前加上 v-bind: 属性绑定 -->
<input v-bind:value="url"><br>
<!-- v-bind: 可简写为 : -->
单项数据绑定:<input :value="url"><br>

<!-- v-model 双向绑定 -->
<!-- 只可用于表单类元素(input、select 等),v-model:value 可简写为 v-model -->
<input v-model:value="url"><br>
双向数据绑定:<input v-model="url"><br>
</h4>
</div>

<script>
Vue.config.productionTip = false;
// Vue 实例与容器是一对一的关系
new Vue({
// el 属性用于指定当前 Vue 实例所服务的容器,通常为 CSS 选择器字符串
el : "#root",
// data 属性的值可为对象,在容器中用两个大括号引用
data : {
name : "zhangsan",
url : "http://gdpolar.top"
}
});
</script>
</body>
</html>

el与 data 的另外一种写法

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../vue.js"></script>

<title>Document</title>
</head>
<body>
<div id="root">
<h1>hello, {{name}}</h1>
</div>
</body>
<script>
vm = new Vue({
// data 的另一种写法:函数式,必须返回一个对象
data() {
return {
name : "zhangsan"
}
}
});
// 另一种写法将实例对象挂载到元素上
vm.$mount("#root");
</script>
</html>

MVVM 模型

  • MVVM 模型

    M:模型(Model) :data 中的数据
      V:视图(View) :模板代码
      VM:视图模型(View Model):Vue 实例
    
  • 注意到:

    1、data 中所有的属性, 最后都出现在了 vm 身上。
      2、vm 身上所有的属性及 Vue 原型上所有属性, 在 Vue 模板中都可以直接使用。
    
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../vue.js"></script>

<title>Document</title>
</head>
<body>
<div id="root">
<h1>hello, {{name}}</h1>

</div>
</body>
<script>
vm = new Vue({
data() {
return {
name : "zhangsan"
}
}
});
vm.$mount("#root");
</script>
</html>

Object.defineProperty

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
48
49
50
51
52
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
let person = {
name : "zhangsan"
};
// 等价于
Object.defineProperty(person, "name", {
value : "zhangsan",
configurable : true,
writable : true,
enumerable : true
});

let myCar = {};
Object.defineProperty(myCar, "band", {
value : "wuling"
});
// 等价于
Object.defineProperty(myCar, "band", {
value : "wuling",
configurable : false,
writable : false,
enumerable : false
});


let agenumber = 18;
let st = {
name : "lisi"
};
// 每次访问 st 的 age 属性,都会调用 getter
Object.defineProperty(st, "age", {
get() {
return agenumber;
},
set(v) {
agenumber = v;
}
});
console.log(st);

</script>
</head>
<body>
</body>
</html>

数据代理

  • Vue 中的数据代理:

    通过 vm 对象来代理 data 对象中属性的操作(读/写)
    
  • Vue 中数据代理的好处:

    更加方便的操作 data 中的数据
    
  • 基本原理:

    通过 Object.defineProperty() 把 data 对象中所有属性添加到 vm 上,为每一个添加到 vm 上的属性,都指定一个 getter/setter,在 getter/setter 内部去操作(读/写)data 中对应的属性。
    
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../vue.js"></script>
</head>
<body>
<div id="root">
<h1>hello, {{name}}</h1>
</div>
</body>
<script>
let data = {
name : "zhangsan"
};
let vm = new Vue({
el : "#root",
data
});
alert(vm._data === data); // true
alert(vm.name === vm._data.name);
</script>
</html>

事件处理

  • Vue中的事件修饰符:

    1、prevent:阻止默认事件(常用);
      2、stop:阻止事件冒泡(常用);
      3、once:事件只触发一次(常用);
      4、capture:使用事件的捕获模式(捕获顺序与冒泡相反,即从外向内);
      5、self:只有 event.target 是当前操作的元素时才触发事件;
      6、passive:事件的默认行为立即执行,无需等待事件回调执行完毕。
    
  • Vue中常用的按键别名:

    回车 => enter
      删除 => delete (捕获“删除”和“退格”键)
      退出 => esc
      空格 => space
      换行 => tab (特殊,必须配合keydown去使用,因为tab键按下后会跳出聚焦)
      上 => up
      下 => down
      左 => left
      右 => right
    
  • 对于 Vue 未提供别名的按键,可以使用按键原始的 key 值去绑定,但注意要转为 kebab-case(短横线命名)。eg:若需要CapsLock,则需要写为 caps-lock

  • 系统修饰键(用法特殊):ctrl、alt、shift、meta
    (1)配合 keyup 使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
    (2)配合 keydown 使用:正常触发事件。

  • 也可以使用 keyCode 去指定具体的按键(不推荐)

  • Vue.config.keyCodes.自定义键名 = 键码,可以定制按键别名

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../vue.js"></script>
</head>
<body>

<div id="root">
<h1>hello, {{name}}</h1>
<!-- v-on: 绑定事件-->
<!-- 传参时用 $event 占位,表示传入 event 参数 -->
<!-- once 修饰符,使得事件只触发一次 -->
<button v-on:click.once="showInfo1(123, $event)">click only once</button>

<!-- stop 修饰符阻止事件冒泡 -->
<div @click="showInfo2">
<!-- v-on: 可简写为 @ -->
<button @click.stop="showInfo2">click stopPropagation</button>
</div>

<!-- a 标签默认跳转,使用 prevent 修饰符阻止跳转 -->
<!-- 修饰符可以连着写 -->
<a href="http://www.baidu.com" v-on:click.prevent.stop="showInfo3">go to baidu</a>
</div>
<script>
const vm = new Vue({
el : "#root",
data : {
name : "san"
},
// methods 中配置的函数,都会被 vm 管理
methods : {
showInfo3(e) {
alert(e);
},
showInfo1(age, event) {
console.log(age);
console.log(event); // [Object MouseEvent]
},
showInfo2(event) {
console.log(this === vm); // true
console.log(event); // [Object MouseEvent]
}
}
});

</script>


<div id="r">
<!--
@keyup.enter="showMsg"
相当于
if(e.keyCode == 13) {
showMsg(e);
}
-->
<input type="text" placeholder="按下回车提交" @keyup.enter="showMsg">
<input type="text" placeholder="按下大小写切换键提交" @keyup.caps-lock="showCapsLock">
</div>
<script>
new Vue({
el : "#r",
data : {

},
methods : {
showMsg(e) {
alert(e.target.value);
},
showCapsLock(e) {
alert(e.key + " " + e.keyCode);
}
}
});
</script>
</body>
</html>

计算属性

  • 定义:要用的属性不存在,要通过已有属性计算得来。
  • 原理:底层借助了 Objcet.defineproperty 方法提供的 getter 和 setter。
  • get 函数什么时候执行?
    (1)初次读取时会执行一次;
    (2)当依赖的数据发生改变时会被再次调用。
  • 优势:与 methods 实现相比,内部有缓存机制(复用),效率更高,调试方便。
  • 注意:
    1、计算属性最终会出现在 vm 上,直接读取使用即可。
    2、如果计算属性要被修改,那必须写 set 函数去响应修改,且 set 中要引起计算时依赖的数据发生改变。
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../vue.js"></script>
</head>
<body>
<div id="root">
姓:<input type="text" v-model:value="firstName"> <br>
名:<input type="text" v-model="lastName"> <br>
全名: <span>{{fullName}}</span><br>
全名2: <span>{{simpFullName}}</span><br>
全名3:<span>{{fullName | getFirstName | changeFirstName}}</span>
</div>
<script>
Vue.filter("mySlice", function(val) {
return val.slice(0,2);
});

const vm = new Vue({
el : "#root",
data : {
firstName : "张",
lastName : "三"
},
// computed 定义计算属性
computed : {
// vm._data 中无计算属性 fullName
fullName : {
// 定义 getter,当 fullName 被初次读取时调用( Vue 的缓存机制)
// 所依赖的数据(firstName 或 lastName)发生变化时调用
get() {
console.log("getter 被调用");
// Vue 将 this 指向了 vm
return this.firstName + "·尼古拉斯·" + this.lastName;
},
// setter 非必需
set(value) {
console.log("setter 被调用" + " : value = " + value);
const arr = value.split('-');
console.log(arr);
this.firstName = arr[0];
this.lastName = arr[1];
}
},

// 若不需要 setter 时,可使用简写,即只写一个函数,作为 getter
simpFullName() {
return this.firstName + "·尼古拉斯·" + this.lastName;
}
},
// filters 配置局部过滤器
filters : {
// 过滤器默认将管道符 | 前的数据作为函数的第一个参数传入,额外传入的参数从第二个开始
getFirstName(val, firstNameLength = 1) {
return val.slice(0,firstNameLength);
},
changeFirstName() {
return "刘";
}
}
});
</script>
</body>
</html>

侦听属性

  • 侦听属性 watch:
    1、当被监视的属性变化时,回调函数自动调用,进行相关操作
    2、监视的属性必须存在,才能进行监视!!
    3、监视的两种写法:
    (1)new Vue 时传入 watch 配置
    (2)通过 vm.$watch 监视

  • 深度监视:
    (1)Vue 中的 watch 默认不监测对象内部值的改变(一层)。
    (2)配置 deep:true 可以监测对象内部值改变(多层)。

  • 注意:
    (1)Vue 自身可以监测对象内部值的改变,但 Vue 提供的 watch 默认不可以!
    (2)使用 watch时根据数据的具体结构,决定是否采用深度监视。

  • computed 和 watch 之间的区别:

    1、computed 能完成的功能,watch 都可以完成。
    2、watch 能完成的功能,computed 不一定能完成,例如:watch 可以进行异步操作。

  • 两个重要的小原则:
    1、所被 Vue 管理的函数,最好写成普通函数,这样 this 的指向才是 vm 或 组件实例对象。
    2、所有不被 Vue 所管理的函数(定时器的回调函数、ajax 的回调函数等、Promise 的回调函数),最好写成箭头函数,这样 this 的指向才是 vm 或 组件实例对象。

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../vue.js"></script>
<script>
console.log("********************************************************************");
</script>
</head>
<body>
<div id="root">
<h1>天气很{{info}}</h1>
<button @click="changeWeather">切换天气</button>
<h1>a 的值: {{numbers}}</h1>
<button @click="aPlus">a++</button>
</div>
</body>
<script>
const vm = new Vue({
el : "#root",
data : {
ishot : true,
numbers : {
a : 1,
b : 2
}
},
computed : {
// 计算属性的简写方式
info() {
return this.ishot ? "炎热" : "凉爽";
}
},
methods: {
changeWeather() {
this.ishot = !this.ishot;
},
aPlus() {
this.numbers.a++;
}
},
// 监视 vm 的属性,包括计算属性等
watch : {
// 被监视的变量
ishot : {
// 当监视到 ishot 发生改变时,调用 handler,handler 默认有两个参数
handler(newValue, oldValue) {
console.log("ishot: newValue:" + newValue + ", oldValue:" + oldValue);
},
// immediate 默认为 false,当为 true 时,即初始化时就调用一次 handler
immediate : true
},


// 只用到 handler 时,可以简写为一个函数,作为 handler
"numbers.a"(newValue, oldValue) {
console.log("numbers.a: newValue:" + newValue + ", oldValue:" + oldValue);
},


// numbers 存放的是对象的地址,所以对象属性的改变不会导致 numbers 的值发生变化
// Vue 的 watch 默认不监视多级结构某属性的变化
// deep 为 true时,可监视多级结构所有属性的变化,默认为 false
numbers : {
deep: true,
handler(newValue, oldValue) {
console.log("numbers's handler is called");
},
}
}
});

// 另一种监视
vm.$watch("info",{
handler(newValue, oldValue) {
console.log("info: newValue:" + newValue + ", oldValue:" + oldValue);
}
});
</script>
</html>

样式绑定

  • class 样式

    写法 :class="xxx",xxx 可以是字符串、对象、数组。
      字符串写法适用于:类名不确定,要动态获取。
      数组写法适用于:要绑定多个样式,个数不确定,名字也不确定。
      对象写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
    
  • style 样式

    :style="{fontSize: xxx}",其中 xxx 是动态值。
      :style="[a,b]",其中 a、b 是样式对象。
    
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>绑定样式</title>
<style>
.basic{
width: 400px;
height: 100px;
border: 1px solid black;
}

.happy{
border: 4px solid red;;
background-color: rgba(255, 255, 0, 0.644);
background: linear-gradient(30deg,yellow,pink,orange,yellow);
}
.sad{
border: 4px dashed rgb(2, 197, 2);
background-color: gray;
}
.normal{
background-color: skyblue;
}

.myStyle1{
background-color: yellowgreen;
}
.myStyle2{
font-size: 30px;
text-shadow:2px 2px 10px red;
}
.myStyle3{
border-radius: 20px;
}
</style>
<script type="text/javascript" src="../vue.js"></script>
</head>
<body>
<div id="root">
<!-- :class 即 v-bind:class 的简写,将值字符串作为表达式执行 -->
<!-- @click 即 v-on:click 的简写,为 click 绑定事件 -->

<!-- 绑定 class 样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/>

<!-- 绑定 class 样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
<div class="basic" :class="classArr">{{name}}</div> <br/><br/>

<!-- 绑定 class 样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
<div class="basic" :class="classObj">{{name}}</div> <br/><br/>

<!-- 绑定 style 样式--对象写法 -->
<div class="basic" :style="styleObj">{{name}}</div> <br/><br/>
<!-- 绑定 style 样式--数组写法,不常用 -->
<div class="basic" :style="styleArr">{{name}}</div>
</div>
</body>

<script type="text/javascript">
Vue.config.productionTip = false

const vm = new Vue({
el:'#root',
data:{
name:'at',
mood:'normal',
classArr:['myStyle1','myStyle2','myStyle3'],
// true 即为启用样式
classObj:{
myStyle1:false,
myStyle2:false,
},
styleObj:{
fontSize: '40px',
color:'red',
},
styleObj2:{
backgroundColor:'orange'
},
styleArr:[
{
fontSize: '40px',
color:'blue',
},
{
backgroundColor:'gray'
}
]
},
methods: {
changeMood(){
const arr = ['happy','sad','normal']
const index = Math.floor(Math.random()*3)
this.mood = arr[index]
}
},
})
</script>
</html>

条件渲染

  • v-if
    写法:
    (1)v-if=”表达式”
    (2)v-else-if=”表达式”
    (3)v-else=”表达式”
    适用于:切换频率较低的场景。
    特点:不展示的 DOM 元素直接被移除。
    注意:v-if 可以和 :v-else-if、v-else 一起使用,但要求结构不能被“打断”

  • v-show
    写法:v-show=”表达式”
    适用于:切换频率较高的场景。
    特点:不展示的 DOM 元素未被移除,仅仅是使用样式隐藏掉(display: none)

  • 注意

    使用 v-if 时,元素可能无法获取到,而使用 v-show 一定可以获取到。

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
48
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../vue.js"></script>
</head>
<body>
<div id="root">
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>

<!-- 使用 v-show 做条件渲染 -->
<!-- v-show="false" 时,渲染为 style="display: none;" -->
<h2 v-show="false">1欢迎{{name}}</h2>
<h2 v-show="1 === 1">1欢迎{{name}}</h2>

<!-- 使用 v-if 做条件渲染 -->
<!-- v-if="false "时,直接移除掉元素 -->
<h2 v-if="false">2欢迎{{name}}</h2>
<h2 v-if="1 === 1">2欢迎{{name}}</h2>

<!-- v-else 和 v-else-if 必须连着使用,中间不允许被打断 -->
<div v-if="n === 1">Angular</div>
<div v-else-if="n === 2">React</div>
<div v-else-if="n === 3">Vue</div>
<div v-else>哈哈</div>

<!-- v-if 与 template 的配合使用,渲染时不会破坏结构,即只有两个 h2 标签 -->
<template v-if="n === 1">
<h2>hello</h2>
<h2>world</h2>
</template>

</div>
</body>
<script>
new Vue({
el : "#root",
data : {
n : 0,
name : "张三"
}
})
</script>
</html>

列表渲染

  • v-for指令:
    用于展示列表数据
    语法:v-for=”(item, index) in xxx” :key=”yyy”
    可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
  • react、vue中的 key 有什么作用?(key 的内部原理)
  • 虚拟 DOM 中 key 的作用:
    key 是虚拟 DOM 对象的标识,当数据发生变化时,Vue 会根据【新数据】生成【新的虚拟 DOM】, 随后 Vue 进行【新虚拟 DOM】与【旧虚拟 DOM】的差异比较,比较规则如下:
    (1)旧虚拟 DOM 中找到了与新虚拟 DOM 相同的 key:
    ① 若虚拟 DOM 中内容没变, 直接使用之前的真实 DOM!
    ② 若虚拟 DOM 中内容变了, 则生成新的真实 DOM,随后替换掉页面中之前的真实 DOM。
    (2)旧虚拟 DOM 中未找到与新虚拟 DOM 相同的 key:
    创建新的真实 DOM,随后渲染到到页面。
  • 用 index 作为 key 可能会引发的问题:
    (1)若对数据进行:逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实 DOM 更新 ==> 界面效果没问题, 但效率低。
    (2)如果结构中还包含输入类的 DOM,会产生错误 DOM 更新 ==> 界面有问题。
  • 开发中如何选择 key?
    (1)最好使用每条数据的唯一标识作为 key, 比如 id、手机号、身份证号、学号等唯一值。
    (2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用 index 作为 key 是没有问题的。
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../vue.js"></script>
</head>
<body>
<div id="root">
<!-- 遍历数组 -->
<h2>遍历数组</h2>
<button @click="add">添加赵六</button>
<input placeholder="查询" v-model:value="keyWord">
<ul>
<!--
key对每一虚拟 DOM 进行标识,缺省值为 index
标签的 :key 属性只用于 Vue,不会渲染在页面上
-->
<li v-for="(p,index) in filPersons" :key="index">
{{index}}.{{p.name}}-{{p.age}}
<input type="text">
</li>
</ul>

<!-- 遍历对象 -->
<h2>遍历对象</h2>
<ul>
<!-- 第一个参数是 value -->
<li v-for="(v,k) in car" :key="k">
{{k}}-{{v}}
</li>
</ul>

<!-- 遍历字符串 -->
<h2>测试遍历字符串(用得少)</h2>
<ul>
<li v-for="(char,index) in str" :key="index">
{{index}}-{{char}}
</li>
</ul>
<!-- 遍历指定次数 -->
<h2>测试遍历指定次数(用得少)</h2>
<ul>
<li v-for="(number,index) in 5" :key="index">
{{index}}-{{number}}
</li>
</ul>
</div>
</body>
<script>
const vm = new Vue({
el : "#root",
data : {
persons : [
{id : "001", name : "张三", age : 18},
{id : "002", name : "李四", age : 20},
{id : "003", name : "王五", age : 21}
],
filPersons : [],
car : {
name : "五菱之光",
color : "白色"
},
str : "test",
keyWord : ""
},
methods: {
add() {
this.persons.unshift({id : "004", name : "赵六", age : 40});
}
},
watch : {
keyWord : {
// immediate 为 true,则一开始执行 handler(""),任何字符串的 indexOf("") 返回 0
immediate : true,
handler(val) {
/*
array.filter(function(currentValue,index,arr))
数组的filter()函数,过滤符合条件的数组元素,并将其作为新数组返回
其中function返回true则表示留下该元素
*/
console.log(val == "");
this.filPersons = this.persons.filter((p)=>{
return p.name.indexOf(val) != -1;
});
}
}
},

/*
computed : {
filPersons() {
return this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord) != -1;
});
}
}
*/
})
</script>
</html>

Vue.set() 的使用

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
push()、pop()、shift()、unshift()、splice()、sort()、reverse()
而直接操作数组元素不会成功,应该用 vm.arr.splice(x, 1)

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
48
49
50
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../vue.js"></script>
</head>
<body>
<div id="root">
<h1>学生信息</h1>
<button @click="addSex">添加性别、身高属性</button>
<h1>姓名:{{student.name}}</h1>
<h1>年龄:{{student.age}}</h1>
<h1 v-if="student.sex">性别:{{student.sex}}</h1>
<h1 v-show="student.height">身高:{{student.height}}</h1>
<h1>爱好:</h1>
<ul>
<li v-for="(h, index) in student.hobbies">
爱好{{index}}:{{h}}
</li>
</ul>
</div>
</body>
<script>
const vm = new Vue({
el : "#root",
data : {
student : {
name : "张三",
age : 13,
hobbies : ["吃饭", "睡觉"]
}
},
methods: {
addSex() {
// Vue.set(target, prop, value) 向响应式对象添加一个 property(Vue 无法探测到普通的新增 property)
// 并保证这个 property 同样是响应式的,且触发视图更新
// target 不能为 vm 或 vm.data
Vue.set(this.student, "sex", "男");
// vm.$set() 起同样的作用
this.$set(this.student, "height", "199");
// 由于 JS 中数组是特殊的对象,即以 index 作为索引操作元素,所以可用 Vue.set() 修改数组
this.$set(this.student.hobbies, 0, "学习");
}
},
})
</script>
</html>

数据监视

  • Vue 会监视 data 中所有层次的数据。

  • 如何监测对象中的数据?
    通过 setter 实现监视,且要在 new Vue 时就传入要监测的数据。
    (1)对象中后追加的属性,Vue 默认不做响应式处理
    (2)如需给后添加的属性做响应式,使用如下 API:
    Vue.set(target,propertyName/index,value) 或
    vm.$set(target,propertyName/index,value)

  • 如何监测数组中的数据?
    通过包裹数组更新元素的方法实现,本质就是做了两件事:
    (1)调用原生对应的方法对数组进行更新。
    (2)重新解析模板,进而更新页面。

  • 在 Vue 修改数组中的某个元素一定要用如下方法:
    1、push()、pop()、shift()、unshift()、splice()、sort()、reverse()
    2、Vue.set() 或 vm.$set()

  • 特别注意:Vue.set() 和 vm.$set() 不能给 vm 或 vm 的根数据对象添加属性!!!

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>总结数据监视</title>
<style>
button{
margin-top: 10px;
}
</style>
<script type="text/javascript" src="../vue.js"></script>
</head>
<body>
<div id="root">
<h1>学生信息</h1>
<button @click="student.age++">年龄+1岁</button> <br/>
<button @click="addSex">添加性别属性,默认值:男</button> <br/>
<button @click="student.sex = '未知' ">修改性别</button> <br/>
<button @click="addFriend">在列表首位添加一个朋友</button> <br/>
<button @click="updateFirstFriendName">修改第一个朋友的名字为:张三</button> <br/>
<button @click="addHobby">添加一个爱好</button> <br/>
<button @click="updateHobby">修改第一个爱好为:开车</button> <br/>
<button @click="removeSmoke">过滤掉爱好中的抽烟</button> <br/>
<h3>姓名:{{student.name}}</h3>
<h3>年龄:{{student.age}}</h3>
<h3 v-if="student.sex">性别:{{student.sex}}</h3>
<h3>爱好:</h3>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
<h3>朋友们:</h3>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}--{{f.age}}
</li>
</ul>
</div>
</body>

<script type="text/javascript">
// 阻止 vue 在启动时生成生产提示
Vue.config.productionTip = false

const vm = new Vue({
el:'#root',
data:{
student:{
name:'tom',
age:18,
hobby:['抽烟','喝酒','烫头'],
friends:[
{name:'jerry',age:35},
{name:'tony',age:36}
]
}
},
methods: {
addSex(){
// Vue.set(this.student,'sex','男')
this.$set(this.student,'sex','男')
},
addFriend(){
this.student.friends.unshift({name:'jack',age:70})
},
updateFirstFriendName(){
this.student.friends[0].name = '张三'
},
addHobby(){
this.student.hobby.push('学习')
},
updateHobby(){
// this.student.hobby.splice(0,1,'开车')
// Vue.set(this.student.hobby,0,'开车')
this.$set(this.student.hobby,0,'开车')
},
removeSmoke(){
this.student.hobby = this.student.hobby.filter((h)=>{
return h !== '抽烟'
})
}
}
})
</script>
</html>

表单数据收集

  • 若:<input type="text"/>,则 v-model 收集的是 value 值,用户输入的就是 value 值。
  • 若:<input type="radio"/>,则 v-model 收集的是 value 值,且要给标签配置 value 值。
  • 若:<input type="checkbox"/>
    1、没有配置 input 的 value 属性,那么收集的就是 checked(勾选 or 未勾选,是布尔值)
    2、配置了 input 的 value 属性:
    (1)v-model 的初始值是非数组,那么收集的就是 checked(勾选 or 未勾选,是布尔值)
    (2)v-model 的初始值是数组,那么收集的的就是 value 组成的数组
  • v-model 的三个修饰符:
    lazy:失去焦点再收集数据
    number:输入字符串转为有效的数字
    trim:输入首尾空格过滤
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>收集表单数据</title>
<script type="text/javascript" src="../vue.js"></script>
</head>
<body>

<div id="root">
<!-- .prevent 修饰符,阻止点击提交后自动跳转这一默认行为 -->
<form @submit.prevent="demo">
<!-- input 输入框类型, v-model 收集的是 value 值,用户输入的就是 value 值 -->
账号:<input type="text" v-model.trim="userInfo.account"> <br/><br/>
密码:<input type="password" v-model="userInfo.password"> <br/><br/>
<!-- type 为number 使得输入框只能输入数字, v-model 的 number修饰符使得最终接收到的是数字 -->
年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/>

<!-- radio 单选框类型,通过 name 属性实现互斥,需要手动配置 value 属性 -->
性别:
<input type="radio" name="sex" v-model="userInfo.sex" value="male">
<input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/><br/>

<!-- checkbox 复选框类型,默认收集 checked 值(true 或 false) -->
<!-- 配置了 value 属性,v-model 的初始值是数组,那么收集的的就是 value 组成的数组 -->
爱好:
学习<input type="checkbox" v-model="userInfo.hobby" value="study">
打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
<br/><br/>

<!-- select 下拉框,默认收集选中的 option 的 value -->
所属地区
<select v-model="userInfo.city">
<option value="">请选择地区</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
<option value="guangzhou">广州</option>
</select>
<br/><br/>

<!-- textarea 输入框类型, v-model 收集的是 value 值,用户输入的就是 value 值 -->
其他信息:
<textarea v-model.lazy="userInfo.other"></textarea> <br/><br/>
<!-- v-model 的初始值 userInfo.agree 不是数组,则收集 checked 值(true 或 false) -->
<input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="#">《用户协议》</a>
<button>提交</button>
</form>
</div>
</body>

<script type="text/javascript">
Vue.config.productionTip = false

new Vue({
el:'#root',
data:{
userInfo:{
account:'',
password:'',
age:18,
sex:'female',
hobby:[],
city:'beijing',
other:'',
agree:''
}
},
methods: {
demo(){
console.log(JSON.stringify(this.userInfo))
}
}
})
</script>
</html>

内置指令与自定义指令

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
/*
网络较慢,网页还在加载 Vue.js ,而导致 Vue 来不及渲染
这时页面就会显示出 Vue 源代码
使用 v-cloak 指令来解决这一问题
*/
[v-cloak] {
display: none
}
</style>
<title>Document</title>
<script src="../vue.js"></script>
</head>
<body>
<div id="root">
<!-- cloak: n.v. 遮盖 -->
<!-- 在 Vue 实例编译结束时会自动删去元素的 v-cloak属性 -->
<h1 v-cloak>{{str}}</h1>
<span v-html="str"></span>
<span v-text="str"></span>
<!--
v-once 所在节点在初次动态渲染后,就视为静态内容了。
以后数据的改变不会引起 v-once 所在结构的更新,可以用于优化性能。
-->
<div>
<!-- v-pre:跳过 Vue 对其所在节点的编译,加快速度 -->
<h1 v-pre>v-once测试</h1>
<h1 v-once>n的初始值为:{{n}}</h1>
<h1>n的当前值为:{{n}}</h1>

<!-- 自定义指令为 v-指令名 -->
<h1>放大10倍的n为:<span v-big="n"></span></h1>
<h1>放大10倍的n为:<span v-big="n"></span></h1>
<button @click="n++">点击n+1</button>
</div>
</div>
</body>
<script>
const vm = new Vue({
el : "#root",
data : {
str : "<h1>hello</h1>",
n : 1
},

// 自定义指令 directives
directives : {
// 默认传俩参数,第一个为相应的 DOM 元素,第二个为一个对象,包含绑定的相关信息
// 该函数会在1、指令与元素成功绑定时;2、指令所在的模板被重新解析时调用
big(elment, binding) {
// 不用返回值,直接对 DOM 元素操作
elment.innerText = binding.value * 10;
},
fbind : {

}
}
})
</script>
</html>

生命周期

生命周期.png

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../../vue.js"></script>
</head>
<body>
<div id="root" :x="n">
<h2 v-text="n"></h2>
<h2>当前的n值是:{{ n }}</h2>
<button @click="add">点我n+1</button>
<button @click="bye">点我销毁vm</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false

new Vue({
el: '#root',
// template:`
// <div>
// <h2>当前的 n 值是:{{n}}</h2>
// <button @click="add">点我 n+1</button>
// </div>
// `,
data: {
n: 1
},
methods: {
add() { console.log('add')
this.n++
},
bye() {
console.log('bye')
this.$destroy()
}
},
watch: {
n() {
console.log('n变了')
}
},
// 此时 vm 上无 data、methods 等
// 数据代理、数据监测 create 之前
beforeCreate() {
console.log('beforeCreate');
console.log(this);
debugger;
},
// 此时已完成数据监测 (vm._data.xxx)和数据代理(vm.xxx)
// created() 可以调用 vm 的 data 及 methods
created() {
console.log('created');
console.log(this);
debugger;
},
// vm 有 el 或 vm 无 el 但调用了 vm.$mount(xx),才会继续
// 虚拟 DOM 编译完在内存中,但页面上显示的是待替换的未编译的真实 DOM
beforeMount() {
console.log('beforeMount');
console.log(this);
debugger;
},
// 将内存中的虚拟 DOM 转换为真实 DOM插入页面
// 用于发送 ajax 请求、启动定时器、绑定自定义事件、订阅消息等
mounted() {
console.log('mounted');
console.log(this);
debugger;
},
// 数据已更新,但页面仍是旧的
beforeUpdate() {
console.log('beforeUpdate');
console.log(this);
debugger;
},
// 生成新的虚拟 DOM,diff 比较后更新
updated() {
console.log('updated');
console.log(this);
debugger;
},
// vm 中的 data、methods、指令等仍可用,但对数据的修改不会奏效
// 一般用于关闭定时器、取消订阅消息、解绑自定义事件
beforeDestroy() {
console.log('beforeDestroy');
console.log(this);
debugger;
},
destroyed() {
console.log('destroyed');
console.log(this);
debugger;
},
})
</script>

组件

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../../vue.js"></script>
</head>
<body>
<div id="root">
<my-student></my-student>
<hello></hello>
</div>
</body>

<script type="text/javascript">
Vue.config.productionTip = false

// 通过 Vue.extend() 创建组件
const student = Vue.extend({
// 组件定义时,不能写 el 配置项,因为最终所有的组件都要被一个 vm 管理,由 vm 决定服务于哪个容器。
// 换行输入用 ` (位于ESC下方)
template:`
<div>
<h2>学生姓名:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
// data 项必须写成返回一个对象的形式,避免组件复用产生引用关系
data(){
return {
studentName:'张三',
age:18
}
}
})
const hello = Vue.extend({
template:`
<div>
<h2>你好啊!{{name}}</h2>
</div>
`,
data(){
return {
name:'Tom'
}
}
})

// 全局注册组件
Vue.component('hello',hello)

new Vue({
el:'#root',
// components 配置项局部注册组件
components:{
// 在 Vue-CLI 环境下,若组件名为多个单词,用大驼峰 MyStudent
"my-student" : student
// 若 key 和 value 相同,可简写为 student
}
})
console.log(student)
</script>

Vue与VueComponent 的关系