Vue数据双向绑定原理 ( Vue.version = 2.6.14 )

Vue数据双向绑定原理 ( Vue.version = 2.6.14 )

分析主要代码

1. 监测Object的变化

vue.commone.dev.js文件中,Vue在defineReactive$$1方法中监听对象的属性变化。

/**
 * defineReactive$$1
 */
function defineReactive$$1 (
  obj,
  key,
  val,监测
  customSetter,
  shallow
) {

  // 创建Dep实例
  const dep = new Dep();

  // 获取属性默认的 getter/setter ,防止重新定义getter/setter 的时候覆盖默认行为
  const descriptor = Object.getOwnPropertyDescriptor(obj, key);
  const getter = descriptor && descriptor.get;
  const setter = descriptor && descriptor.set;

  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key];
  }

  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      const value = getter ? getter.call(obj) : val;
      if (Dep.target) {

        /**
         * 1. 调用Dep.prototype.depend
         * 2. 在Dep.prototype.depend中调用Watcher.prototype.addDep
         * 3. Watcher.prototype.addDep中将Dep实例保存到Watcher实例的newDepIds、newDeps属性上,再将Watcher实例保存到Dep实例的subs属性上
         */
        dep.depend();
      }
      return value;
    },
    set: function(newVal) {
      const value = getter ? getter.call(obj) : val;

      // 设置新的值
      if (setter) {
        setter.call(obj, newVal);
      } else {
        val = newVal;
      }

      /**
       * 1. 调用Dep.prototype.notify
       * 2. 在Dep.prototype.notify中遍历Dep实例上的subs属性 ( getter中保存的Watcher实例 )
       * 3. 调用Watcher.prototype.update更新视图 ( 具体update中做了什么,下次单独分析 )
       */
      dep.notify(); 
    }
  })
}

整体流程:

① 通过observer,使用Object.definePropertydata的获取、变化能够被监测

② 获取data时,触发getter, 将依赖添加到watcher中,将wacher添加到依赖subs ( 订阅? )

③ 更新data时,触发setter,通知依赖遍历subs,并调用watcherupdate方法更新视图 ( 其实最终调用的是Vue实例_render方法 )

疑问:

watcher是在何时创建的?

initComputed阶段创建watcher,并且watcher的vm属性指向Vue实例,将watcher保存到Vue实例的_watchers

不足:

  • 向对象中添加或删除属性时,无法监测到变化
  • 无法监测数组的变化
2. 监测Array的变化

定义拦截器,在调用修改数组的方法时,触发拦截器。

const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);

// 能够改变数组自身的7个方法
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
];

// 遍历methodsToPatch并修改arrayMethods上对应名称的方法
methodsToPatch.forEach(method => {
  Object.defineProperty(
    arrayMethods,
    method,
    {
      value: function() {

        /**
         * clone参数
         */
        const args = [];
        let len = arguments.length;
        while (len--) {
          args[len] = arguments[len];
        }

        // 调用原生的方法
        const result = arrayProto[method].apply(this, args);

        /**
         * 调用 push/unshift/splice时若添加/更新 了新元素
         * 则调用observe观察该元素
         */
        const ob = this.__ob__;
        let inserted;
        switch (method) {
          case 'push':
          case 'unshift':
            inserted = args;
            break;
          case 'splice':
            inserted = args.slice(2);
            break;
          default:
            break;
        }
        if (inserted) {
          ob.observeArray(inserted);
        }

        // 通知依赖更新
        ob.dep.notify();

        return result;
      }
    }
  )
})

整体流程:

① 给Array.prototype上能改变数组本身的方法添加拦截器

② 在Observer的构造器中Array实例的__proto__指向arrayMethods

③ 调用这些方法时,通知依赖更新

不足:

  • 通过索引修改数组时,无法监测到数组的变化
3. Vue对以上不足的解决方案

增加了两个全局API:Vue.setVue.delete

posted @   刘狗蛋Alison  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示