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的主要原因是解决一些历史问题,比如对象新增属性,删除属性不能更新视图问题。还有数组通过下标新增和修改数据无效等问题

相关推荐
2501_94452554几秒前
Flutter for OpenHarmony 个人理财管理App实战 - 支出分析页面
android·开发语言·前端·javascript·flutter
jin12332239 分钟前
React Native鸿蒙跨平台完成剧本杀组队详情页面,可以复用桌游、团建、赛事等各类组队详情页开发
javascript·react native·react.js·ecmascript·harmonyos
李白你好41 分钟前
Burp Suite插件用于自动检测Web应用程序中的未授权访问漏洞
前端
经年未远2 小时前
vue3中实现耳机和扬声器切换方案
javascript·学习·vue
刘一说2 小时前
Vue 组件不必要的重新渲染问题解析:为什么子组件总在“无故”刷新?
前端·javascript·vue.js
可触的未来,发芽的智生2 小时前
狂想:为AGI代称造字ta,《第三类智慧存在,神的赐名》
javascript·人工智能·python·神经网络·程序人生
徐同保3 小时前
React useRef 完全指南:在异步回调中访问最新的 props/state引言
前端·javascript·react.js
fanruitian3 小时前
uniapp 创建项目
javascript·vue.js·uni-app
刘一说3 小时前
Vue 导航守卫未生效问题解析:为什么路由守卫不执行或逻辑失效?
前端·javascript·vue.js
一周七喜h4 小时前
在Vue3和TypeScripts中使用pinia
前端·javascript·vue.js