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

相关推荐
江城开朗的豌豆1 分钟前
事件绑定三件套:从onclick到addEventListener的进化史
前端·javascript·面试
小满zs2 小时前
Zustand 第四章(中间件)
前端·react.js
GalaxyPokemon3 小时前
LeetCode - 2. 两数相加
java·前端·javascript·算法·leetcode·职场和发展
dualven_in_csdn6 小时前
搞了两天的win7批处理脚本问题
java·linux·前端
你的人类朋友6 小时前
✍️【Node.js程序员】的数据库【索引优化】指南
前端·javascript·后端
小超爱编程7 小时前
纯前端做图片压缩
开发语言·前端·javascript
银色的白7 小时前
工作记录:人物对话功能开发与集成
vue.js·学习·前端框架
应巅7 小时前
echarts 数据大屏(无UI设计 极简洁版)
前端·ui·echarts
萌萌哒草头将军8 小时前
🚀🚀🚀什么?浏览器也能修改项目源文件了?Chrome 团队开源的超强 Vite 插件!🚀🚀🚀
vue.js·react.js·vite
Jimmy8 小时前
CSS 实现描边文字效果
前端·css·html