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

相关推荐
新手小新几秒前
C++游戏开发(2)
开发语言·前端·c++
我不吃饼干26 分钟前
【TypeScript】三分钟让 Trae、Cursor 用上你自己的 MCP
前端·typescript·trae
飞翔的佩奇1 小时前
基于SpringBoot+MyBatis+MySQL+VUE实现的经方药食两用服务平台管理系统(附源码+数据库+毕业论文+部署教程+配套软件)
数据库·vue.js·spring boot·mysql·毕业设计·mybatis·经方药食两用平台
小杨同学yx1 小时前
前端三剑客之Css---day3
前端·css
星月心城2 小时前
Promise之什么是promise?(01)
javascript
二川bro3 小时前
第二篇:Three.js核心三要素:场景、相机、渲染器
开发语言·javascript·数码相机
Mintopia3 小时前
🧱 用三维点亮前端宇宙:构建你自己的 Three.js 组件库
前端·javascript·three.js
故事与九3 小时前
vue3使用vue-pdf-embed实现前端PDF在线预览
前端·vue.js·pdf
小西↬3 小时前
vite+vue3+websocket处理音频流发送到后端
javascript·websocket·音视频
Mintopia4 小时前
🚀 顶点-面碰撞检测之诗:用牛顿法追寻命运的交点
前端·javascript·计算机图形学