Vue 源码
你想要的 vue 源码分析 · 20
watch 是基于 Watcher 实例实现的,是一个 user watcher,支持不同类型 watcher 的 4 个类型 deep watcher 为了监听对象的子属性的变化,所以在 watch 中设置 deep:true 属性,实际上会 触发 traverse 函数,traverse 函数是对对象做深层遍历,遍历就会触发子对象的 getter,这样就依赖收集,订阅子对象的 watcher,…
watch 是基于 Watcher 实例实现的,是一个 user watcher,支持不同类型 watcher 的 4 个类型 deep watcher 为了监听对象的子属性的变化,所以在 watch 中设置 deep:true 属性,实际上会 触发 traverse 函数,traverse 函数是对对象做深层遍历,遍历就会触发子对象的 getter,这样就依赖收集,订阅子对象的 watcher,…
为了监听对象的子属性的变化,所以在 watch 中设置 deep:true 属性,实际上会 触发 traverse 函数,traverse 函数是对对象做深层遍历,遍历就会触发子对象的 getter,这样就依赖收集,订阅子对象的 watcher,这样对 watch 中每一个值的修改,都会触发 watcher 的回调函数
通过 this.$watch 创建一个 watcher
就是计算属性 computed
一般是把 watcher 推到一个队列中,在 nextTick 中执行 watcher 的回调函数 ,sync watcher 是在 tick 中执行 watcher 的回调函数
// src/core/instance/state.js
function initWatch(vm: Component, watch: Object) {
// 对watch进行遍历,拿到每一个handler,如果handler是一个数组,则遍历这个数组
for (const key in watch) {
const handler = watch[key];
if (Array.isArray(handler)) {
for (let i = 0; i < handler.length; i++) {
// 最终调用createWatcher
createWatcher(vm, key, handler[i]);
}
} else {
createWatcher(vm, key, handler);
}
}
}
// src/core/instance/state.js
function createWatcher(vm: Component, expOrFn: string | Function, handler: any, options?: Object) {
// 对handler类型判断
if (isPlainObject(handler)) {
options = handler;
handler = handler.handler;
}
if (typeof handler === 'string') {
handler = vm[handler];
}
// 最终执行vm.$watch函数,$watch是Vue原型上的方法
return vm.$watch(expOrFn, handler, options);
}
// src/core/instance/state.js
Vue.prototype.$watch = function(expOrFn: string | Function, cb: any, options?: Object): Function {
const vm: Component = this;
// 如果cb是一个对象,则调用createWatcher
// this.$watch是Vue暴露在外面的钩子函数
if (isPlainObject(cb)) {
return createWatcher(vm, expOrFn, cb, options);
}
options = options || {};
options.user = true;
// 实例化一个watcher
// 这个watcher不是渲染watcher,而是一个 自定义watcher,所以watcher.user为true
// 一但watcher的数据有变化,就会执行watcher的润方法,执行回调函数cb
const watcher = new Watcher(vm, expOrFn, cb, options);
if (options.immediate) {
cb.call(vm, watcher.value);
}
// 该方法会调用teardown方法移除这个watcher
return function unwatchFn() {
watcher.teardown();
};
};
// src\core\observer\watcher.js
if (options) {
this.deep = !!options.deep;
this.user = !!options.user;
this.computed = !!options.computed;
this.sync = !!options.sync;
// ...
} else {
this.deep = this.user = this.computed = this.sync = false;
}
如果在 data 中定义了一个有子属性 b 的 a 对象,在 watch 中监听 a 对象,做了一个 a.b=2 的操作,但是 watch 却没有监听到 a.b 的 getter,也就是说,不用 depp watcher 就只能触发 a 的 getter, 不能触发 a.b 的 getter,虽然也触发了 a.b 的 setter,但是没有可以通知的对象,所以不会触发 watch 的回调函数
var vm = new Vue({
data() {
a: {
b: 1;
}
},
watch: {
a: {
handler(newVal) {
console.log(newVal);
}
}
}
});
-->
watch: {
a: {
deep: true,
handler(newVal) {
console.log(newVal)
}
}
}
在 watcher 执行 get 求值的过程中,如果 deep 为 true,会调用 traverse 函数
// src\core\observer\watcher.js
get() {
let value = this.getter.call(vm, vm)
// ...
if (this.deep) {
traverse(value)
}
}
// src/core/observer/traverse.js
// 实际上是对一个对象做深层递归遍历,在遍历过程中就是对子对象的访问,会触发子对象的getter
// 这样就可以收集到依赖,然后订阅子对象的的watcher
// 在遍历过程中会记录子对象watcher的dep.id到seenObjects中,避免以后重复访问
// 这样对watch中的每一个值修改,也会触发watcher的回调函数
import { _Set as Set, isObject } from '../util/index';
import type { SimpleSet } from '../util/index';
import VNode from '../vdom/vnode';
const seenObjects = new Set();
export function traverse(val: any) {
_traverse(val, seenObjects);
seenObjects.clear();
}
function _traverse(val: any, seen: SimpleSet) {
let i, keys;
const isA = Array.isArray(val);
if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) {
return;
}
if (val.__ob__) {
const depId = val.__ob__.dep.id;
if (seen.has(depId)) {
return;
}
seen.add(depId);
}
if (isA) {
i = val.length;
while (i--) _traverse(val[i], seen);
} else {
keys = Object.keys(val);
i = keys.length;
while (i--) _traverse(val[keys[i]], seen);
}
}
通过 vm.$watch 创建的 watcher 就是一个 user watcher
// src\core\observer\watcher.js
// 对watcher求值,然后执行回调的时候,处理一下错误
get() {
if (this.user) {
handleError(e, vm, `getter for watcher "${this.expression}"`)
} else {
throw e
}
},
getAndInvoke() {
// ...
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)
}
}
computed watcher 就是 computed 计算属性
sync watcher
watch 中的数据发生变化的时候,触发 watcher.update,把这个 watcher 放到队列中,在 nextTick 才会执行这个队列中的所有 watcher 回调函数,一旦设置 sync,就可以在主线程中执行 watcher 回调函数
// src\core\observer\watcher.js
update () {
if (this.computed) {
// ...
} else if (this.sync) {
// 如果sync属性有true,那么直接执行watcher的回调函数,watcher的回调函数是一个同步的过程
this.run()
} else {
queueWatcher(this)
}
}
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论