基于proxy和Reflect简单实现vue3常用方法的数据劫持部分功能

本文介绍了如何在Vue3中模拟实现部分核心功能,如reactive、shallowReactive、readonly和ref,以及相应的辅助函数,展示了它们在数据劫持和读写权限控制中的应用。
摘要由CSDN通过智能技术生成

经过一段时间的学习,自己简单模拟实现了vue3中部分方法的功能

// 定义reactiveHandler处理对象
const reactiveHandler = {
  // 获取属性值
  get(target, prop) {
    if (prop === "_is_reactive") return true;
    console.log("拦截到了读取属性值", prop);
    return Reflect.get(target, prop);
  },
  // 修改属性值或者添加属性
  set(target, prop, value) {
    console.log("拦截到了修改属性", prop, value);
    return Reflect.set(target, prop, value);
  },
  // 删除属性
  deleteProperty(target, prop) {
    console.log("拦截到了删除属性", prop);
    return Reflect.deleteProperty(target, prop);
  },
};

// shallowReactive和reactive
function shallowReactive(target) {
  // 判断传入的参数是不是object类型
  if (target && typeof target === "object") {
    return new Proxy(target, reactiveHandler);
  } else {
    // 如果是基本类型,直接返回
    return target;
  }
}

function reactive(target) {
  // 判断传入的参数是不是object类型
  if (target && typeof target === "object") {
    // 判断是对象还是数组,并且对数据或对象中的数据进行reactive的递递归处理
    if (Array.isArray(target)) {
      target.forEach((item, index) => {
        target[index] = reactive(item);
      });
    } else {
      Object.keys(target).forEach((key) => {
        target[key] = reactive(target[key]);
      });
    }
    return new Proxy(target, reactiveHandler);
  } else {
    // 如果是基本类型,直接返回
    return target;
  }
}

const readonlyHandler = {
  get(target, prop) {
    if (prop === "_is_readonly") return true;
    console.log("拦截到了读取数据", prop);
    return Reflect.get(target, prop);
  },
  set(target, prop, value) {
    console.warn("不能修改或添加数据,只能读取数据!");
    return true;
  },
  deleteProperty(target, prop) {
    console.warn("不能删除数据,只能读取数据!");
    return true;
  },
};

// shallowReadonly和readonly
function shallowReadonly(target) {
  if (target && typeof target === "object") {
    return new Proxy(target, readonlyHandler);
  } else {
    return target;
  }
}

function readonly(target) {
  if (target && typeof target === "object") {
    if (Array.isArray(target)) {
      target.forEach((item, index) => {
        target[index] = readonly(item);
      });
    } else {
      Object.keys(target).forEach((key) => {
        target[key] = readonly(target[key]);
      });
    }
    return new Proxy(target, readonlyHandler);
  } else {
    return target;
  }
}

// shallowRef和ref
function shallowRef(target) {
  return {
    _is_ref: true, //标识当前对象是ref对象
    _value: target,
    get value() {
      console.log("拦截到了读取数据");
      return this._value;
    },
    set value(val) {
      console.log("拦截到了修改数据");
      return (this._value = val);
    },
  };
}

function ref(target) {
  if (target && typeof target === "object") {
    target = reactive(target);
  }
  return {
    _is_ref: true, //标识当前对象是ref对象
    _value: target,
    get value() {
      console.log("拦截到了读取数据");
      return this._value;
    },
    set value(val) {
      console.log("拦截到了修改数据", val);
      this._value = val;
    },
  };
}

// isRef isReactive isReadonly isProxy
function isRef(obj) {
  return obj && obj._is_ref ? obj && obj._is_ref : false;
}

function isReactive(obj) {
  return obj && obj._is_reactive ? obj && obj._is_reactive : false;
}

function isReadonly(obj) {
  return obj && obj._is_readonly ? obj && obj._is_readonly : false;
}

function isProxy(obj) {
  return isReactive(obj) || isReadonly(obj);
}

下面为测试代码

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>手搓vue3中常用api,只是实现劫持操作,页面刷新没有</title>
</head>

<body>

</body>
<script src="./index.js"></script>
<script type="text/javascript">
    console.log("-------------------测试shallowReactive--------------------");
    const proxyData1 = shallowReactive({
        name: "张三",
        car: {
            color: "red",
            type: ["奔驰", "宝马", "劳斯莱斯"]
        }
    })
    proxyData1.name += "~"  // 拦截到了读和写
    proxyData1.car.color = "blue"  // 只拦截到了读
    console.log(proxyData1.car.color);  // blue
    delete proxyData1.name  //拦截到了删除数据
    delete proxyData1.car.color  // 只拦截到了读取数据,删除数据拦截不到
    console.log(proxyData1.car); // {type: ["奔驰", "宝马", "劳斯莱斯"]}

    console.log("-------------------测试reactive--------------------");
    const proxyData2 = reactive({
        name: "张三",
        car: {
            color: "red",
            type: ["奔驰", "宝马", "劳斯莱斯"]
        }
    })
    proxyData2.name += "!"  // 拦截到了读和写
    proxyData2.car.type[0] = "特斯拉"  // 拦截到了读和写
    delete proxyData2.car.color  // 拦截到了读取和删除

    console.log("-------------------测试shallowReadonly--------------------");
    const proxyData3 = shallowReadonly({
        name: "小明",
        work: {
            address: "北京",
            money: 100000000
        }
    })
    proxyData3.name += "@"  // 只能读取数据
    console.log(proxyData3.name);  // 小明
    proxyData3.work.address = "保定"  //拦截到了读取数据,修改数据没有拦截到 
    console.log(proxyData3.work.address);  // 保定
    delete proxyData3.name  // 只能读取数据,不能删除数据
    delete proxyData3.work.money  //拦截到了读取数据,删除数据没有拦截到 
    console.log(proxyData3.work); // {address: "北京"}

    console.log("-------------------测试readonly--------------------");
    const proxyData4 = readonly({
        name: "小明",
        work: {
            address: "北京",
            money: 100000000
        }
    })
    proxyData4.name += "@"  // 只能读取数据
    console.log(proxyData4.name);  // 小明
    proxyData4.work.address = "保定"  //只能读取数据,不能修改数据
    console.log(proxyData4.work.address);  // 北京
    delete proxyData4.name  // 只能读取数据,不能删除数据
    delete proxyData4.work.money  //只能读取数据
    console.log(proxyData4.work); // {address: '北京', money: 100000000}

    console.log("-------------------测试shallowRef--------------------");
    const proxyData5 = shallowRef({
        name: "小强",
        car: {
            color: "red"
        }
    })
    console.log(proxyData5.value);  // 拦截到了读取数据
    proxyData5.value = "!"  // 拦截到了修改数据
    proxyData5.value.name = "小six"  // 拦截不到修改数据

    console.log("-------------------测试ref--------------------");
    const proxyData6 = ref({
        name: "老八",
        car: {
            type: "宝马"
        }
    })
    console.log(proxyData6.value.car); // 拦截到了读取数据
    proxyData6.value.car.type = "奔驰" // 拦截到了读取和修改数据

    console.log("-------------------测试isRef--------------------");
    console.log(isRef(ref({}))); // true
    console.log(isRef(readonly({}))); //false
    console.log(isRef(shallowReactive({}))); //false
    console.log(isRef(reactive({}))); //false
    console.log("-------------------测试isReactive--------------------");
    console.log(isReactive(reactive({}))); //true
    console.log(isReactive(readonly({})));  //false
    console.log(isReactive(shallowReadonly({}))); //false
    console.log(isReactive(ref({}))); //false
    console.log(isReactive(shallowReactive({})));  //true
    console.log("-------------------测试isReadonly--------------------");
    console.log(isReadonly(readonly({})));  //true
    console.log(isReadonly(shallowReadonly({})));  //true
    console.log(isReadonly(reactive({})));  // false
    console.log("-------------------测试isProxy--------------------");
    console.log(isProxy(readonly({})));  //true 
    console.log(isProxy(reactive({})));  //true
    console.log(isProxy(ref({})));  //false
</script>

</html>

经过测试达到预期效果

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 2.x 中,使用的是 Object.defineProperty() 进行数据劫持,但是这种方法有一些缺点,比如无法监听数组的变化,还需要递归遍历对象属性进行绑定。 在 Vue 3.x 中,采用了 Proxy 对象进行数据劫持Proxy 对象可以拦截对象的读取、赋值、删除等操作,比 Object.defineProperty() 更加强大。 下面我们来看一下 Proxy 对象在 Vue 3.x 中的应用。 首先,我们创建一个 reactive 函数,它接收一个普通对象,返回一个响应式的对象。 ```javascript function reactive(obj) { return new Proxy(obj, { get(target, key) { console.log('get', key) return Reflect.get(target, key) }, set(target, key, value) { console.log('set', key, value) return Reflect.set(target, key, value) }, deleteProperty(target, key) { console.log('delete', key) return Reflect.deleteProperty(target, key) } }) } ``` 上面的代码中,我们创建了一个 Proxy 对象,其中 get、set、deleteProperty 方法分别对应了读取、赋值、删除操作。在这些方法中,我们可以打印出操作的类型以及操作的属性名,方便我们调试。 接下来,我们创建一个普通对象,并使用 reactive 函数将其转为响应式对象。 ```javascript const obj = reactive({ name: 'Tom', age: 18, hobbies: ['reading', 'music'] }) ``` 现在,我们可以通过访问 obj 的属性来触发 get、set、deleteProperty 方法。 ```javascript console.log(obj.name) // 输出 get name, Tom obj.age = 20 // 输出 set age 20 delete obj.hobbies // 输出 delete hobbies ``` 可以看到,我们成功地拦截了对象的读取、赋值、删除操作,并打印出了相关信息。 除了上述常用方法外,Proxy 还有许多其他的拦截方法,如 has、apply、construct 等,可以满足更多的需求。 总的来说,Vue 3.x 中采用 Proxy 对象进行数据劫持,使得响应式系统更加强大、灵活,提高了开发效率,是一个非常不错的改进。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值