要理解watch 的实现过程,先看下watch 的几种使用方式
一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个 property。
var vm = new Vue({
data: {
a: 1, b: 2, c: 3, d: 4, e: { f: { g: 5 } }
},
watch: {
a: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
},
// 方法名
b: 'someMethod',
// 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
c: {
handler: function (val, oldVal) { /* ... */ },
deep: true
},
// 该回调将会在侦听开始之后被立即调用
d: {
handler: 'someMethod',
immediate: true
},
// 你可以传入回调数组,它们会被逐一调用
e: [
'handle1',
function handle2 (val, oldVal) { /* ... */ },
{ handler: function handle3 (val, oldVal) { /* ... */ },
/* ... */ }
],
// watch vm.e.f's value: {g: 5}
'e.f': function (val, oldVal) { /* ... */ }
}
})
vm.a = 2 // => new: 2, old: 1
接下来我们顺着源码来梳理下watch 的流程
- 在state.js 中初始化 watch
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch) initWatch(vm, opts.watch)
}
function initWatch (vm: Component, watch: Object) {
for (const key in watch) {
const handler = watch[key]
if (Array.isArray(handler)) {
for (let i = 0; i < handler.length; i++) {
createWatcher(vm, key, handler[i])
}
} else {
createWatcher(vm, key, handler)
}
}
}
这里将watch 的几种写法做了适配。 最终调用 createWatcher
function createWatcher (vm: Component, key: string, handler: any) {
let options
if (isPlainObject(handler)) {
options = handler
handler = handler.handler
}
if (typeof handler === 'string') {
handler = vm[handler]
}
vm.$watch(key, handler, options)
}
在这里我们看到,会调用watch挂载到 vm上了
Vue.prototype.$watch = function (
expOrFn: string | Function,
cb: Function,
options?: Object
): Function {
const vm: Component = this
options = options || {}
options.user = true
const watcher = new Watcher(vm, expOrFn, cb, options)
if (options.immediate) {
cb.call(vm, watcher.value)
}
return function unwatchFn () {
watcher.teardown()
}
}
在这里我们看到 $watch 函数返回了一个函数。 返回的函数执行可以将添加的观察取消掉。通过const watcher = new Watcher(vm, expOrFn, cb, options) 添加了观察的值,和对应的回调函数。 具体原理可以参考 之前写的文章 juejin.cn/post/699458…