vue2响应式原理

主要使用Object的defineProperty为每个属性添加get和set操作,在get和set中添加拦截或者是数据渲染

简单实现//监听

复制代码
function observe(target) {
  if (typeof target !== "object" || target === "null") {
    return;
  }
  //遍历添加get set 属性
  for (let key in target) {
    defObserve(target, key, target[key]);
  }
}
function defObserve(target, key, value) {
  debugger;
  observe(value); //递归遍历
  Object.defineProperty(target, key, {
    get() {
      console.log("获取数据");
      return value;
    },
    set(newValue) {
      if (newValue !== value) {
        console.log("设置数据");
        renser();
        value = newValue;
      }
    },
  });
}
//渲染数据
function renser() {
  console.log("渲染数据");
}
let obj = {
  name: "zhangsan",
};
  observe(obj);
  obj.name.a = "lisi";
  console.log(obj.name.a);
//获取数据;
// 设置数据;
// 渲染数据;
// 获取数据;
// 获取数据;
// lisi;
 
复制代码

因为obj.name时也触发一次get,所以获取数据第一次就输出了,至此对象的数据响应实现了,还有数组需要特殊处理。

添加数组响应式,就是对原数组进行拦截

复制代码
const arrayMap = [
  "push",
  "pop",
  "shift",
  "unshift",
  "slice",
  "splice",
  "reverse",
]; //还有其它方法此处不定义
const oldArrayPropoty = Array.prototype;
const newArrayPropoty = Object.create(oldArrayPropoty);
arrayMap.forEach((method) => {
  newArrayPropoty[method] = function() {
    //添加拦截操作
    render();
    //依旧执行数组原有方法
    oldArrayPropoty[method].call(this, ...arguments);
  };
});

//监听
function observe(target) {
  //判断是否是对象
  if (typeof target !== "object" || target === "null") {
    return;
  }
  //判断是否是数组
  if (Array.isArray(target)) {
    //绑定__proto__为newArrayPropoty
    // target.__proto__ = newArrayPropoty;
    //同理
    Object.setPrototypeOf(target, newArrayPropoty);
  }
  //遍历添加get set 属性
  for (let key in target) {
    //若是数组,会为每个方法添加get和set
    defObserve(target, key, target[key]);
  }
}
function defObserve(target, key, value) {
  observe(value); //递归遍历
  Object.defineProperty(target, key, {
    get() {
      console.log("获取数据");
      return value;
    },
    set(newValue) {
      if (newValue !== value) {
        console.log("设置数据");
        render();
        value = newValue;
      }
    },
  });
}
//渲染数据
function render() {
  console.log("渲染数据");
}
// let obj = {
//   name: { a: "张三" },
// };
// observe(obj);
// obj.name.a = "lisi";
// console.log(obj.name.a);

let arr = [1, 2, 3];
observe(arr);
arr.push(4);
console.log(arr);
复制代码

输出打印如下

 

 

 

 可以看到新增元素4操作数组时也触发了拦截操作,但是这里还有一个问题是当对象被重新赋值时是不会触发操作的,如obj.name={a:'王五'},此时这个a没有get和set属性,需要在set中增加一步

复制代码
set(newValue) {
      if (newValue !== value) {
        console.log("设置数据");
        //重新监测
        observe(newValue);
        render();
        value = newValue;
      }
    },    
复制代码

 

posted @   lijun12138  阅读(118)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示