Watcher和Dep建立关联

首先在 ​​defineReactive​​​ 中创建 ​​Dep​​​ 实例,与 ​​data.key​​​ 是一一对应的关系,然后再 ​​get​​​ 中 调用​​dep.addDep​​​ 进行依赖的收集,​​Dep.target​​​ 就是一个 ​​Watcher​​​。在 ​​set​​​ 中 调用 ​​dep.notify()​​​ 通知所有的 ​​Watchers​​ 更新视图。

function defineReactive(obj, key, val) {  ... ...  // 创建 Dep 实例 , 与 key 一一对应  const dep = new Dep()  // 通过该方法拦截数据  Object.defineProperty(obj, key, {    // 读取数据的时候会走这里    get() {      console.log('????????~ get:', key);      // 依赖收集 Dep.target 就是 一个Watcher      Dep.target && dep.addDep(Dep.target)      return val    },    // 更新数据的时候会走这里    set(newVal) {      // 只有当新值和旧值不同的时候 才会触发重新赋值操作      if (newVal !== val) {        console.log('????????~ set:', key);        // 如果 newVal 是个对象类型,再次做响应式处理。        if (typeof obj === 'object' && obj !== null) {          observe(newVal)        }        val = newVal        // 通知更新        dep.notify()      }    }  })}复制代码

代码实现 - 第四回合 事件和双向绑定

事件绑定

事件绑定也很好理解,首先判断节点的属性是不是以 ​​@​​​ 开头,然后拿到事件的类型,也就是例子中的 ​​click​​​, 再根据函数名找到 ​​methods​​ 中定义的函数体,最后添加事件监听就行了。

class Compile {  ... ... // 省略号的地方都没有发生改变  compile(el) {      // 判断节点属性是不是一个事件      if (this.isEvent(attrName)) {        // @click="onClick"        const dir = attrName.substring(1) // click        // 事件监听        this.eventHandler(node, exp, dir)      }  }  ... ...  // 判断节点是不是一个事件 也就是以@开头  isEvent(dir) {    return dir.indexOf("@") === 0  }  eventHandler(node, exp, dir) {    // 根据函数名字在配置项中获取函数体    const fn = this.$vm.$options.methods && this.$vm.$options.methods[exp]    // 添加事件监听    node.addEventListener(dir, fn.bind(this.$vm))  }  ... ...}复制代码

双向绑定

​my-model​​​ 其实也是一个指令,走的也是指令相关的处理逻辑,所以我们只需要添加一个 ​​model​​​ 指令和对应的 ​​modelUpdater​​ 处理函数就行了。

​my-model​​ 双向绑定其实就是 ​事件绑定​ 和修改 ​​value​​​ 的一个语法糖,本文以 ​​input​​ 为例,其它的表单元素绑定的事件会有不同,但是道理是一样的。

class Compile {  // my-model指令 my-model='xxx'  model(node, exp) {    // update 方法只完成赋值和更新    this.update(node, exp, 'model')    // 事件监听    node.addEventListener('input', e => {      // 将新的值赋值给 data.key 即可      this.$vm[exp] = e.target.value    })  }  modelUpdater(node, value) {    // 给表单元素赋值    node.value = value  }}复制代码

现在也可以更新一下模板编译的流程图啦~

【vue】数据响应式原理(5)_赋值

后语

【vue】数据响应式原理(5)_事件绑定_02

到这里一个简易版的 ​​Vue​​ 数据响应式就完成了,整套流程从头到尾都是自己手写的,还怕不懂原理么?

​​