Vue2到Vue3 响应式实现为什么由Object.defineProperty换成Proxy

响应式设计是Vue中比较核心的设计,Vue2升级到Vue3之后响应式的核心实现由Object.defineProperty换成Proxy。这是为什么呢?

Object.defineProperty 实现响应式的问题

Vue2 的官方文档有这样一段话:

这是什么意思呢!我们来看个小案例

js 复制代码
<template>
  <div id="app">
    <p v-for="(val, key) in info" :key="key">{{ val }}</p>
    <button @click="addGender">增加性别</button>
    <button @click="deleteGender">删除地址信息</button>
    <p v-for="(val, index) in list" :key="index">{{ val }}</p>
    <button @click="updateList">更新手机列表</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      info: {
        name: "hello",
        address: "深圳",
      },
      list: ["小米", "华为"],
    };
  },
  methods: {
    addGender() {
      this.info.gender = "男";
      console.log(this.info, "添加之后");
    },
    deleteGender() {
      delete this.info.address;
      console.log(this.info, "删除之后");
    },
    updateList() {
      this.list[2] = "oppo";
      this.list[0] = "vivo";
      console.log(this.list, "添加和更新之后");
    },
  },
};
</script>

<style lang="scss"></style>

运行效果:

点击下增加性别

可以看到,控制台打印的数据已经更新了,有了性别属性,但是视图并没有更新。

现在我们来点击下删除地址信息按钮:

可以看到,控制台的数据已经删除了地址信息,但是视图并没有更新,还是显示了地址信息。

点击下更新手机列表:

可以看到,在控制台的数据里面,新增了一个oppo, 并且将手机列表的第一个改成了vivo, 但是视图并没有同步更新。

通过这个小案例,相信你已经看到了在Vue2中响应式存在的问题,这就是Object.defineProperty的缺陷,为什么Object.defineProperty会有这个缺陷呢? 因为Object.defineProperty 是对定义的属性进行拦截的。看看Object.defineProperty 的使用就知道了:

js 复制代码
const data = {
  name: "hello",
  address: "深圳",
};

const newData = { ...data };
Object.defineProperty(newData, "name", {
  get() {
    console.log("getter");
    return data.name;
  },
  set(key, newVal) {
    console.log("setter", key);
    data[key] = newVal;
  },
});

console.log(newData.name);
newData.name = "world";

console.log(newData.address);
newData.address = "上海";

在这段代码中,我们只对name 在Object.defineProperty 中进行了定义,所以只有name 属性读取和设置才会触发getter 和setter, address 的读取和设置不会触发getter和setter

Vue3 响应式的解决方案

为了更好的解决这些问题,于是在Vue3 中将响应式的实现改成了Proxy。 在Vue3 是否是真的解决了这些问题了呢, 我们将之前的的小案例改成Vue3来写。

js 复制代码
<template>
  <div id="app">
    <p v-for="(val, key) in info" :key="key">{{ val }}</p>
    <button @click="addGender">增加性别</button>
    <button @click="deleteGender">删除地址信息</button>
    <p v-for="(val, index) in list" :key="index">{{ val }}</p>
    <button @click="updateList">更新手机列表</button>
  </div>
</template>

<script setup>
import { reactive, toRefs } from "vue";

const data = reactive({
  info: {
    name: "hello",
    address: "深圳",
  },
  list: ["小米", "华为"],
});

const addGender = () => {
  data.info.gender = "男";
  console.log(data.info, "添加之后");
};

const deleteGender = () => {
  delete data.info.address;
  console.log(data.info);
};

const updateList = () => {
  data.list[2] = "oppo";
  data.list[0] = "vivo";
  console.log(data.list);
};

const { info, list } = toRefs(data);
</script>

<style lang="scss"></style>

点击增加性别:

可以看到控制台的数据和视图都同步新增了性别字段。

点击删除地址信息:

可以看到控制台的数据和视图都同步删除了地址信息

点击更新手机列表:

可以看到控制台数据和视图都将列表的第一条数据改成了vivo, 并且新增了一条数据oppo。这就是Proxy 的魅力。为什么Proxy 能解决这个问题呢。因为Proxy代理的是整个对象,而不是一个属性。来看看Proxy 的使用就明白了。

js 复制代码
const data = {
  name: "hello",
  address: "深圳",
};

const proxy = new Proxy(data, {
  get(target, key) {
    console.log("getter", key);
    return Reflect.get(target, key);
  },
  set(target, key, newVal) {
    console.log("setter", key);
    Reflect.set(target, key, newVal);
    return true;
  },
});
console.log(proxy.name);
proxy.name = "world";
console.log(proxy.address);
proxy.address = "上海";

proxy.gender = "男";
console.log(proxy.gender);

可以看到无论是之前定义的属性还是新增的属性都能触发gettter 和setter, 因为Proxy代理的是整个对象而不是某个属性。

总结

Vue2升级到Vue3将Object.defineProperty换成Proxy的主要原因是解决一些历史问题,比如对象新增属性,删除属性不能更新视图问题。还有数组通过下标新增和修改数据无效等问题

相关推荐
Ticnix13 分钟前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人16 分钟前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl20 分钟前
OpenClaw 深度技术解析
前端
崔庆才丨静觅23 分钟前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人31 分钟前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼35 分钟前
shadcn/ui,给你一个真正可控的UI组件库
前端
布列瑟农的星空38 分钟前
前端都能看懂的Rust入门教程(三)——控制流语句
前端·后端·rust
Mr Xu_43 分钟前
Vue 3 中计算属性的最佳实践:提升可读性、可维护性与性能
前端·javascript
jerrywus1 小时前
我写了个 Claude Code Skill,再也不用手动切图传 COS 了
前端·agent·claude
玖月晴空1 小时前
探索关于Spec 和Skills 的一些实战运用-Kiro篇
前端·aigc·代码规范