vue2响应式原理

原理

Vue 会遍历data 选项所有的 property,并使用 Object.defineProperty 把这些 property 全部转为getter/setter,在 property 被访问和修改时通知变更,以实现响应式。

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

总结:

*对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。
* 数组类型:通过重写数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。

存在问题(部分可以通过Vue.set解决):
* 对象新增属性、删除属性, 界面不会更新。
* 直接通过下标修改数组, 界面不会自动更新。

实现

1.针对一般类型和对象类型

  1. 把data选项中的所有property转为getter/setter以实现响应式
  2. 如果property也是对象,使用递归方式为其添加响应式
  3. setter操作的新值为对象时,使用observe方法为其添加响应式
  4. 新增属性使用defineReactive方法添加响应式
  5. 使用Object.assign为已有对象添加多个property时,需要为已有对象重新赋值
// 判断是否object
function isObject(obj) {
  return Object.prototype.toString.call(obj) === '[object Object]';
};
// 1.为对象增加响应式
function observe(obj) {
  if(isObject(obj)) {
    for(let key in obj) {
      defineReactive(obj, key, obj[key]);
      // 2.递归式为深层对象添加响应式
      observe(obj[key]);
    };
  }
};
// 实现为对象每个属性增加响应性
function defineReactive(obj, key, value) {
  // 这里的value使用了闭包原理。由于get和set函数仍然在使用value变量,因此他并不会被销毁
  Object.defineProperty(obj, key, {
    get() {
      console.log(`${key}取值: `, value);
      return value;
    },
    set(newValue) {
      if(value === newValue) return;
      // 3.如果新值是对象,仍然为其添加响应式
      observe(newValue);
      console.log(`${key}存值:`, newValue);
      value = newValue;
    }
  });
};

const data = { 
  mode: 'normal', 
  option: {
    name: '123',
    value: 'x',
  } 
};
observe(data);

// 4.为新增属性增加响应性,vue提供了Vue.set,我们只需调用defineReactive函数就能达到目的
defineReactive(data.option, 'mode', 'normal');

// 5.如果要用Object.assign方法为已有对象添加一个或多个新property,这样的property没有响应式,所以需要为已有对象重新赋值
const option2 = {
  name: '789',
  value: 'xxx',
};
data.option = Object.assign({}, data.option, { option2 } );

2.针对数组

重写数组的一系列方法

// 复制数组原型对象
const arrProperty = Object.create(Array.prototype);
// 重写部分数组原型方法
const methods = ['pop', 'push', 'shift', 'unshift', 'splice'];
for(let i of methods) {
  arrProperty[i] = function() {
    console.log(`调用了数组${i}方法`);
    Array.prototype[i].call(this, ...arguments);
  };
}

// 为对象增加响应式
function observe(obj) {
  // 如果是数组则修改原型
  if(Array.isArray(obj)) obj.__proto__ = arrProperty;
  if(isObject(obj)) {
    for(let key in obj) {
      defineReactive(obj, key, obj[key]);
      // 递归式为深层对象添加响应式
      observe(obj[key]);
    };
  }
};
  • 12
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值