Dep和Observer的关系就是Observer监听整个data,遍历data的每个属性给每个属性绑定defineReactive方法劫持getter和setter, 在getter的时候往Dep类里塞依赖(dep.depend),在setter的时候通知所有watcher进行update(dep.notify)。
Watcher类(观察者)Watcher类的角色是观察者,它关心的是数据,在数据变更之后获得通知,通过回调函数进行更新。
由上面的Dep可知,Watcher需要实现以下两个功能:
dep.depend()的时候往subs里面添加自己
dep.notify()的时候调用watcher.update(),进行更新视图
同时要注意的是,watcher有三种:render watcher、 computed watcher、user watcher(就是vue方法中的那个watch)
var uid = 0 import {parsePath} from "../util/index" import Dep from "./dep" export default class Watcher{ constructor(vm,expr,cb,options){ this.vm = vm // 组件实例 this.expr = expr // 需要观察的表达式 this.cb = cb // 当被观察的表达式发生变化时的回调函数 this.id = uid++ // 观察者实例对象的唯一标识 this.options = options // 观察者选项 this.getter = parsePath(expr) this.value = this.get() } get(){ // 依赖收集,把全局的Dep.target设置为Watcher本身 Dep.target = this const obj = this.vm let val // 只要能找就一直找 try{ val = this.getter(obj) } finally{ // 依赖收集完需要将Dep.target设为null,防止后面重复添加依赖。 Dep.target = null } return val } // 当依赖发生变化时,触发更新 update() { this.run() } run() { this.getAndInvoke(this.cb) } getAndInvoke(cb) { let val = this.get() if(val !== this.value || typeof val == 'object') { const oldVal = this.value this.value = val cb.call(this.target,val, oldVal) } } }要注意的是,watcher中有个sync属性,绝大多数情况下,watcher并不是同步更新的,而是采用异步更新的方式,也就是调用queueWatcher(this)推送到观察者队列当中,待nextTick的时候进行调用。
这里的parsePath函数比较有意思,它是一个高阶函数,用于把表达式解析成getter,也就是取值,我们可以试着写写看:
export function parsePath (str) { const segments = str.split('.') // 先将表达式以.切割成一个数据 // 它会返回一个函数 return obj = > { for(let i=0; i< segments.length; i++) { if(!obj) return // 遍历表达式取出最终值 obj = obj[segments[i]] } return obj } } Dep与Watcher的关系watcher 中实例化了 dep 并向 dep.subs 中添加了订阅者, dep 通过 notify 遍历了 dep.subs 通知每个 watcher 更新。
总结 依赖收集initState 时,对 computed 属性初始化时,触发 computed watcher 依赖收集
initState 时,对侦听属性初始化时,触发 user watcher 依赖收集(这里就是我们常写的那个watch)
render()时,触发 render watcher 依赖收集
re-render 时,render()再次执行,会移除所有 subs 中的 watcer 的订阅,重新赋值。
observe->walk->defineReactive->get->dep.depend()-> watcher.addDep(new Dep()) -> watcher.newDeps.push(dep) -> dep.addSub(new Watcher()) -> dep.subs.push(watcher) 派发更新组件中对响应的数据进行了修改,触发defineReactive中的 setter 的逻辑
然后调用 dep.notify()
最后遍历所有的 subs(Watcher 实例),调用每一个 watcher 的 update 方法。
set -> dep.notify() -> subs[i].update() -> watcher.run() || queueWatcher(this) -> watcher.get() || watcher.cb -> watcher.getter() -> vm._update() -> vm.__patch__() 推荐阅读【Vue源码学习】响应式原理探秘
JS定时器执行不可靠的原因及解决方案
从如何使用到如何实现一个Promise
超详细讲解页面加载过程
原文首发地址点这里,欢迎大家关注公众号 「前端南玖」。
我是南玖,我们下一期见!!!