目录
[2、计算属性的原理------getter 和 setter](#2、计算属性的原理——getter 和 setter)
[3、computed 里返回一个函数------给 computed 属性中的方法传参](#3、computed 里返回一个函数——给 computed 属性中的方法传参)
[4、computed 与 methods 的区别](#4、computed 与 methods 的区别)
[2、使用 watch的注意事项](#2、使用 watch的注意事项)
[5、watch 与 $watch 的关系](#5、watch 与 $watch 的关系)
[6、watch 的使用案例](#6、watch 的使用案例)
[三、计算属性 和 侦听器 适用场景](#三、计算属性 和 侦听器 适用场景)
[四、推荐几篇关于 computed 和 watch 的原理的深度解析好文](#四、推荐几篇关于 computed 和 watch 的原理的深度解析好文)
一**、计算属性(computed)**
1、计算属性的特点
- 支持 数据缓存------页面重新渲染时,只有数据发生了改变,才会执行相应的函数。
- 减少模板中计算逻辑。
- 依赖"响应式数据"。
2、计算属性的原理------getter 和 setter
在computed中的,属性都有一个get和一个set方法。当 computed 属性的属性值是函数,那么默认会走get方法,函数的返回值就是属性的值;当数据变化时,调用set方法。
计算属性默认只有 getter,在需要时你也可以提供一个 setter。
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
再运行 vm.fullName = 'John Doe'
时,setter 会被调用,vm.firstName
和 vm.lastName
也会相应地被更新。
实战应用案例:用computed监听:在子组件中更新父组件中传过来的变量
3、computed 里返回一个函数------给 computed 属性中的方法传参
<template lang="pug">
.topic-list(v-for='item in topicList')
a(:href='splitUrl(item.id)' target='_blank') {
{item.name}}
</template>
<script>
export default {
computed: {
splitUrl () {
return id => `${window.location.origin}/list/topic/${id}${this.isFromYY ? `?yyId=${this.info.yyId}` : ''}`
}
}
}
</script>
使用了闭包。
4、computed 与 methods 的区别
<template>
<div>
<p>reverseMsg1:{
{reverseMsg1}}</p>
<p>reverseMsg2:{
{reverseMsg2()}}</p>
<button @click="()=> $forceUpdate()">强制更新</button>
<div>
<input type="text" v-model="msg">
</div>
</div>
</template>
<script>
export default {
data() {
return {
msg: "hello vue"
}
},
computed: {
reverseMsg1() {
console.log("reverseMsg1执行了");
return this.msg.split("").reverse().join("");
}
},
methods: {
reverseMsg2(){
console.log("reverseMsg2执行了");
return this.msg.split("").reverse().join("");
}
},
}
</script>
由上面这个例子可以看出:
computed 与 methods 的区别:
- computed里定义的函数,函数名可以作为变量直接使用,默认 会执行该函数体。
- methods里定义的函数,必须调用 才能执行该函数体。
- computed里的数据会被缓存,每次更新都会先对比数据是否发生了变化,是才更新,否不执行,大大减少了渲染时间。
- methods里的函数不会被缓存,只要数据有更新,就会执行对应的函数。
- 如果你不希望有缓存,请用方法来替代。
二、侦听器(watch)
1、侦听器的特点
- 支持异步。
- 可以侦听任何逻辑,比如:函数节流,Ajax异步获取的数据,甚至操作DOM(不建议)。
- 页面重新渲染时,无论值变不变,都会重新执行,因为watch不支持数据缓存。
- 监听的函数接收两个参数,第一个参数是:新值,第二个参数是:原来的值。
2、使用 watch的注意事项
不要在 watch 中使用箭头函数,因为箭头函数没有 this,它的 this 会继承它的父级函数,但是它的父级函数是 window,导致箭头函数的 this 指向 window,而不是 Vue 实例。
3、watch的两个属性
(1)、deep
默认情况下,侦听器只会监听数据本身的改变,若要进行深度监听,那就需要使用 deep。
deep:其值是 true 或 false。深度监听,用来控制是否要监听对象内布值的变化(在页面初始化化时不会触发,只有当值改变时才会触发)。
不过,deep 无法监听到数组的变动和对象的新增,参考 vue 数组变异,只有以响应式的方式触发才会被监听到------[vm.set](https://cn.vuejs.org/v2/api/#vm-set "vm.set")。
(2)、immediate
默认情况下,侦听器需要 data 后面值改变了才会生效,若需要侦听器一进入页面就生效,那就需要使用 immediate。
immediate:其值是 true 或 false。组件加载时,用来控制是否要立即执行回调函数(在页面初始化后是否立即执行)。
4、深度监听一个对象时如何避免新值与旧值相等呢?
深度监听一个对象时,要监测到对象的具体需要被监听的属性,就能够解决新值与旧值相等的问题。
例如:问题再现
data () {
return {
obj: { a: 1 }
}
},
watch: {
obj (newVal, oldVal) {
console.log(newVal, oldVal)
}
}
上述代码中,当改变 obj 里属性的值时,打印 newVal 和 oldVal 的结果,发现他们是相等的。这是因为:改变一个对象里属性的值时,新值和旧值指向的是同一块内存区,所以无法拿到旧值,也可以理解为是浅拷贝的问题。我们可以通过直接监听该对象里的属性来解决这个问题:
data () {
return {
obj: { a: 1 }
}
},
watch: {
"obj.a": (newVal, oldVal) => {
console.log(newVal, oldVal)
}
}
5、watch 与 $watch 的关系
Vue 实例将会在实例化时调用$watch(),遍历 watch 对象的每一个属性
6、watch 的使用案例
<template>
<div>
{
{$data}}
<br/>
<button @click="() => {a += 1}">a+1</button>
</div>
</template>
<script>
export default {
data() {
return {
a: 1,
b: {c: 2, d: 3},
e: {
f:{
g: 4
}
},
h: []
}
},
watch: {
a(val, oldVal){
this.b.c += 1;
console.log("new: %s, old: %s", val, oldVal);
},
"b.c": function(val, oldVal){
this.b.d += 1;
console.log("new: %s, old: %s", val, oldVal);
},
"b.d": function(val, oldVal){
this.e.f.g += 1;
console.log("new: %s, old: %s", val, oldVal);
},
e: {
handler: function(){
this.h.push("哈")
},
deep: true
},
h(val, oldVal){
console.log("new: %s, old: %s", val, oldVal);
}
},
}
</script>
三、计算属性 和 侦听器 适用场景
computed 能做的,watch 都能做,反之不行。不过,能用 computed 的尽量用 computed。
如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个 多对一 或者 一对一,一般用computed。
比如:
<template>
<div>
{
{fullName}}
<div>firstName:<input type="text" v-model="firstName"></div>
<div>lastName:<input type="text" v-model="lastName"></div>
</div>
</template>
<script>
export default {
/**
* 用计算属性去实现
*/
data() {
return {
firstName: "charles",
lastName: "jake",
}
},
computed: {
fullName(){
return this.firstName + " " + this.lastName;
}
},
/**
* 用侦听器去实现
*/
// data() {
// return {
// fullName: "charles jake",
// firstName: "charles",
// lastName: "jake",
// }
// },
// watch: {
// firstName(val, oldVal){
// if(val != oldVal){
// this.fullName = val + " " + this.lastName;
// }
// },
// lastName(val, oldVal){
// if(val != oldVal){
// this.fullName = this.firstName + " " + val;
// }
// }
// },
}
</script>
四、推荐几篇关于 computed 和 watch 的原理的深度解析好文
深入理解 Vue Computed 计算属性:https://segmentfault.com/a/1190000010408657
Vue的数据依赖实现原理简析:https://segmentfault.com/a/1190000010014281#articleHeader2
vue中watch源码阅读笔记:[vue中watch源码阅读笔记 - Clarence2J - 博客园](https://www.cnblogs.com/Clarence2J/p/6860329.html "vue中$watch源码阅读笔记 - Clarence2J - 博客园")