Vue3整合Pinia,懂行的大佬进来帮忙看下,为什么 重新在将store的属性设置为 reactive({})不生效?

作者站长头像
站长
· 阅读数 18

本来是向搞个在线项目 方便大家大家理解,但是试了几个云ide平台都太卡了,不好用就没搞。

问题

需求场景是这样的 有一个pinia Store(studentStore),这个store内部定义了一个let address = reactive({}) . 然后 有一个方法 resetAddress 方法会重新将 address变量指向另外一个新的响应式代理对象。

export const useStudentStore = defineStore('student', () => {

    //只会被执行一次
    console.log("studentStore的defineStore函数执行")
  
    const name = ref("zhangsan")
    let address = reactive({})
    let studentInfo = reactive({})

    const changeCity = function (city) {
        address.city = city
    }
    const resetAddress = function () {
        //注意这里将 address 指向一个新的响应式代理对象
        address = reactive({})
    }
    const resetStudentInfo = function () {
        studentInfo = reactive({})
    }

    return {
        name,
        address,
        studentInfo,
        resetStudentInfo,
        changeCity,
        resetAddress,
    }
})

在组件A中 执行 useStudentStore 函数得到studentStore. 然后 在组件A中显示studentStore的address属性。

组件A中有一个按钮,这个按钮随机生成 字符串1 和字符串2, 他们分别作为 属性名和属性值 被添加到address身上。 这个按钮还会生成一个随机数字,数字大于17的时候就会 调用resetAddress方法。

我们看到的效果就是 点击按钮---》addres新增了一个属性---》再次点击---》再次新增一个属性---》突然某次生成的随机数大于17,执行resetAddress方法。

resetAddres方法内部会将 defineStoree函数的第二个参数函数中的 address 指向一个新的响应式代理对象, 这个新的响应式代理对象显然没有任何属性。 对于组件A 他模板中渲染的仍然还是之前的对象。 这是为什么

<template>
  <div>
    <div>这是TestPinia.vue</div>
    {{ studentStore.name }}
    <div>address: {{ studentStore.address }}</div>
    <div>studentInfo: {{ studentStore.studentInfo }}</div>

    <el-button @click="handleClick">点击</el-button>
  </div>
</template>

<script setup>

import {useStudentStore} from '@/store/student.js'
import {ref, reactive, onMounted, inject, toRef, toRefs} from "vue";
import {storeToRefs} from "pinia";
import Constants from "@assets/js/Constants.js";

const $utils = inject(Constants.$utilsSymbol);

const studentStore = useStudentStore();
// const {
//   name,
//   address,
//   studentInfo
// } = storeToRefs(studentStore);
const {changeCity, resetCity, resetStudentInfo} = studentStore;

function handleClick() {
  let propName = $utils.randomString(5);
  let propvalue = $utils.randomString(3);
  studentStore.address[propName] = propvalue;
  let random = $utils.GetRandomNum(10, 20);
  if (random > 17) {
    console.log("resetCity");
    resetCity();
  }

}

</script>

问题

1 . resetAddres方法内部会将 defineStoree函数的第二个参数函数中的 address 指向一个新的响应式代理对象, 这个新的响应式代理对象显然没有任何属性。 对于组件A 他模板中渲染的仍然还是之前的对象。 这是为什么

  1. 应该不会有这种 resetAddress内部 重新将 address指向一个新的响应式代理对象的需求吧? 我之所以会这样写 是因为 有一种场景: 把后端返回的一个对象放置到store的addres中,但是不知道 返回的这个对象有哪些属性。 所以我无法通过address.xxPropName的形式给address设置值 ,而是用了 Object.assign(后端返回对象, address)。 在需要清空address对象的时候 就写出了这种 resetAddress的代码。

问题有描述清除么?


补充问题

我还有一个地方不明白。

const person = ref({
  name: "zhangsan",
  age: 12,
})
console.log("person", person)

ref的是一个对象,等价于这个对象 先 reactive( {} )得到一个响应式代理对象Proxy,然后RefImpl对象的value等于这个响应式代理对象. 就是i下面这种结构Vue3整合Pinia,懂行的大佬进来帮忙看下,为什么 重新在将store的属性设置为 reactive({})不生效?

如果执行 代码 person.value = {} 会出现什么现象?我的理解: person是RefImpl, 将他的value指向一个空对象,不再是之前的响应式代理对象。

实际现象:如下图,value还是一个响应式代理对象,只不过这个Proxy代理对象的target目标对象是空对象Vue3整合Pinia,懂行的大佬进来帮忙看下,为什么 重新在将store的属性设置为 reactive({})不生效?

问题: 这是为什么?怎么做到的? 有相关的参考资料或者源码么?

回复
1个回答
avatar
test
2024-06-27

如果想要直接替换整个对象的话,应该使用 ref 而不是 reactive,具体可查看官方文档reactive() 的局限性中的描述

不能替换整个对象:由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用。这意味着我们不能轻易地“替换”响应式对象,因为这样的话与第一个引用的响应性连接将丢失:

let state = reactive({ count: 0 })

// 上面的 ({ count: 0 }) 引用将不再被追踪
// (响应性连接已丢失!)
state = reactive({ count: 1 })

正确写法如下:

const address = ref({});

function resetAddress() {
  address.value = {};
}
回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容