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

相关推荐
货拉拉技术7 分钟前
LLM 驱动前端创新:AI 赋能营销合规实践
前端·程序员·llm
MariaH13 分钟前
深入了解vertical-align
前端
草巾冒小子27 分钟前
element-ui图片查看器
前端·vue.js·ui
光影少年44 分钟前
vue3为什么不需要时间切片
前端·vue.js
Json_1 小时前
Vue 初识Hello word
前端·vue.js·深度学习
工藤孤独1 小时前
网页字体终极指南:从选择到优化加载体验
前端·css
一枚前端小姐姐1 小时前
git merge - 本地解决无权限dev分支的合并冲突
前端·git
Shi_haoliu1 小时前
各种网址整理-vue,前端,linux,ai前端开发,各种开发能用到的网址和一些有用的博客
linux·前端·javascript·vue.js·nginx·前端框架·pdf
东望1 小时前
从基础用法到源码实现:手写 Promise 的完整指南
javascript·promise