这年头你不研究点vue源码都不好意思说自己可以熟练使用vue,对于源码的的学习,我只说三件事:耐心,耐心,还是***耐心

响应式原理

源码分析

  • 监听器Observer拦截所有的属性并监听属性的变化
// core/instance/state.js
function initData(){
      .....
    // 对data的是否是对象
    // 对属性的判重等操作
   observe(data, true /* asRootData */) //去监听数据
}
// core/observer/index.js
export function observe(value, asRootData){
    let ob
    ob = new Observer(value) // 创建一个监听器实例
    return ob
}
// core/observer/index.js
export class Observer{
    constructor(value) {
        if (Array.isArray(value)) { // 是数组类型
            // 重写数组的一些方法   'push', 'pop', 'shift','unshift','splice','sort', 'reverse'
            // ...
        } else {
            this.walk(value)
        }
    }
    walk(obj) {
        const keys = Object.keys(obj)
        for (let i = 0; i < keys.length; i++) {
            defineReactive(obj, keys[i]) // 遍历属性添加拦截
        }
    }
}    
//  core/observer/index.js
export function defineReactive(){
    const dep = new Dep()   //每个属性 对应一个Dep,
    // ...
     !shallow && observe(val) // data里面可能存在嵌套对象,继续递归
     Object.defineProperty(obj, key,{
         get(){
             dep.depend()  // 在get中收集watcher 访问时触发 
         },
         set(){
             childOb = !shallow && observe(newVal) //set赋值了对象  继续递归这个对象进行拦截
             dep.notify() //发布订阅消息
         }
     })
}

  • 页面中一个数据可能在多个地方被使用,所以一个属性可能对应多个Watcher,设置一个Dep来管理Watcher
// core/observer/dep.js
class Dep(){
    depend(){
        if (Dep.target) { //Dep.target是一个watcher
            Dep.target.addDep(this)
        }
    }
    addSub (sub: Watcher) {
        this.subs.push(sub) //一个Dep对应多个Watcher
    }
    notify () {
        for (let i = 0, l = subs.length; i < l; i++) { //遍历收集的Watcher,
            subs[i].update()//依次更新Watcher
        }
    }
}

  • 订阅者Watcher接收到属性的变化去更新相应的视图
// core/observer/watcher.js
class Watcher(){
    addDep(dep){
        dep.addSub(this) //将watcher添加到dep.subs中, 里面存放的是watcher列表
    }
    update() {
    //同步或者异步更新? 
     this.run()
    }
    run(){
        const  value = this.get() // 获取当前的值
        if (
            value !== this.value ||
            isObject(value) ||
            this.deep
        ){ // 旧值新值是否不等,值是一个对象,deep模式
            this.cb.call(this.vm, value, oldValue)  //执行Watcher的回调函数
        }
    }
    get(){
        value = this.getter.call(vm, vm) //重新触发属性的get方法,收集watcher
        this.cleanupDeps()
        return value
    }
    cleanupDeps() {
        //遍历所有的Dep,移除每个dep里面对Watcher的订阅
        let i = this.deps.length
        while (i--) {
            const dep = this.deps[i]
            if (!this.newDepIds.has(dep.id)) {
                // 根据订阅的id移除掉废弃的Watcher,避免更新时的额外消耗
                dep.removeSub(this)
            }
        }
        //  将newDeps,newDepIds赋值给depIds,deps,然后清空
        let tmp = this.depIds
        this.depIds = this.newDepIds
        this.newDepIds = tmp
        this.newDepIds.clear()
        tmp = this.deps
        this.deps = this.newDeps
        this.newDeps = tmp
        this.newDeps.length = 0
    }
}

具体过程

依赖收集

get()===>dep.depend()===>(Dep.target就是Watcher)Dep.target.addDep()===>dep.addSub()

  • 去读取属性的时候,触发属性的get拦截操作
  • 进入到Dep对象中,每个属性对应创建一个Dep,使用dep.depend()方法去收集相关的依赖
  • 进入到watcher.addDep方法,Watcher对象中存在一个newDepsnewDepIds属性,它存储了与这个Watcher相关的Dep对象,一个Watcher对应多个DepnewDeps里面存储的是Dep列表
  • 重新回到Dep对象中,将这个Watcher添加到subs属性中,一个Dep对应多个Watcher
graph LR; prop1-->dep1-->watcher1 prop2-->dep2-->watcher2 & watcher1 prop3-->dep3-->watcher3 & watcher2 prop4-->dep4-->watcher4

派发更新

set()--->dep.notify()--->watcher.update()--->watcher.run()--->watchrt.get()--->this.getter.call(vm, vm)--->watcher.cleanupDeps()---> this.cb.call(this.vm, value, oldValue)

  • 给属性赋值的时候触发set()拦截操作
  • 进入这个属性对应的Dep对象中,遍历subs属性里面存放的相关的watcher列表,准备更新
  • 进行更新时会判断更新的方式是同步还是异步更新,然后更具更新方式执行不同的操作
  • 同步更新进入到run()方法,触发get()方法获取更新的值
  • 进入get()方法会重新调用his.getter.call(vm, vm)触发getter拦截器,获取属性的值,并且重新去收集依赖
  • 重新收集依赖之后会触发clearupDeps()清理掉与当前更新无关的的`Watcher
  • 执行watcher中得回调函数