vue2.0|思路篇|依赖收集

1,612 阅读4分钟

前言

  • 文章结构采用【指出阶段目标,然后以需解决问题为入口,以解决思路为手段】达到本文目标,若使诸君稍有启发,不枉此文心力^-^
  • 文分【思路篇】和【实现篇】,本文是思路篇,建议看两个窗口同步阅读-》vue2.0|实现篇|依赖收集

目标

第一阶段:Vue.mixin实现及生命周期实现

Vue文档:mixin
先看使用
## index.html
Vue.mixin({
            created(){
                console.log("mixin created");
            }
        })
Vue.mixin({
    created(){
        console.log("mixin2 created");
    }
})
let vm = new Vue({
    el: '#app',
    data(){
        return {
            name: '王志远',
            age: 23
        }
    },
    created () {
        console.log("my created");
    }
})

## 将会打印
mixin created
mixin2 created
my created
核心问题
  • 针对不同选项,如何实现不同的合并策略;
    • 数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
    • 同名钩子函数将合并为一个数组,因此都将被调用
  • 控制优先级: Vue.mixin内函数,即混入对象的钩子将在组件自身钩子之前调用。
解决思路
  • 策略模式,定义一个策略类(对象),根据不同key对应不同的处理函数
  • 在Vue类上挂载options属性用于存储“混入”的数据或函数
    • 多次使用mixin时会先将多个mixin对象合并成一个对象,Vue.options指向此对象
    • 在Vue初始化数据前将Vue.options与用户传递过来的配置对象进行合并,从而完成mixin操作
  • 对于生命周期的实现,如果理解了之前【vue2.0|思路篇|数据劫持】、【Vue|思路篇|编译ast】,不难理解,其实就是在特定的地方执行用户传递的回调;以beforeCreate为例:
    • 在Vue执行_init方法时,需要执行initState函数,在此函数调用前执行即可
    • 需注意,由于存在mixin的可能,回调必是采用数组的形式

第二阶段:watcher+dep实现依赖收集(对象)

核心问题
  • 如果我们使用类似watch、或者$watch之类的API呢?这就要求属性改变不止渲染视图,还需要执行其他函数,如何实现?
  • 一个属性对应一个dep很简单 ,在属性的define里形成一个闭包就好了;但dep怎么收集属性需要的watcher呢?
  • 如何避免存入多个相同的watcher?比如我取了多次相同的属性,自然会导致多次收集操作
解决思路
  • 一对多解耦合用发布订阅,引入Dep和Watcher的概念,Watcher是依赖,可以理解为函数;Dep则是承载每个属性对应依赖的容器,可以理解为数组;则有 属性 : dep : watcher== 1: 1 : n

    • 在属性被取值时订阅当前的函数 也就是Watcher,将之存入此属性对应的Dep实例中
    • 在属性取值时发布,执行当前属性对应的dep中的所有watcher
    • 注意对象和数组的依赖触发逻辑的不同,如前所言,数组本身的观测只是重写了方法
  • dep收集属性需要的watcher

    • 在我们取值的时候,触发get,那也就是说,我们只要在get中能拿到需要存的watcher,就可以存入了;
    • 怎么存呢?这就是我们的Watcher是一个对象而不是直接一个函数的原因,它内部并不是直接执行函数的,而是先将当前watcher(也就是自身)挂载到Dep类的target属性上
    • 在get时如果Dep.target存入,属性对应实例就将其存入自己的dep中
  • 每个watcher和dep都有自己的id,id自增,存入时如果id相同则不存入(借助Set数据结构对ID进行避免重复)

第三阶段:watcher+dep实现依赖收集(数组)

核心问题

数组由于数据劫持是特殊处理,采用装饰方法的手段,所以在依赖收集时也需要特殊处理

解决思路
  • 通过observe返回值获取到观察者对象,在观测者对象上挂载一个dep属性,这样就可以在这个dep上进行依赖收集
  • 在数组重写方法的逻辑中去通过__ob__.dep获取到收集到的依赖,并发布,从而实现数组的依赖收集

具体实现链接

vue2.0|实现篇|依赖收集