1. 创建vue构造函数
    2. 创建观察者observer
    3. 创建compiler
    4. 创建watcher
    5. 创建Dep

    每个构造器都是有类图的,类图很清晰的说明这个构造器中含有的变量和方法

    class vue

    1. 通过属性保存选项
    2. 把data中的成员设置getter,setter,注入到vue实例中

      1. // 因为这里写了不是return的形式,所以需要注入到vue实例中。
      2. data: {
      3. msg: 'Hello Vue',
      4. count: 100,
      5. person: { name: 'zs' }
      6. }
    3. 调用observer,监听数据的变化

    4. 调用compiler,解析插值表达式和文本节点

    class observer

    • 使得data中的成员拥有getter,setter方法
    • 调用walk方法处理嵌套的对象,在walk方法中判断入参是对象还是其他类型(没有在外面判断),这个值得我学习。
    • defineReactive方法中,在getter中收集对当前属性的依赖,在setter中触发watcher实例的update方法。
    • 需要创建一个Dep的实例,开始收集依赖。
    • Dep.targe && dep.addSub(Dep.target)很重要,和洋葱模型有点类似,值得学习,在watcher的constructor中会设置Dep.target的值为当前watcher对象

    class compiler

    • 使用childNodes获取组件的子节点(第一级的子节点),遍历,处理文本节(插值表达式属于文本节点)点和元素节点(带有指令的节点)
    • 编译文本节点,使用正则表达式匹配属性,并从this.vm中拿到属性值,替换到节点中,这是节点的第一渲染,然后调用watcher实现此属性值的相应式,使得属性值在更新的时候,调用watcher构造函数中的cb函数。
    • 编译元素节点,因为有格式相同的不同指令,所以我们的处理方法和处理文本节点的不同。利用名称的规律创建了update函数,调用不同指令的处理函数,textUpdater,modelUpdater。在这2个函数中更新节点的值,这是节点的第一渲染,然后调用watcher实现此属性值的相应式,使得属性值在更新的时候,调用watcher构造函数中的cb函数(这个点和文本节点的处理方式相同)。
    • update方法的写法很巧妙,省去了if-else的写法,需要记住,并在平时的开发中使用哦!
    • 至此,我们就实现了,使用vm.msg = 1的方法修改值后会更新到试图!但这只是这是双向中的一向哦!
    • modelUpdater中的 双向绑定 实现了修改输入值更新data中数据,它是双向中的一向!
    • update方法中注意updateFn调用时,this的指向,this需要指向compiler函数,因为这里不能用this.updateFn调用,所以用call实现this的修正!

      1. update (node, key, attrName) {
      2. let updateFn = this[attrName + 'Updater']
      3. updateFn && updateFn.call(this, node, this.vm[key], key)
      4. }
      5. // 处理 v-text 指令
      6. textUpdater (node, value, key) {
      7. node.textContent = value
      8. new Watcher(this.vm, key, (newValue) => {
      9. node.textContent = newValue
      10. })
      11. }
      12. // v-model
      13. modelUpdater (node, value, key) {
      14. node.value = value
      15. new Watcher(this.vm, key, (newValue) => {
      16. node.value = newValue
      17. })
      18. // 双向绑定
      19. node.addEventListener('input', () => {
      20. this.vm[key] = node.value
      21. })
      22. }
    • 我们的判断元素类型的语句都写成了小小的函数,需要记住这种写法,并运用到平时的工作中哦!

      1. // 判断元素属性是否是指令
      2. isDirective (attrName) {
      3. return attrName.startsWith('v-')
      4. }
      5. // 判断节点是否是文本节点
      6. isTextNode (node) {
      7. return node.nodeType === 3
      8. }
      9. // 判断节点是否是元素节点
      10. isElementNode (node) {
      11. return node.nodeType === 1
      12. }

      class Watcher

    • watcher的实现比compiler更加简单

    • Dep.target = this 是为 class observer 中definReactive中的getter 中的 Dep.targe && dep.addSub(Dep.target) 作准备,因为 this.oldValue = vm[key] 会调用key属性的getter方法!接着,需要清空 Dep.target, 因为不能重复注册同一个watcher。
    • watcher自己需要提供一个update方法,供Dep在更新依赖的时候调用。

      class Dep

    • 它也比较短小

    • 它有个subs用来保存它的所有兼听观察者watcher
    • 还有个notify方法用来在自己变化的时候调用watcher者们的update函数啦

    题外话:
    发布订阅模式和观察者模式不同,观察者模式是发布订阅模式的高阶版本。
    发布订阅模式有 事件中心 ,观察者模式没有