0%

关于 watch

1,当数据改变时,直接触发对应操作;
2,可以监听的数据有三部分,即propsdatacomputed
3,所触发的对应操作函数中包含两个参数,第一个参数是新值newValue,第二个参数是旧值oldValue
4,不支持缓存,但支持异步,在数据变化时执行异步或开销较大的操作时使用;
5,一对多,即一个数据改变时,可以通过触发对应操作改变多个其他的数据。

关于 computed

1,computed 依赖于 data 中的数据,只有在它的相关依赖数据发生改变时才会重新求值;
2,用于处理复杂的逻辑运算;
3,支持缓存,但不支持异步;
4,多对一或一对一,即一个属性是由其他属性计算而来的,这个属性可能是依赖其他一个或多个属性。

示例代码

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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<div>watchMsg = {{ watchMsg }}</div>
<div>computedMsg = {{ computedMsg }}</div>
<div>msg = {{ msg }}</div>
<div>watchAsync = {{ watchAsync }}</div>
<button @click="msg = 1">msg = 1</button>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data() {
return {
msg: '0',
watchMsg: '',
watchAsync: ''
}
},
computed: {
computedMsg() {
return this.msg;
}
},
watch: {
msg(value) {
this.watchMsg = value;
// 模拟一个异步
setTimeout(() => {
this.watchAsync = value;
}, 1000)
}
}
})
</script>
</body>
</html>

在浏览器中运行上面的代码,当页面加载时,computed会先执行一次,显示结果如下:

1
2
3
4
watchMsg =
computedMsg = 0
msg = 0
watchAsync =

为什么watch没有触发监听呢?因为在Vue实例时,msg的值一直都为0,不存在数据变化,自然无法触发watchwatchMsg也不会发生变化。当点击按钮时,显示如下:

1
2
3
4
watchMsg = 1
computedMsg = 1
msg = 1
watchAsync = 1

可以看到,watchAsync = 1是过了一秒后才显示出来的,说明watch支持异步。

用法

观察 Vue 实例上的一个表达式或者一个函数计算结果的变化。回调函数得到的参数为新值和旧值。表达式只接受监督的键路径。对于更复杂的表达式,用一个函数取代。

模版代码:

1
2
3
4
5
watch: {
name(value) {
this.newName = value;
}
}

示例代码:

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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<div>大家好,我新名字是{{ newName }}</div>
<input v-model="name" />
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data() {
return {
name: '小明',
newName: ''
}
},
watch: {
name(value) {
this.newName = value;
}
}
})
</script>
</body>
</html>

在上面的例子中,watch 监听的都是一些简单的数据,如果我们需要监听一些比较复杂的数据,比如对象呢?这个时候我们就需要用到深度监听deep了。

深度监听

模版代码:

1
2
3
4
5
6
7
8
watch: {
childrens: {
handler(value) {
this.name = value.name;
},
deep: 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
32
33
34
35
36
37
38
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<div>大家好,我是{{ childrens.name }},今年{{ childrens.age }}岁了。</div>
<input v-model="childrens.name" />
<div>我是监听器,我监听到了 {{ oldName }}
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data() {
return {
childrens: {
name: '小明',
age: 18
},
name: '',
oldName: ''
}
},
watch: {
childrens: {
handler(value) {
this.name = value.name;
},
deep: true
}
}
})
</script>
</body>
</html>

总结

在日常开发中基本是离不开watch的,所以大家要多敲几遍代码熟悉一下。如果想了解更多watch的用法,大家可以到官网去查看一下,这里只是列举了经常用的的方法。既然watch可以监听,那么它和我们上次提到的computed有什么区别呢?下一章我们来看一下它们之间的区别。

介绍

methods 将被混入到 Vue 实例中。可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用。方法中的 this 自动绑定为 Vue 实例。

模版代码:

1
2
3
4
5
methods: {
add() {
this.count += 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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<div>{{ count }}</div>
<button @click="add">累加</button>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data() {
return {
count: 0
}
},
methods: {
add() {
this.count += 1;
}
}
})
</script>
</body>
</html>

通过上面的示例代码,我们可以发现:

  1. methods属性给可以给 Vue 定义add方法
  2. 在方法中使用this.可以直接访问data中的数据count

注意

很多小伙伴在定义方法时喜欢使用es6的语法,比如箭头函数() => {},那么在给 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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<div>{{ count }}</div>
<button @click="add">累加</button>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data() {
return {
count: 0
}
},
methods: {
add: () => {
this.count += 1;
}
}
})
</script>
</body>
</html>

在浏览器中运行上面的示例代码,我们可以发现,当我们点击累加按钮时,数据并没有出现出现累加的结果。这是为什么呢?
了解过箭头函数的小伙伴应该知道,() => {}有一个用法就是改变this的指向,那么在 Vue 中,箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例。因此我们可以得出结论:不应该使用箭头函数来定义 method 函数

前言

在日常开发中,如果说有哪些选项是经常使用的,computed绝对是其中一个。因为我们有时需要对原始数据进行更改后再输出。比如我在文本框输入了一大串数据,然后我希望这一大串数据可以颠倒显示出来,你也许会在模版内直接使用表达式,比如这样:

1
2
3
<div>
{{ msg.split('').reverse().join('') }}
</div>

这样写可以达到我们的要求,看起来也没什么问题。但是,如果我输入的这一大串数据需要在多个地方显示出来,总不能每个地方都写这么一个表达式吧?这样会很累的。Vue 也考虑了在模板中放入太多的逻辑会让模板过重且难以维护,所以computed出现了。

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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<div>{{ reversedMsg }}</div>
<input v-model="msg" />
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data() {
return {
msg: 'hello'
}
},
computed: {
// 颠倒
reversedMsg() {
return this.msg.split('').reverse().join('')
}
}
})
</script>
</body>
</html>

通过上面的示例代码,可以看到颠倒数据的表达式已经被统一在了computed中,这样写会给人更清晰的感觉,在后期维护中也会更方便。

前言

小编在日常开发中,基本是不使用propsData的,所以在这里我们了解一下即可。当然,可能对于一些开发人员会经常用到,比如在测试时,可以直接模拟一些数据,从而方便测试。

使用

创建实例时传递 props。主要作用是方便测试。只用于 new 创建的实例中。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<v-demo></v-demo>
</div>
<script type="text/javascript">
var Comp = Vue.extend({
props: ['msg'],
template: `<div>{{ msg }}</div>`
})
new Comp({
propsData: {
msg: 'hello'
}
}).$mount('#app');
</script>
</body>
</html>

在浏览器运行上面的代码,可以看到页面显示内容为hello,运行正常。

通过上面的代码,我们用三步解决了全局扩展的传值:

  1. 先在时全局扩展中加入props: ['msg']接受msg的值;
  2. 然后在 new 示例中使用propsData: { msg: 'hello'}msg赋值;
  3. 最后通过$mount挂载传递msg的值到全局扩展中。

在前面的一些章节中已经简单地使用过props,知道它是用来接受来自父组件的数据的。这一章来深入了解一下 props 的更多使用方法。

介绍

props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。

用法

选项 说明
type 数据类型
default 默认值
required 是否必填
validator 自定义验证函数

数据类型可以为StringNumberBooleanArrayObjectDateFunctionSymbol、任何自定义构造函数、或上述内容组成的数组。如果prop不是给定的类型将会抛出警告。

示例代码:

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>
<head>
<meta charset="utf-8">
<title></title>
<!-- 注意,引入vue.js时需要使用开发环境版本,这样才能在看到报错提示效果 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="number" v-model="msg" />
<v-demo :age="msg"></v-demo>
</div>
<script type="text/javascript">
Vue.component('v-demo', {
props: {
age: {
type: [Number, String],
default: 18,
required: true,
validator: function (value) {
return value >= 1;
}
}
},
template: `<div>我今年 {{ age }} 岁。</div>`
})
var vm = new Vue({
el: '#app',
data() {
return {
msg: 1
}
}
})
</script>
</body>
</html>

注意:
validator只是验证,这个函数返回值是布尔值,用这个布尔值来判断是否通过,它的返回值是验证作用,而不是修改props,如果想要修改原始数据,那么可以采用其他的方式,比如computed等方式修改,后面会讲到。

使用

Vue 实例的数据对象。Vue 将会递归将 data 的属性转换为 getter/setter,从而让 data 的属性能够响应数据变化。对象必须是纯粹的对象 (含有零个或多个的 key/value 对):浏览器 API 创建的原生对象,原型上的属性会被忽略。大概来说,data 应该只能是数据 - 不推荐观察拥有状态行为的对象。

模版代码:

1
2
3
4
5
data() {
return {
title: 'Hello World'
}
}

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<h1>{{ title }}</h1>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data() {
return {
title: 'Hello World'
}
}
})
</script>
</body>
</html>

是不是很熟悉?其实在开发过程中,data的使用是不可或缺的,我们以前写的那些例子,基本都是有用到的。

注意

当一个组件被定义,data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。

如果有需要深究的小伙伴,可以返回vue基础学习:组件基础(一)中的data 必须是一个函数进行了解,里面有两个详细的示例代码进行解释,在这里就不再详解。

其实在 Vue2.0基础学习 中,我们已经简单地了解过组件的使用了。在这一章中,我们主要来了解一下组件之间的通信。

父组件向子组件传递数据

使用 props 向子组件传递数据

示例代码:

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>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<c-b></c-b>
</div>
<script type="text/javascript">
// 子组件
var cA = {
props: ['msg'],
template: `<div>
<p>子组件收到:{{ msg }}</p>
</div>`
};
// 父组件
var cB = {
template: `<div>
<p>父组件说:我准备给子组件通信</p>
<c-a msg="我是父组件,我来给子组件通个信"></c-a>
</div>`,
components: {
cA: cA
}
};
var vm = new Vue({
el: '#app',
components: {
cB: cB
}
})
</script>
</body>
</html>

看上面的代码会感觉有点绕,因为子组件和父组件都是写在同一个文件里的,如果理解为分成三个文件,子组件一个,父组件一个,主文件一个,那么你就会清晰一些。

子组件向父组件传递数据

子组件主要通过事件传递数据给父组件,也就是在基础学习中的$emit

示例代码:

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>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<c-b></c-b>
</div>
<script type="text/javascript">
// 子组件
var cA = {
template: `<div>
<button @click="$emit('say', '爸爸好')">我是子组件,点我,我有话对父组件说</button>
</div>`
};
// 父组件
var cB = {
data() {
return {
msg: ''
}
},
template: `<div>
<p>我是父组件,子组件刚刚对我说:{{ msg }}</p>
<c-a @say="msg = $event"></c-a>
</div>`,
components: {
cA: cA
}
};
var vm = new Vue({
el: '#app',
components: {
cB: cB
}
})
</script>
</body>
</html>

兄弟组件传递数据

兄弟组件直接的传递数据方式有很多,比如:

  1. 子传父,父再传子,太麻烦了,不考虑
  2. vuex,状态管理
  3. eventBus,可以理解为 $emit$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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<c-a></c-a>
<c-b></c-b>
</div>
<script type="text/javascript">
var eventBus = new Vue();
// 兄弟组件A
var cA = {
template: `<div>
<button @click="say">我是兄弟组件A,点我,我有话对兄弟组件B说</button>
</div>`,
methods: {
say() {
eventBus.$emit('say', '你好,兄弟组件B');
}
}
};
// 兄弟组件B
var cB = {
data() {
return {
msg: ''
}
},
template: `<div>
<p>我是兄弟组件B,我是兄弟组件A刚刚对我说:{{ msg }}</p>
</div>`,
created() {
eventBus.$on('say', (val) => {
this.msg = val;
})
}
};
var vm = new Vue({
el: '#app',
components: {
cA: cA,
cB: cB
}
})
</script>
</body>
</html>

在这里只介绍eventBus,因为后期会讲到vuex,而且现在估计有一部分人不知道vuex是什么。

总结

上面的示例代码看起来可能会有些乱,其实只要多敲几遍就会感觉没有想象中那么难,在后期我们使用vue-cli时,这些就会显得更简单。而且组件之间的传递数据在日常开发中,使用是超级多的,所以一定要多练习,很重要,很重要,很重要,重要的事情说三遍。

前言

在前面的很多章节中,在我们自定义组件时经常会出现template标签,那么它到底是什么呢?

template是html5的一个新元素,主要用于保存客户端中的内容,表现为浏览器解析该内容但不渲染出来,可以将一个模板视为正在被存储以供随后在文档中使用的一个内容片段。

template标签在 Vue 中的作用:存放dom结构的

写法一:写在 template 标签里

模版代码:

1
2
3
<template id="demo">
<div>写在template标签里</div>
</template>

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<template id="demo">
<div>写在template标签里</div>
</template>
<div id="app">div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
template: '#demo'
})
</script>
</body>
</html>

写在script标签里

模版代码:

1
2
3
<script type="text/template" id="demo">
<div>写在script标签里</div>
</script>

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">div>
<script type="text/template" id="demo">
<div>写在script标签里</div>
</script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
template: '#demo'
})
</script>
</body>
</html>

直接写在选项里

模版代码:

1
template: '<div>直接写在选项里</div>'

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
template: '<div>直接写在选项里</div>'
})
</script>
</body>
</html>

总结

以上三种写法都可以达到我们需要的效果,在日常开发中,使用哪一种并没有特定的要求,哪种合适就选择哪一种。如果我们开发项目使用了vue-cli脚手架,那么就更简单了,直接新建一个xxx.vue文件,按照规则编写即可。

用法

将一个模板字符串编译成 render 函数。只在完整版时可用。

模版代码:

1
Vue.compile('<div>{{ msg }}</div>');

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app"></div>
<script type="text/javascript">
var res = Vue.compile('<div>{{ msg }}</div>');
new Vue({
el: '#app',
data: {
msg: 'hello'
},
render: res.render
})
</script>
</body>
</html>

在浏览器中运行代码可以看到页面正常显示hello,在这一过程中,到底经历了什么呢?

compile 的步骤

compile 的内容非常多,大致分为三块主要内容,就是parseoptimizegenerate
另外,compile 的作用是解析模板,生成渲染模板的 render

  1. 步骤一:parse
  • 接收 template 原始模板,按照模板的节点 和数据 生成对应的 ast 。
  1. 步骤二:optimize
  • 遍历递归每一个ast节点,标记静态的节点(没有绑定任何动态数据),这样就知道那部分不会变化,于是在页面需要更新时,减少去比对这部分DOM,从而达到性能优化的目的。
  1. 步骤三:generate
  • 把前两步生成完善的 ast 组装成 render 字符串。

因为目前是基础学习,所以这里只作为了解,后期如果有机会再深入。