vue2 对象、数组属性新增删除失去响应式($set、$delete) 和 vue3解构赋值原始类型失去响应式

1,288 阅读2分钟

vue2失去响应式

原理

Object.defineProperty无法监控到数组下标和长度的变化,因为添加或删除的对象的属性没 有 在 初 始 化 进 行 响 应 式 处 理 ,Object.defineProperty检测不到

<template>
  <div>
    <h2>{{ obj }}</h2>
    <h2>{{ arr }}</h2>
  </div>
</template>
<script>
export default {
  data() {
    return {
      obj: {
        a: "Hello World!",
      },
      arr: [1, 2, 3],
    };
  },
  mounted() {
    this.changeObj();
    this.changeArr();
  },
  methods: {
    changeObj() {
      this.obj.b = "新增的属性b";
      console.log(this.obj);
    },
    changeArr() {
      this.arr[4] = 7;
      this.arr.length = 5;
      console.log(this.arr);
    },
  },
};
</script>

image.png

this.$set() this.$delete解决

this.$set(target, key, value)

this.$delete(target, key)

 changeObj() {
     this.$set(this.obj, b, "新增的属性b")
     console.log(this.obj);
 },
 changeArr() {
     this.$set(this.arr, 3, 7)
     console.log(this.arr);
     this.$delete(this.arr, 0)
     console.log(this.arr);
 },

vue3解构赋值原始类型失去响应式

原理

解构赋值原始类型失去响应式

解构赋值引用类型失去响应式

原始类型的赋值相当于按值传递, 引用类型的值就相当于按引用传递

原始类型

// 假设a是个响应式对象
const a={ b:1}
// c 此时就是一个值跟当前的a 已经不沾边了
const c=a.b
 
// 你直接访问c就相当于直接访问这个值 也就绕过了 a 对象的get ,也就像原文中说的失去响应式

引用类型

// 假设a是个响应式对象
const a={ b:{c:3}}
// 当你访问a.b的时候就已经重新初始化响应式了,此时的c就已经是个代理的对象
const c=a.b
 
// 你直接访问c就相当于访问一个响应式对象,所以并不会失去响应式

禁止直接赋值reactive响应式对象

由于解构赋值的问题, 他直接禁止了reactive的解构赋值

image.png

// 当reactive 之后返回一个代理对象的地址被vue 存起来,
// 用一个不恰当的比喻来说,就是这个地址具备响应式的能力
const vue = reactive({ a: 1 })
 
//  而当你对于vue重新赋值的时候不是将新的对象赋值给那个地址,而是将vue 换了个新地址
// 而此时新地址不具备响应式,可不就失去响应式了吗
vue = { b: 2 }

通过storeToRefs解构pinia属性保持响应式

import { storeToRefs } from "pinia";

const system = useSystem();
const { isNewProject, isOpenEdit } = storeToRefs(system)

el-select 的option 绑定pinia数据失去响应

// 失去响应写法
  <el-select
                v-model="selectedValue"
                clearable
                popper-class="select_default"
                class="my-select"
                placeholder="全部"
                size="default"
                @change="selectChange"
            >
                <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
            </el-select>
            
            
import { OrderStore } from '@/store';
const orderStore = OrderStore(); // 实例化store

const option = orderStore.orderType

// 正确带响应写法1
  <el-select
                v-model="selectedValue"
                clearable
                popper-class="select_default"
                class="my-select"
                placeholder="全部"
                size="default"
                @change="selectChange"
            >
                <el-option v-for="item in orderStore.orderType" :key="item.value" :label="item.label" :value="item.value" />
            </el-select>
            
import { OrderStore } from '@/store';
const orderStore = OrderStore(); // 实例化store


// 正确带响应写法2
  <el-select
                v-model="selectedValue"
                clearable
                popper-class="select_default"
                class="my-select"
                placeholder="全部"
                size="default"
                @change="selectChange"
            >
                <el-option v-for="item in orderType" :key="item.value" :label="item.label" :value="item.value" />
            </el-select>

import { storeToRefs } from 'pinia';
import { OrderStore } from '@/store';
const orderStore = OrderStore(); // 实例化store
const { orderType } = storeToRefs(orderStore);