「这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战」
Vue.js 源码分析 - watcher 源码
接下来我们来查看 $watcher方法的实现
创建监听器
- 路径:src\core\instance\state.js
- 方法: initState
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
我们会首先获取页面中是否有 watch 函数,如果有的话,将该方法内的内容传入到
initWatch中
initWatch
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)
}
}
}
initWatch 方法非常的简单
- 首先遍历 watch 对象,也就是选项中传入的 watch 对象
- 然后获取我们的
handler方法,并且判断是否是数组 - 然后根据情况调用
createWatcher方法
createWatcher
参数:
vm: Component当前的实例属性expOrFn: string | Function-当前监听的属性handler: any- 属性对应的值options?: Object
- 代码块1
解析:if (isPlainObject(handler)) { options = handler handler = handler.handler }isPlainObject:判断当前属性对应的值是否是一个原生的对象,如果是的话,会直接将handler保存到options中,并且取出对象中的handler.handler - 代码块2
if (typeof handler === 'string') {
handler = vm[handler]
}
解析: 如果是字符串就直接将vue实例中的对象直接进行赋值
$watch
- 获取 Vue 实例 this,正是因为这块,所以
$watch是一个实例方法,并且没有对应的静态方法,理由就是使用到了 Vue 的实例
const vm: Component = this
- 判断
$watch的第二个参数cb是否是原始对象
if (isPlainObject(cb)) {
// 判断如果 cb 是对象执行 createWatcher
return createWatcher(vm, expOrFn, cb, options)
}
- 处理 options; 如果options 被处理过就进行赋值,否则直接赋值为空对象
options = options || {}
-
标记为用户 watcher;
user属性设置为 true 有什么深意呢? 如果是用户watcher的时候,需要加上try/catch进行报错的获取options.user = true- watcher类
- 路径:src\core\observer\watcher.js
- run方法
if (this.user) { try { this.cb.call(this.vm, value, oldValue) } catch (e) { handleError(e, this.vm, `callback for watcher "${this.expression}"`) } } else { this.cb.call(this.vm, value, oldValue) }
-
创建用户 watcher 对象
const watcher = new Watcher(vm, expOrFn, cb, options)
- 判断 immediate 如果为 true
if (options.immediate) {
// 立即执行一次 cb 回调,并且把当前值传入
try {
cb.call(vm, watcher.value)
} catch (error) {
handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)
}
}