代码测试
- 源对象改变也会改变ref内存数据
<template>
<div class='box'>
{{ abcDemo?.a?.b?.c }}
<button @click="changeAbc">Change ABC</button>
<div>
{{ abdDemo?.a?.b?.c }}
</div>
</div>
</template>
<script lang='ts' setup>
import { ref, reactive, computed, onMounted, nextTick, PropType } from 'vue';
const abc: any = {
a: {
b: {
c: 1
}
}
}
const abcDemo = ref<any>({})
const abdDemo = ref<any>({})
abcDemo.value = abc;
function changeAbc() {
abc.a.b.c = 1;
// abcDemo 经打印,数值也会跟着变化,说明abc和abcDemo用的是同一块堆内存
console.log(abcDemo.value, "slfjslkslkfjsdlfsj")
abc.d = "5"
console.log(abcDemo.value, "asdasdasdasdasdasdasdasdasdasd")
//这样赋值,能够使页面更新数值
abdDemo.value = abc;
}
/*
结论是:上面对象虽然用的是同一块内存数据,但是,原对象abc修改的时候不会触发abcDemo ref的响应式,说明vue3不是通过监听数据内存的改变
来响应式,而是通过直接修改的时候劫持的方式来监听响应式
*/
</script>
<style lang='scss' scoped></style>
- 赋值源对象数据后的ref数据改变也会影响源对象的内存数据
<template>
<div class='box'>
{{ abcDemo?.a?.b?.c }}
<button @click="changeAbc">Change ABC</button>
</div>
</template>
<script lang='ts' setup>
import { ref, reactive, computed, onMounted, nextTick, PropType } from 'vue';
const abc: any = {
a: {
b: {
c: 1
}
}
}
const abcDemo = ref<any>({})
function changeAbc() {
abcDemo.value = abc;
// 这个修改子对象的数据,也会让abc的内存发生变化,所以他们共用的是一块内存数据
abcDemo.value.a.b.c = 5
console.log(abc, "abc-是否发生内存变化")
}
</script>
<style lang='scss' scoped></style>
- 如何赋值才能使视图刷新
<template>
<div class='box'>
{{ abcDemo?.a?.b?.c }}
<button @click="changeAbc">Change ABC</button>
</div>
</template>
<script lang='ts' setup>
import { ref, reactive, computed, onMounted, nextTick, PropType } from 'vue';
const abc: any = {
a: {
b: {
c: 1
}
}
}
const abcDemo = ref<any>({})
function changeAbc() {
abcDemo.value = abc;
abc.a.b.c = 8
//这里这样赋值能够响应式,因为视图模板在这里赋值的时候还是空对象数据
abcDemo.value = abc;
}
</script>
<style lang='scss' scoped></style>
<template>
<div class='box'>
{{ abcDemo?.a?.b?.c }}
<button @click="changeAbc">Change ABC</button>
</div>
</template>
<script lang='ts' setup>
import { ref, reactive, computed, onMounted, nextTick, PropType } from 'vue';
const abc: any = {
a: {
b: {
c: 1
}
}
}
const abcDemo = ref<any>({
a: {
b: {
c: 1
}
}
})
function changeAbc() {
abcDemo.value = abc;
setTimeout(() => {
abcDemo.value = null;
nextTick(() => {
// 这样能够响应式,说明对象引用指针发生改变就能响应式
abcDemo.value = abc;
abc.a.b.c = 8
})
})
}
</script>
<style lang='scss' scoped></style>
- 如何赋值才能使同样内存的对象的ref发生数据改变呢
<template>
<div class='box'>
{{ demo[0]?.a?.b?.c }}
</div>
</template>
<script lang='ts' setup>
import { ref, reactive, computed, onMounted, nextTick, PropType, toRaw } from 'vue';
import { cloneDeep } from "lodash-es"
const obj1 = {
a: {
b: {
c: 1
}
}
}
const arr1 = [obj1, { "a": 3 }, { "a": "slkdjfslkfdjs" }]
console.log(arr1[0] === obj1, "打印结果是true,说明用的是同一块内存引用")
const demo = ref<any>(arr1)
console.log(demo.value[0] === obj1, "打印结果是false,因为demo.value[0] 已经是一个proxy包装的对象了")
console.log(toRaw(demo.value[0]) === obj1, "打印结果是true,说明toRaw不是单纯的拷贝一份对象返回的,而是返回原对象的引用,所以和obj1是对等的")
setTimeout(() => {
obj1.a.b.c = 7
demo.value.map((i, index) => {
console.log(toRaw(i), "原始值")
console.log(obj1, "obj1")
if (index === 0) {
console.log(toRaw(i) === obj1, "slkvnskdjlsfsdfnlsfsjlkfj")
}
if (toRaw(i) === obj1) {
console.log("find found")
// 下面这样也不能触发响应式,因为i是个proxy对象,不能直接赋值,直接赋值会失去proxy,变成一个单纯的对象
//i = obj1;
//下面这样不能使视图发生改变,因为内存是一致的,数据没发生改变,不会触发页面响应式
//i = Object.assign(i, obj1)
//下面这样也可以使视图发生改变,因为深拷贝了一份内存数据,引用发生改变,所以赋值会使vue监听到且比较数据变更不同才会响应式刷新页面
//demo.value[index] = cloneDeep(obj1)
// 下面这样可以使 视图发生改变,i是proxy,需要通过Object.assign 等方式赋值,这样就能响应式,且还是那个proxy对象
i = Object.assign(i, cloneDeep(obj1))
}
})
}, 2000)
</script>
<style lang='scss' scoped></style>
结论
ref 或者reactive被赋值其他对象数据,用的是同一块内存,而不是深拷贝
所以如果你想通过修改原对象abc,使拷贝的ref reactive发生视图改变,那是不行的。
所以你得直接去修改已经赋值abc的ref 或者reactive 对象去修改数据才能发生视图改变
复杂的对象内存数据发生改变,能否触发视图更新响应式的关键一个是新旧数据的内存引用发生改变,另外一个务必得通过ref或者reactive对象进行操作才能进行数据改变的劫持。
前端工程师、程序员