上一篇我们说了ref 和 reactive创建数据,下面来做一些补充
1…递归监听和非递归监听
1.1 递归监听
1. 默认情况下, 无论是通过ref还是reactive都是递归监听
每一层都监听
<template>
<div>
<p>{{state.a}}</p>
<p>{{state.gf.b}}</p>
<p>{{state.gf.f.c}}</p>
<p>{{state.gf.f.s.d}}</p>
<button @click="myFn">按钮</button>
</div>
</template>
<script>
import {reactive} from 'vue';
// import {ref} from 'vue';
export default {
name: 'App',
setup() {
let state = reactive({
a:'a',
gf:{
b:'b',
f:{
c:'c',
s:{
d:'d'
}
}
}
});
function myFn() {
state.a = '1';
state.gf.b = '2';
state.gf.f.c = '3';
state.gf.f.s.d = '4';
console.log(state);
console.log(state.gf);
console.log(state.gf.f);
console.log(state.gf.f.s);
}
return {state, myFn}
}
}
</script>
2.递归监听存在的问题
如果数据量比较大, 非常消耗性能
打印后,发现每一层对象都被包装为了Proxy对象,
如果数据量比较大, 非常消耗性能
2.2 非递归监听
使用shallowReactive
和shallowRef
shallowReactive:
<script>
// 3.非递归监听
import {shallowReactive} from 'vue';
export default {
name: 'App',
setup() {
let state = shallowReactive({
// let state = ref({
a:'a',
gf:{
b:'b',
f:{
c:'c',
s:{
d:'d'
}
}
}
});
function myFn() {
state.a = '1';
state.gf.b = '2';
state.gf.f.c = '3';
state.gf.f.s.d = '4';
console.log(state);
console.log(state.gf);
console.log(state.gf.f);
console.log(state.gf.f.s);
}
return {state, myFn}
}
}
</script>
再次打印输出结果,发现只有第一层被封装为了Proxy对象
,
但是第2, 3,4层数据为什么也发生了变化呢?
因为修改了第一层,它就去修改UI了
shallowRef:
注意点:
- 如果是通过shallowRef创建数据,
那么Vue监听的是.value的变化, 并不是第一层的变化 - Vue3只提供了triggerRef方法, 没有提供triggerReactive方法
- 所以如果是reactive类型的数据, 是无法主动触发界面更新的
triggerRef(state);
2.toRow 和 markRow
2.1 toRow
- 从Reactive 或 Ref中得到原始数据
let obj = {name:‘lnj’, age:18};
let state = reactive(obj);
let obj2 = toRaw(state);- toRaw作用 :做一些不想被监听的事情(提升性能)
例子:
ref/reactive数据类型的特点:
- 每次修改都会被追踪, 都会更新UI界面, 但是这样其实是非常消耗性能的
- 所以如果我们有一些操作不需要追踪, 不需要更新UI界面, 那么这个时候,
- 我们就可以通过
toRaw
方法拿到它的原始数据, 对原始数据进行修改, 这样就不会被追踪, 这样就不会更新UI界面, 这样性能就好了
import {reactive, toRaw} from 'vue';
export default {
name: 'App',
setup() {
let obj = {name:'lnj', age:18};
// state和obj的关系:
// 引用关系, state的本质是一个Proxy对象, 在这个Proxy对象中引用了obj
let state = reactive(obj);
let obj2 = toRaw(state);
function myFn() {
// 如果直接修改obj, 那么是无法触发界面更新的
// 只有通过包装之后的对象来修改, 才会触发界面的更新
obj2.name = 'zs';
console.log(obj2); // {name: "zs", age: 18}
console.log(state); // {name: "zs", age: 18}
}
return {state, myFn}
}
}
上面的例子是,toRow获取 Reactive
类型的原始数据,
如果toRow要获取 ref
类型的原始数据,
注意点:
- ref本质: reactive
ref(obj) -> reactive({value: obj}) - 注意点: 如果想通过toRaw拿到ref类型的原始数据(创建时传入的那个数据)
那么就必须明确的告诉toRaw方法, 要获取的是.value
的值
因为经过Vue处理之后, .value中保存的才是当初创建时传入的那个原始数据
let obj = {name:'lnj', age:18};
let state = ref(obj);
let obj2 = toRaw(state.value);
2.2 markRow
markRaw
将数据标记为永远不能追踪的数据
一般在编写自己的第三方库时使用
import {reactive, markRaw} from 'vue';
export default {
name: 'App',
setup() {
let obj = {name: 'lnj', age: 18};
obj = markRaw(obj);
let state = reactive(obj);
function myFn() {
state.name = 'zs';
}
return {state, myFn}
}
}
3. toRef 和 toRefs
- toRef
创建一个ref类型数据, 并和以前的数据关联 - toRefs
批量创建ref类型数据, 并和以前数据关联 - toRef和ref区别
ref
创建出来的数据和以前无关
(复制)
toRef
-创建出来的数据和以前的有关
(引用)
ref-数据变化会自动更新
界面
toRef-数据变化不会自动更新
界面
<script>
import {ref, toRef} from 'vue';
export default {
name: 'App',
setup() {
let obj = {name:'lnj'};
/*
ref(obj.name) -> ref(lnj)
-> reactive({value:lnj})
* */
// ref->复制
// let state = ref(obj.name);
// toRef->引用
/*
ref和toRef区别:
ref->复制, 修改响应式数据不会影响以前的数据
toRef->引用, 修改响应式数据会影响以前的数据
ref->数据发生改变, 界面就会自动更新
toRef->数据发生改变, 界面也不会自动更新
toRef应用场景:
如果想让响应式数据和以前的数据关联起来, 并且更新响应式数据之后还不想更新UI, 那么就可以使用toRef
* */
let state = toRef(obj, 'name');
function myFn() {
state.value = 'zs';
console.log(obj);
console.log(state);
}
return {state, myFn}
}
}
</script>