引言
computed和watch都是基于wacther来实现的,渲染函数,computed和watch在源码中公用的一个watcher类,根据传入不同的参数来定义不同的wacther。
computed和watch的区别
1 computed的watcher默认不执行,需要用户获取的时候触发
2 computed有缓存效果,数据没有发生变化不会触发
3 computed是一对多
4 watch是多对一
5 watch支持异步
wacth用法
在讲解原理之前我们先看一看wacth
的使用方法。wacth
有三种使用方式:
1 字符串
2 数组
3 函数
1.字符串
new Vue({
watch: {
name:'fn'
},
methods: {
fn(newVal,oldVal){}
}
})
2.数组
new Vue({
watch: {
name:[
{
handler:'fn',
async:true
}
]
},
methods: {
fn(newVal,oldVal){}
}
})
3.函数
new Vue({
watch: {
name(newVal,oldVal){ }
}
})
源码解析
1.初始化wacther
vm是new Vue时传进来的对象
function initState (vm) {
...
const opts = vm.$options
//如果用户传进来wacth
if (opts.watch) {
initWatch(vm, opts.watch)
}
}
2.处理传入上述三种不同的类型
function initWatch (vm, watch) {
//循环wacth对象
for (const key in watch) {
//handler接收每一项,这里是name
const handler = watch[key]
//如果name是数组的话
if (Array.isArray(handler)) {
//循环name这个数组
for (let i = 0; i < handler.length; i++) {
createWatcher(vm, key, handler[i])
}
} else {
//如果是函数或者字符串
createWatcher(vm, key, handler)
}
}
}
function createWatcher (vm,expOrFn,handler,options) {
//如果是对象的话,因为数组的每一项是对象
if (isPlainObject(handler)) {
//options接收多余的值比如deep,async
options = handler
//这里传入的handler是对象{handler:'fn'},让handler指向这个函数名
handler = handler.handler
}
//如果是字符串的话
if (typeof handler === 'string') {
//直接在实例上获取name这个属性,因为methods中的方法都能在实例上获取到,这里获取的是fn函数
handler = vm[handler]
}
return vm.$watch(expOrFn, handler, options)
name fn 可能有,可能没有 async..
}
3.创建wacther
Vue.prototype.$watch = function (expOrFn,cb,options){
const vm= this
options = options || {}
//Watcher使用的是渲染wacther,设置user是为了区分渲染wacther和用户传入的wacth
options.user = true
const watcher = new Watcher(vm, expOrFn, cb, options)
}
wacther中的处理
class Watcher {
constructor(vm, expOrFn, cb, options) {
this.vm = vm
this.user = !!options.user
this.cb = cb
//上面我们传的expOrFn是key值 如果是一个函数那就是渲染wacther
if (typeof expOrFn === 'function') {
this.getter = expOrFn
} else {
//wacth方法 如果key是'a.b.c.d'那么这个方法parsePath是将d这个值从实例上一层一层的取出来,包装成一个函数 function(){return d} 如果不进行这层包装 下面方法会报错
this.getter = parsePath(expOrFn)
}
//接收初始化的数据
this.value = this.get()
}
get() {
//依赖收集的操作将当前的wacther存放在Dep.target中
pushTarget(this)
let value = this.getter.call(this.vm)
popTarget()
return value
}
//依赖收集的时候当数据发生改变时会触发这个方法
update() {
this.run()
}
run() {
//改变之后的数据
const value = this.get()
//改变前的数据
const oldValue = this.value
this.value = value
if (this.user) {//如果是wacth函数
//直接将对应函数执行并且将最新的值和老的值传递过去
this.cb.call(this.vm, value, oldValue)
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。