前言

Vue:

  • 动态构建用户界面的渐进式 JavaScript 框架;

  • 遵循 MVVM 模式

    1. M:模型(Model) :对应 data 中的数据

    2. V:视图(View) :模板

    3. VM:视图模型(ViewModel) : Vue 实例对象

      图片

  • 它本身只关注 UI, 也可以引入其它第三方库开发

常用UI库:

快速上手

打开vue官网,可以看到vue官网给了两种VueJS(2.0)的引入方式:

1
2
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

或者:

1
2
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

开发环境版本即为完整版本的js,提供了vue的完整内容,较为臃肿,适合学习使用;

生产环境版本则是按需提供,需要自己引入功能,适合实际开发使用

安装教程给出了更多安装 Vue 的方式。请注意我们不推荐新手直接使用 vue-cli,尤其是在你还不熟悉基于 Node.js 的构建工具时。

图片

Hello Vue

新建一个Html项目(在vscode中输入 !Tab键 可快速生成一个标准的Html模板);

打开开发工具 Visual Studio Code

安装插件 Live Server ,实现Html静态界面的实时预览功能(建议);

注意:安装Live Server 后,需要打开html界面所在文件夹(文件—>打开文件夹),而不是直接打开文件,然后在Html界面右键,选择Open with Live Server ,会自动在浏览器打开文件(注意看浏览器路径)

图片

图片

写一个Hello 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
28
29
30
31
32
33
34
35
36
37
38
<!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>Hello World</title>
</head>

<body>
<div id="app">
<!-- 与下面的message变量对应 -->
{{ message }}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
//创建VM(vue实例)
var app = new Vue({
el: '#app', //这里对应的就是ID为app的元素
//data的两种写法:对象式
data: {
message: 'Hello Vue!' //设置message的变量为 Hello Vue!
}
//data的两种写法:函数式(组件中必须为函数式)
//data(){
// console.log('@@@',this)//此处this为Vue实例对象
// return{
// message: 'Hello Vue!'
// }
//}

})
</script>

</body>

</html>

图片

el 挂载点

  • Vue会管理el选项命中的元素及其内部的后代元素
  • el可以挂载各种css选择器及Dom元素(除了body和html标签)

例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
<body>
{{ message }}
<div id="app">
{{ message }}
<span>{{ message }} </span>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Ahzoo'
}
})
//挂载的另一种写法:
//var app = new Vue({
// data: {
// message: 'Ahzoo'
// }
//}).$mount('#app')

</script>

</body>

图片

可以看到,只有在el命中的元素内部,标签才会生效

例2:

el支持多种选择器测试:

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
<body>
class选择器测试:
<div class="app2">
{{ message }}
</div>
<br />
(button)标签选择器测试:
<br />
<button>
{{ message }}
</button>
<div id="app">
{{ message }}
<span>{{ message }} </span>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
var app = new Vue({
el: '.app2',
data: {
message: 'Ahzoo'
}
})
</script>

<script>
var app = new Vue({
el: 'button',
data: {
message: '这里是标签选择器测试'
}
})
</script>

</body>

图片

date数据对象

  • Vue中使用到的数据定义在data
  • data中可以写复杂类型的数据
  • 渲染(调用)复杂类型数据时遵循JS语法
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
<body>
<div id=msg-div>
{{ thisMsg }}
<h3>{{ person }}</h3>
<h4>{{ color }}</h4>
<h5>姓名:{{ person.name }} 、年龄: {{ person.age }}</h5>
<li>{{ color[0] }}</li>
<li>{{ color[1] }}</li>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el: "#msg-div",
data: {
//普通数据
thisMsg: "Ahzoo",

//对象
person: {
name: "ahzoo",
age: "18"
},

//数组
color:["red","blue","pink"]

}
})
</script>


</body>

图片

Vue指令

Vue指定即以v-开头的一组特殊语法

  • 内容及事件绑定:v-text、v-html、v-on
  • 显示切换,属性绑定:v-show、v-if、v-bind
  • 列表循环,表单元素绑定:v-for、v-on、v-model

v-text

v-text指令用于设置文本数据

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
<body>
<div id=app>
<h2 v-text="message"></h2>
相当于:<h2>{{message}}</h2>

<h2 v-text="message2">!!!</h2> <!-- h2标签中的内容将会完全被v-text指令替换掉-->

<h2>{{message2}}!!!</h2> <!-- 只会替换掉{{message2}}中的数据-->

</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el:"#app",
data:{
message:"ahzoo",
message2:"十玖八柒"
}
})
</script>



</body>

图片

v-html

  • v-html指令用于设置标签的innerHTML
  • 如果内容为普通文本和v-text结果无异
  • 如果内容为HTML结构,v-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
<body>
<div id=app>

<h2 v-text="content"></h2>
<h2 v-htmlt="content"></h2> <!-- 对于普通数据和v-text无异-->

<h2 v-text="content2" ></h2>
<h2 v-html="content2"></h2> <!-- 对于html语法v-html会进行解析-->

</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el:"#app",
data:{
content:"ahzoo",
content2:"<a href='https://ahzoo.cn' style=color:red> 十玖八柒 </a>"
}
})
</script>

</body>

图片

v-on

  • v-on指令用于为元素绑定事件
  • v-on指令可以简写为@
  • 定义方法时可以定义形参用于接收传入的实参
  • 在元素的点击事件上面加上.修饰符可以对事件进行限制

基本写法:v-on:事件名="方法名"

简写:@事件名="方法名"

拓展写法:v-on:事件名.修饰符="方法名"

动态写法:@[event],event为事件名,不进行写死,动态赋值

点击事件:v-on:click

鼠标移入事件:v-on:monseenter

双击事件:v-on:dblclick

Enter按键触发事件:v-on:onclick.enter

vue2.0可以使用v-on:onclick.13 ,相当于v-on:onclick.enter ,但是此方法在vue3.0中已被移除

点击回调只会触发一次:v-on:click.once

v-on官方文档

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
<body>
<div id=app>

<button v-on:dblclick="dOnClick">双击测试</button>
<button @click="onChangeClick">修改数据测试</button>
{{msg}}

</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el: "#app",
data: {
msg: "ahzoo"
},
methods: {
dOnClick: function () {
alert("ahzoo")
},
onChangeClick: function () {
console.log("当前数据:" + this.msg)
this.msg += "9";
}
}
})
</script>

</body>

图片

示例2:

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
<body>
<div id=app>

<button @click="onClick(999,'数字')">点击</button>

<!-- @keyup表示键盘点击事件,@keyup.enter表示键盘点击Enter键事件 -->
<input type="text" @keyup.enter="sayHi('Hi')"></input>


</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el: "#app",
data:{

},
methods:{
sayHi:function(t1){
console.log(t1);
},
onClick:function(t1,t2){
console.log(t2 + ":" + t1)
}
}
})
</script>

</body>

图片

实例:

计数器

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
<!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>Ahzoo</title>
</head>

<body>
<div id=app>

<button @click="onSubClick">-</button>
{{num}}
<button @click="onAddClick">+</button>


</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el: "#app",
data: {
num: "9"
},
methods: {
onAddClick: function () {
this.num++;
},
onSubClick: function () {
this.num--;
}
}
})
</script>

</body>

</html>

图片

修饰符
事件修饰符
  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
系统修饰符

.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。

鼠标修饰符
  • .left
  • .right
  • .middle

v-show

  • v-show会根据表达式的true或false切换元素显示或者隐藏
  • 本质是修改元素的display属性
  • 表达式为数字时,默认为true
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
<body>
<div id=app>

<h2 v-show="isShow"> {{ message }} </h2>

<button @click="changeShow"> 显示/隐藏 </button>

<h2 v-show="isShowN"> {{ message }} </h2>


</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el: "#app",
data: {
message: "ahzoo",
isShow:true,
isShowN:99
},
methods: {
changeShow: function () {
this.isShow = !this.isShow
}
}
})
</script>

</body>

图片

v-if

  • v-show会根据表达式的true或false切换元素显示或者隐藏
  • v-show不同的是,v-if操纵的是Dom元素
  • 本质是直接移除Dom元素;因此对于频繁切换的元素建议使用v-show,反之;因为操作Dom元素对性能消耗较大
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
<body>
<div id=app>

<h2 v-if="isShow"> {{ message }} </h2>

<button @click="changeShow"> 显示/隐藏 </button>

</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el: "#app",
data: {
message: "ahzoo",
isShow:true
},
methods: {
changeShow: function () {
this.isShow = !this.isShow
}
}
})
</script>

</body>

图片

v-else

v-if 或者 v-else-if 添加“else 块”。

v-else-if

表示 v-if 的“else if 块”。可以链式调用。

v-bind

  • v-bind指令用于为元素绑定属性
  • v-bind指令可以直接省略不写,直接使用缩写形式::
  • 用于动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。

基本写法:v-bind:属性名=表达式

简写::属性名=表达式

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
<style>
img {
width: 99px;
height: 99px;
}

.activeClass {
border: 5px solid blue;
}
</style>

<body>
<div id=app>

<img v-bind:src="srcUrl" :title="'这是图片标题:' + srcTitle"
:class="isActive ? 'activeClass' : ''">
<br/>
<img v-bind:src="srcUrl" :title="'这是图片标题:' + srcTitle"
:class="{activeClass:!isActive}">

<!-- 表达式的两种写法:对象 -->
<!-- <img :class="{activeClass:isActive}"> -->

<!-- 写法二:三元表达式 -->
<!-- <img :class="activeClass ? 'active' : ''"> -->

</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el: "#app",
data: {
srcUrl: "https://ahzoo.cn/img/avatar.jpg",
srcTitle: "ahzoo",
isActive: true
},
methods: {
changeShow: function () {
this.isShow = !this.isShow
}
}
})
</script>

</body>

图片

v-for

  • v-for指令用于根据数据生成列表结构
  • 使用时需要配合v-bind:key="键值"指令

基本写法:v-for="(索引名,项目名) in 数据名"

对于key的选择:最好使用每条数据的唯一标识作为key,比如ID、手机号等

使用index作为key的问题:

  1. 若对数据进行逆序添加或者删除等破坏顺序的操作时,会产生没有必要的真实DOM更新(导致效率较低)
  2. 如果结构中包含输入类DOM(比如输入框),会产生错误的DOM更新(界面功能会出现问题,比如顺序错乱之类的)
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
<body>
<div id=app>

<!-- 写法一:元素固定 -->
<ul>
<li v-for="itemName in arr" v-bind:key="itemName">ahzoo</li>
</ul>


<!-- 写法二:动态数据 -->
<ul>
<!-- itemName in arr 中的 itemName 与后面的 {{ itemName }}对应 ;arr 对应下面的 arr 数组-->
<li v-for="itemName in arr" v-bind:key="itemName">数字:{{ itemName }}</li>
</ul>

<!-- 写法三: 显示列表索引-->
<ul>
<!-- itemName 对应项目名;indexName对应索引名 ;arr 对应下面的 arr 数组-->
<li v-for="(itemName,indexName) in arr" v-bind:key="itemName">
{{ indexName }} :数字:{{ itemName }}
</li>
</ul>

<!-- 写法四,获取对象数据 -->
<ul>
<!-- itemName 对应项目名;indexName对应索引名 ;arr 对应下面的 arr 数组-->
<li v-for="(itemName,indexName) in objArr" v-bind:key="itemName">
{{ indexName }} :姓名:{{ itemName.name }}、年龄:{{ itemName.age }}
</li>
</ul>

</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el: "#app",
data: {
arr: [10, 9, 8, 7],
objArr: [
{ name: "ahzoo", age:18 },
{ name: "ouo", age:19 }
]
}
})
</script>

</body>

图片

实例:

添加/移除列表元素:

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
<body>
<div id=app>

<button @click="onAddClick">增加</button>
<button @click="onSubClick">减少</button>
<ul>
<!-- itemName 对应项目名;indexName对应索引名 ;arr 对应下面的 arr 数组-->
<li v-for="(itemName,indexName) in objArr" v-bind:key="itemName">
{{ indexName }} :姓名:{{ itemName.name }}、年龄:{{ itemName.age }}
</li>
</ul>

</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el: "#app",
data: {
objArr: [
{ name: "ahzoo", age:18 },
{ name: "ouo", age:19 }
]
},
methods:{
onAddClick: function(){
//push方法将会在右边增加元素(unshift是在左边增加元素)
this.objArr.push({ name: "ahzoo", age:20 });
},
onSubClick: function(){
//shift方法将会移除最左边的元素
this.objArr.shift();
}
}
})
</script>

</body>

图片

v-model

  • v-model指令用于获取和设置表单元素的值(双向数据绑定)
  • 绑定的数据会和表单数据值相互关联
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
<body>
<div id=app>


<!-- 在表单输入框中修改message值时,data中的message值也会被同步修改 -->
<input type="text" v-model="message">
<h2>{{ message }}</h2>

<!-- 修改data中的message值时,表单输入框中的message值也会被同步修改 -->
<input type="button" @click="onClick" value="修改">


</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el: "#app",
data:{
message:"ahzoo"
},
methods: {
onClick:function(){
this.message = "十玖八柒"
}
}
})
</script>

</body>

图片

修饰符

.lazy :在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转为在 change 事件_之后_进行同步

.trim:如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符

.number:即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。number修饰符将会自动将用户的输入值转为数值类型

示例:

表单提交

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
<!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>Ahzoo</title>
</head>

<body>
<!-- 提供一个容器 -->
<div id=root>
<!-- @submit:表示表单提交事件, .prevent 表示阻止控件的原生事件(表单提交后会有一个默认的刷新事件,将表单中是数据情况,这里使用 prevent 阻止了此默认事件 -->
<form @submit.prevent="onSubmit">
<!-- .trim 修饰符表示去除首尾空格 -->
账户:<input type="text" v-model.trim="userInfo.account"> <br/>
密码:<input type="password" v-model="userInfo.password"><br/>
<!-- 使用type属性限制输入框中输入的只能是数字,但是接收的时候会转为字符串,所以要使用 v-model.number ,确保接收的数据也是数字类型 -->
年龄:<input type="number" v-model.number="userInfo.age"><br/>
性别:<br/>
<input type="radio" name="sex" value="famale" v-model="userInfo.sex">
<input type="radio" name="sex" value="male" v-model="userInfo.sex"><br/>
喜欢的朝代:<br/>
<input type="checkbox" value="han" v-model="userInfo.dynasty">
<input type="checkbox" value="tang" v-model="userInfo.dynasty">
其它<input type="checkbox" value="other" v-model="userInfo.dynasty"><br/>
备注:<br/>
<!-- v-model默认是实时同步的,加上 .lazy 修饰符表示失去焦点时,再进行数据同步 -->
<textarea v-model.lazy="userInfo.remark"> </textarea><br/>
确认注册<input type="checkbox" v-model="userInfo.reinspection"><br/>

<!-- 可以自行写表单提交事件 -->
<!-- <button @click="onSubmit"> 提交 </button> -->

<button> 提交 </button>

</form>

</div>


</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script type="text/javascript">

Vue.config.productionTip = false;

// 创建VM(vue实例)
new Vue({
el: "#root",
data:{
userInfo:{
account:"",
password:"",
age:"",
// 设置性别初始值
sex:"male",
// 多选框的初始值,将影响后续赋值。1.如果初始值为数组,后续则为数组
dynasty:[],
// 2.如果初始值为字符串,后续结果将为布尔值(即:是否有多选框被勾选)
reinspection:""
}
},
methods:{
onSubmit(){
// 这里写表单提交事件,一般填ajax请求,为了简便,这里直接进行打印输出
console.log(this.userInfo)
}
}
})
</script>

</body>

</html>

图片

v-cloak

这个指令保持在元素上直到关联组件实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到组件实例准备完毕。

  • v-cloak指令会在vue编译完成后自动消失,配合css样式,就可以达到针对编译完成前后的不同样式效果
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
<!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>Ahzoo</title>
<style>
/* 隐藏所有v-clack属性的元素 */
[v-cloak]{
display: none;
}
</style>
</head>

<body>
<div id=app>

<h2 v-cloak>{{ message }}</h2>

</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el: "#app",
data:{
message:"ahzoo"
}
})
</script>

</body>

</html>

图片

可以看到编译完成后的<h2>标签是不带v-cloak属性的

v-once

只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。

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
<body>
<div id=app>

<h2 v-once>默认的值是:{{ message }}</h2>
<h2>当前的值是:{{ message }}</h2>
<button @click="onClick">增加</button>

</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el: "#app",
data: {
message: 7
},
methods: {
onClick: function () {
this.message++;
}
}
})
</script>

</body>

图片

v-pre

跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。

  • VueJS不会渲染加了v-pre指令的语句,从而使加载更快;因此建议没有使用vue指令的地方添加v-pre指令
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
<body>
<div id=app>


<h2 v-pre>当前未加渲染的值是:{{ message }}</h2>

<h2>当前的值是:{{ message }}</h2>
<button @click="onClick">增加</button>

<h2 v-pre> 加了v-pre指令后,vue就不会渲染此段文字,从而使加载更快</h2>

</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script>
var app = new Vue({
el: "#app",
data: {
message: 7
},
methods: {
onClick: function () {
this.message++;
}
}
})
</script>

</body>

图片

Web Storage

Web Storage实际上由两部分组成:sessionStorage与localStorage。sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储,仅仅是会话级别的存储。localStorage用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。

本地储存

本地储存空间(LocalStorage):在浏览器关闭时,数据不会被清空,再次打开浏览器时,仍然能够看到数据。

新建一个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
<!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>
</head>

<body>
<button onclick="saveTest()">保存数据测试</button>
<button onclick="readTest()">读取数据测试</button>
<button onclick="deleteTest()">删除数据测试</button>
<button onclick="deleteAllTest()">删除所有数据测试</button>
</body>
<script type="text/javascript">

let p = { name: "ahzoo", age: 18 }
function saveTest() {
localStorage.setItem('msgTest', 'Helloworld')
// 由于localStorage只能储存字符串,所以会自动把数值类型(777)转为字符串类型
localStorage.setItem('msg2', 777)
// 会将msgTest进行替换
localStorage.setItem('msgTest', 'Helloworld!!!!')
// 储存对象,通过JSON.stringify方法将对象转为Json
localStorage.setItem('person', JSON.stringify(p))
}
function readTest() {
const msgTest = localStorage.getItem('msgTest')
const result = localStorage.getItem('person')

console.log(msgTest)
// 由于person储存的字符串,所以读取的也是字符串,需要使用JSON.parse方法转为对象
console.log(JSON.parse(result))

// 读取不存在的数据将返回null
console.log(localStorage.getItem('ouo'))

}
function deleteTest() {
localStorage.removeItem('msgTest')
}
function deleteAllTest() {
localStorage.clear()
}
</script>

</html>

localStorage只能储存字符串,所以会自动把数值类型转为字符串类型,并且多次保存同义数据时,会进行替换操作

图片

图片

会话储存

会话储存空间(sessionStorage):与localStorage使用方式相同。但是在浏览器关闭时(即结束会话),数据会被清空,再次打开浏览器时,不能够看到之前的数据。

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
<!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>
</head>

<body>
<button onclick="saveTest()">保存数据测试</button>
<button onclick="readTest()">读取数据测试</button>
<button onclick="deleteTest()">删除数据测试</button>
<button onclick="deleteAllTest()">删除所有数据测试</button>
</body>
<script type="text/javascript">

let p = { name: "ahzoo", age: 18 }
function saveTest() {
sessionStorage.setItem('msgTest', 'Helloworld')
// 由于sessionStorage只能储存字符串,所以会自动把数值类型(777)转为字符串类型
sessionStorage.setItem('msg2', 777)
// 会将msgTest进行替换
sessionStorage.setItem('msgTest', 'Helloworld!!!!')
// 储存对象,通过JSON.stringify方法将对象转为Json
sessionStorage.setItem('person', JSON.stringify(p))
}
function readTest() {
const msgTest = sessionStorage.getItem('msgTest')
const result = sessionStorage.getItem('person')

console.log(msgTest)
// 由于person储存的字符串,所以读取的也是字符串,需要使用JSON.parse方法转为对象
console.log(JSON.parse(result))

// 读取不存在的数据将返回null
console.log(sessionStorage.getItem('ouo'))

}
function deleteTest() {
sessionStorage.removeItem('msgTest')
}
function deleteAllTest() {
sessionStorage.clear()
}
</script>

</html>

图片

图片

计算属性和侦听器

计算属性(computed)

基本使用

对已有属性进行处理,得到一个全新的属性,即为计算属性,使用computed表示。

原理:底层借助Object.defineproperty方法提供的getter和setter。

method相比,computed内部有缓存机制,可以复用,效率会更高点

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
<!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>Ahzoo</title>
</head>

<body>
<!-- 提供一个容器 -->
<div id=root>

<button @click="onClick"> 确定 </button> <br/>

<!-- 多次调用userInfo时,除非数据发生变化,否则只有首次会执行get方法,后面会直接读取缓存 -->
{{ userInfo }}<br/>
{{ userInfo }}<br/>
{{ userInfo }}<br/>
{{ userInfo }}<br/>
<button @click="onModify"> 修改 </button>

</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<script type="text/javascript">

Vue.config.productionTip = false;

// 创建VM(vue实例)
new Vue({
el: "#root",
data: {
account: "ahzoo",
email: "12345@qq.com"
},
methods: {
onClick() {
console.log(this.userInfo)
},
onModify(){
this.userInfo = {account:"ouo",email:"999@qq.com"}
}
},
computed: {
userInfo: {
// get方法的返回值即为userInfo的值
// get方法的执行时机:
// 1.当user 初次 被读取时 2.所依赖的数据发生 变化 时
// 这里的(get)函数不能写箭头函数,因为所有 “vue管理的函数” 都只能用普通函数
get() {
console.log("get被调用")
const result = "account:" + this.account + " || email:" + this.email
console.log(result)
// 此处的this即为VM
return result
},
// get方法是必写的但是下面set方法可以在有需要时再写
// set方法执行时机:
// 当前属性(userInfo)被修改时
set(value){
console.log(value)
this.account=value.account
this.email=value.email
}
}
}
})
</script>

</body>

</html>

图片

简写

computed中只有set没有get时,就可以使用简写形式。

简写形式如下:

1
2
3
4
5
6
7
8
9
computed: {
userInfo() {
console.log("get被调用")
const result = "account:" + this.account + " || email:" + this.email
console.log(result)
// 此处的this即为VM
return result
}
}

侦听器(watch)

基本使用

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
<!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>Ahzoo</title>
</head>

<body>
<!-- 提供一个容器 -->
<div id=root>

<button @click="onClick"> 修改属性 </button> <br/>
<button @click="onModify"> 修改计算属性 </button> <br/>


</div>

<script src="https://npm.elemecdn.com/vue@2/dist/vue.js"></script>

<script type="text/javascript">

Vue.config.productionTip = false;

// 创建VM(vue实例)
new Vue({
el: "#root",
data: {
num:0,
account: "ahzoo",
email: "12345@qq.com"
},
methods: {
onClick() {
this.num=1
},
onModify(){
this.userInfo = {account:"ouo",email:"999@qq.com"}
}
},
watch:{
// 写需要侦听的属性(注意:这里的num是使用对象的简写形式,完整写法为'num',需要加引号)
num:{
// 设置参数:immediate表示侦听的属性初始化时立即执行一次handler方法
immediate: true,
// 当侦听的属性(num)改变时,handler就会被调用
handler(newValue,oldValue){
// 可以在handler方法中接收改变前后的值
console.log("num改变",newValue,oldValue)
}
},
// 还可以侦听计算属性
userInfo:{
handler(){
// 可以在handler方法中接收改变前后的值
console.log("userInfo改变")
}
}
}
})
</script>

</body>

</html>

如果实例创建完成,还可以使用vm进行侦听:

1
2
3
4
5
6
7
8
9
10
11
      //这里的num不可以使用对象的简写形式,必须加引号
vm.$watch('num',{
// 这里面和watch写法一致
// 设置参数:immediate表示侦听的属性初始化时立即执行一次handler方法
immediate: true,
// 当侦听的属性(num)改变时,handler就会被调用
handler(newValue,oldValue){
// 可以在handler方法中接收改变前后的值
console.log("num改变",newValue,oldValue)
}
})

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
<!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>Ahzoo</title>
</head>

<body>
<div id=root>

<button @click="onClick"> 修改账户 </button> <br />

<button @click="changeEmail"> 修改邮箱(测试深度监视) </button> <br />

</div>

<script src="https://npm.elemecdn.com/vue@2/dist/vue.js"></script>

<script type="text/javascript">

Vue.config.productionTip = false;

new Vue({
el: "#root",
data: {
userInfo: {
account: "ahzoo",
email: "12345@qq.com",
}
},
methods: {
onClick() {
this.userInfo.account = "ouo"
},
changeEmail() {
this.userInfo.email = "999@qq.com"
}
},
watch: {
// 监视多级属性中,某个属性(account)的变化
'userInfo.account': {
handler() {
// 可以在handler方法中接收改变前后的值
console.log("account改变")
}
},
// 此时直接监视userInfo将不会生效,因为watch默认是无法监视data中的多层次属性的改变的,即watch默认监视的是key(userInfo)中的value(userInfo中的(地址)值)而不是userInfo中的变量的数值
userInfo: {
// 设置deep属性(深度监视)后,watch就可以监视data中的多层次属性的改变
deep: true,
handler() {
console.log("userInfo改变")
}
}
}
})
</script>

</body>

</html>

图片

简写

watch:

1
2
3
4
5
6
7
watch: {
// 当监视属性只有handler方法,不需要设置配置项时,就可以使用简写形式
num(newValue, oldValue) {
// 可以在handler方法中接收改变前后的值
console.log("num改变", newValue, oldValue)
}
}

vm:

1
2
3
4
5
6
      //这里的num不可以使用对象的简写形式,必须加引号
// 这里不能写箭头函数,因为所有 “vue管理的函数” 都只能用普通函数(funnction)
vm.$watch('num', function () {
// 可以在handler方法中接收改变前后的值
console.log("num改变", newValue, oldValue)
})

计算属性与侦听器对比

大部分情况下使用计算属性是更合适的

1
<div id="demo">{{ userInfo }}</div>

侦听器写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
new Vue({
el: "#root",
data: {
account: "ahzoo",
email: "12345@qq.com",
userInfo: ""
},
watch: {
account(newAccount) {
this.userInfo = "account:" + newAccount + " || email:" + this.email
},
email(newEmail) {
this.userInfo = "account:" + this.account + " || email:" + newEmail
}
},
})

可以看到上面代码是命令式且重复的。而用计算属性则简单的多

1
2
3
4
5
6
7
8
9
10
11
12
13
14

new Vue({
el: "#root",
data: {
account: "ahzoo",
email: "12345@qq.com"
},
computed: {
userInfo() {
const result = "account:" + this.account + " || email:" + this.email
return result
}
}
})

但是当需要在数据变化时执行异步或开销较大的操作时,就需要使用侦听器了

比如我们需要设置在修改属性1秒之后再修改结果:

侦听器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 new Vue({
el: "#root",
data: {
account: "ahzoo",
email: "12345@qq.com",
userInfo: ""
},
watch: {
account(newAccount) {
// 注意,定时器不是vue管理的,所以可以写箭头函数,且此处只能用箭头函数,因为定时器是由windows管理的,所以函数中的this就是windows(可以再函数中打印this测试),而使用箭头函数不会创建自己的this,那么此时的this就是定时器的外部函数account中的this(account是vue管理的,所以this就是vm)
//普通函数:setTimeout(funnction(){
setTimeout(() => {
this.userInfo = "account:" + newAccount + " || email:" + this.email
}, 1000)
},
email(newEmail) {
this.userInfo = "account:" + this.account + " || email:" + newEmail
}
},
})

而计算则无法实现此效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

new Vue({
el: "#root",
data: {
account: "ahzoo",
email: "12345@qq.com"
},
computed: {
userInfo() {
// 此时修改将不会生效,因为修改的结果返回给了定时器,而不是userInfo
setTimeout(() => {
const result = "account:" + this.account + " || email:" + this.email
return result
}, 1000)
// 只有在这里return才行,但是并未达到暂停1秒再修改的效果
// return "account:" + this.account + " || email:" + this.email
}
}
})

总结:

所有vue管理的函数,最好写成普通函数,此时this的指向为vm或组件的实例对象;

所有非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
<!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>Ahzoo</title>
</head>

<body>
<div id=root>
<input type="text" v-model="keyWorld">
<li v-for="(item,index) in filterUser" :key="index">
{{ item.name }}-{{ item.age }}
</li>

</div>

<script src="https://npm.elemecdn.com/vue@2/dist/vue.js"></script>

<script type="text/javascript">

Vue.config.productionTip = false;

// 创建VM(vue实例)
new Vue({
el: "#root",
data: {
keyWorld: '',
userInfo: [
{ id: '101', name: '张三', age: 18 },
{ id: '102', name: '李三', age: 19 },
{ id: '103', name: '李四', age: 20 },
{ id: '104', name: '王五', age: 19 },
{ id: '105', name: '赵六', age: 18 },
],
filterUser: []
},
watch: {
keyWorld: {
// 初始化过滤器。由于过滤器搜索内容为空,所以会显示所有数组,完成初始化效果
immediate: true,
handler(newValue) {
// 使用filter方法进行过滤,将过滤后的结果赋值给filterUser
this.filterUser = this.userInfo.filter((user) => {
// 使用indexOf方法判断过滤后的字符是否包含输入的字符(包含为0,反之为-1)
const result = user.name.indexOf(newValue)
// 通过打印结果可以看到,过滤器是一个个遍历过滤的
console.log(result)
return result !== -1
})
}
}
},
})

</script>

</body>

</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
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
<!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>Ahzoo</title>
</head>

<body>
<div id=root>
<input type="text" v-model="keyWorld">
<li v-for="(item,index) in filterUser" :key="index">
{{ item.name }}-{{ item.age }}
</li>

</div>

<script src="https://npm.elemecdn.com/vue@2/dist/vue.js"></script>

<script type="text/javascript">

Vue.config.productionTip = false;
// 小技巧在vscode中使用#region和#endregion注释,可以将已注释的代码折叠起来
//#region
/*
new Vue({
el: "#root",
data: {
keyWorld: '',
userInfo: [
{ id: '101', name: '张三', age: 18 },
{ id: '102', name: '李三', age: 19 },
{ id: '103', name: '李四', age: 20 },
{ id: '104', name: '王五', age: 19 },
{ id: '105', name: '赵六', age: 18 },
],
filterUser: []
},
watch: {
keyWorld: {
// 初始化过滤器。由于过滤器搜索内容为空,所以会显示所有数组,完成初始化效果
immediate: true,
handler(newValue) {
// 使用filter方法进行过滤,将过滤后的结果赋值给filterUser
this.filterUser = this.userInfo.filter((user) => {
// 使用indexOf方法判断过滤后的字符是否包含输入的字符(包含为0,反之为-1)
const result = user.name.indexOf(newValue)
// 通过打印结果可以看到,过滤器是一个个遍历过滤的
console.log(result)
return result !== -1
})
}
}
},
})
*/
//#endregion

new Vue({
el: "#root",
data: {
keyWorld: '',
userInfo: [
{ id: '101', name: '张三', age: 18 },
{ id: '102', name: '李三', age: 19 },
{ id: '103', name: '李四', age: 20 },
{ id: '104', name: '王五', age: 19 },
{ id: '105', name: '赵六', age: 18 },
],
},
computed: {
// 回顾下计算属性调用时机:
// 1.当属性(filterUser) 初次 被读取时
// 2.所依赖的数据(此处的用做过滤器参数的keyWord)发生 变化 时
filterUser() {
return this.userInfo.filter((user) => {
// 使用indexOf方法判断过滤后的字符是否包含输入的字符(包含为0,反之为-1)
const result = user.name.indexOf(this.keyWorld)
// 通过打印结果可以看到,过滤器是一个个遍历过滤的
console.log(result)
return result !== -1
})
}
}
})

</script>

</body>

</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
<!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>Ahzoo</title>
</head>

<body>
<div id=root>
<input type="text" v-model="keyWorld">
<li v-for="(item,index) in filterUser" :key="index">
{{ item.name }}-{{ item.age }}
</li>
<button @click="sortType = 0">不排序</button>
<button @click="sortType = 1">升序</button>
<button @click="sortType = 2">降序</button>

</div>

<script src="https://npm.elemecdn.com/vue@2/dist/vue.js"></script>

<script type="text/javascript">

Vue.config.productionTip = false;

new Vue({
el: "#root",
data: {
keyWorld: '',
sortType: 0, // 0表示不排序,1表示升序,2表示降序
userInfo: [
{ id: '101', name: '张三', age: 18 },
{ id: '102', name: '李三', age: 21 },
{ id: '103', name: '李四', age: 19 },
{ id: '104', name: '李五', age: 20 },
{ id: '105', name: '赵六', age: 18 },
],
},
computed: {
filterUser() {
const filterResult = this.userInfo.filter((user) => {
const result = user.name.indexOf(this.keyWorld)
return result !== -1
})
if (this.sortType) {
filterResult.sort((s1, s2) => {
// sort排序方法:前(s1)-后(s2)为升序,后-前为降序
return this.sortType === 1 ? s1.age - s2.age : s2.age - s1.age
})
}
// 将排序结果返回
return filterResult
}
}
})

</script>

</body>

</html>

图片

Vue.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
<!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>Ahzoo</title>
</head>

<body>
<!-- 提供一个容器 -->
<div id=root>

<button @click="onClick"> 增加属性 </button> <br />
{{userInfo.age}}

</div>

<script src="https://npm.elemecdn.com/vue@2/dist/vue.js"></script>

<script type="text/javascript">

Vue.config.productionTip = false;

// 创建VM(vue实例)
new Vue({
el: "#root",
data: {
userInfo: {
account: "ahzoo",
email: "12345@qq.com",
}
},
methods: {
onClick() {
Vue.set(this.userInfo, 'age',18)
//写法二:
// 注:vm._data.userInfo === vm.userInfo
// 此处的this就是vm
// this.$set(this._data.userInfo, 'age',18)

// 不能直接给vue中的data追加数据
// Vue.set(this, 'age',18)
}
},
})

</script>

</body>

</html>

图片

局限性:

只能给data对象追加数据,而不能直接给vue中的data追加数据

官网对vue.set Api的解释:

向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = 'hi')

注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。