Skip to content
On this page

谁是依赖?

这就引出了 Watcher 类,它算是每个数据的依赖,每个数据可能存在很多依赖,所以我们才会把这些依赖放到一个 Dep 类的数组里;

数组中的每一个元素都是一个 watch 实例,只要更新这个数组里的 watch 实例就行了,其实 watch 实例中有个回调函数,就是更新视图的函数

在编译阶段会对不同的数据进行newWatcher(vm,expOrFn,cb);在 wtcher 类中会进行以下操作

  1. 我们把 wacher 实例放到 Dep 的静态属性 target 上
  2. 然后调用数据的 getter ,把依赖(也就是Wathcer实例)添加到 Dep 实例的数组中去
  3. 当设置数据时,会触发 new Watcher() 传入的回调函数cb

下面来看代码:

  • 地址:src/core/observer/watcher.js
js
/**
     * 使一个对象转化成可观测对象
     * @param { Component } vm vue实例
     * @param { string | Function } expOrFn 表达式,要watch 的属性名称
     * @param { Function } cb 更新视图的回调函数
     */
    class Watcher {
        constructor(vm,expOrFn,cb){
            this.vm=vm //vue实例
            this.getter = expOrFn //要观察的表达式
            this.cb=cb //回调函数

            // expOrFn可以是字符串或者函数
            // 什么时候会是字符串,例如我们正常使用的时候,watch: { x: fn }, Vue内部会将 `x` 这个key 转化为字符串
            // 什么时候会是函数,其实 Vue 初始化时,就是传入的渲染函数 new Watcher(vm, updateComponent, ...);

            if (typeof expOrFn === 'function') {
              this.getter = expOrFn
            } else {
              this.getter = parsePath(expOrFn)
            }
            this.value=this.get()
        }

        get (){
            let value
            const vm = this.vm
            // 我们把wacher实例放到Dep静态属性的target上
            //vue源码中将其封装成了一个方法pushTarget,在src/core/observer/dep.js
            Dep.target = this 
            value  = this.getter.call(vm, vm) //触发getter添加依赖
            Dep.target=null //释放
            return value
        }
        update(){
           //  触发回调,更新视图
           this.cb.call(this.vm, value, oldValue)
        } 
    }

接下来,我们对于整体流程进行一个梳理。

Released under the MIT License.