vue3响应式数据的相关函数
问题在初次使用vue3的时候,页面上有一个点击按钮,当点击按钮的时候页面数据发生相应的改变。export default {name: "HelloWorld",props: {msg: String,},setup(props) {let data = {num: 0,};let say = "hello";function add() {data.num++;say = "V
问题
在初次使用vue3的时候,页面上有一个点击按钮,当点击按钮的时候页面数据发生相应的改变。
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
let data = {
num: 0,
};
let say = "hello";
function add() {
data.num++;
say = "Vue3";
}
return { data, add, say };
},
};
然而无论怎么点击也无法实现效果
原因是vue3中的数据响应式需要引入vue内置的方法实现
1 ref函数
vue3中提供了ref来创建基本数据的响应式,
// 引入ref函数
import { ref } from "vue";
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
// 使用ref接收数据参数, 返回响应处理的data数据
let data = ref({
num: 0,
o: ["1", 2],
});
let say = ref("hello");
function add() {
data.value.num++;
data.value.o[data.value.o.length] = "4";
say.value = "Vue3";
console.log(data.value);
}
return { data, add, say };
},
};
注意:使用ref函数的话,在setup中获取数据需要.value
属性
可以发现, 数据已经是响应式了
这里先暂停一下,我们先去看一下经过ref包装之后的数据是什么样的?
import { ref } from "vue";
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
// 使用ref接收数据参数, 返回响应处理的data数据
let data = ref({
num: 0,
o: ["1", 2],
});
let say = ref("hello");
function add() {
data.value.num++;
data.value.o[data.value.o.length] = "4";
say.value = "Vue3";
console.log(data.value);
}
return { data, add, say };
},
};
- 经过ref函数包装 之后,会为包装的数据进行一个
(Proxy)代理
,包装一个响应式对象
,对象中创建一个属性value
,保存着传入的数据。 - 当包装返回的实例属性
value
发生改变的时候(定义的响应式数据改变),便会被代理检测,检测到数据变化则会重新触发渲染 - 当使用ref包装一个
引用数据
的时候,ref会使用reactive
函数, 它会将引用数据对象中的所有属性(包括深层属性)创建ref,同时为每一个包装的实例添加value
属性,这个value保存的也就是对应的属性值,ref也就实现了引用数据类型的响应式
这里打印经过包装的引用类型数据
Proxy {num: 0}
2 reactive
reactive可以处理数据响应式问题
和ref不同的是,reactive可以深层代理数据
import { ref, reactive } from "vue";
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
// 使用ref接收数据参数, 返回响应处理的data数据
let data = reactive({
num: 0,
o: ["1", 2],
});
let say = ref("hello");
function add() {
data.num++;
data.o[data.o.length] = "4";
say.value = "Vue3";
console.log(data);
}
return { data, add, say };
},
};
reactive为传入的数据包装ref,生成value,利用Proxy代理检测数据变化。
注意:一般引用数据类型添加响应式使用
reactive
, 基本数据类型使用ref
响应是数据与源数据引用问题:
- 在
ref
中
根据数据类型保持与源数据的引用关系来决定源数据是否改变(基本数据不保持引用关系,引用数据保持引用关系),即修改ref返回的基本类型数据不会改变源数据,但是修改引用乐行数据会改变源数据
// 引入ref函数
import { ref } from "vue";
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
// 一个普通的数据
let data = {
num: 0,
o: ["1", 2],
job: {
myJob: "web",
wage: 2000,
},
},
str = "hello";
// 包装ref
let refData = ref(data),
dataStr = ref(str);
function add() {
// 改变两种类型数据的响应式
refData.value.num++;
refData.value.o.push("233");
refData.value.job.wage += 1000;
dataStr.value = "Vue3!";
}
// 返回响应数据和响应数据
return { add, refData, data, dataStr, str };
},
};
源数据中,对象源数据发生改变,而基本数据类型数据没有改变
-
在
reactive
中
因为在ref中代理对象时是引用Reactive方法,所以最终reactive代理展现的效果与ref代理对象的效果无异(reactive代理基本数据类型也不会修改源数据,代理引用数据类型会修改源数据) -
两种情况本质上就是数据类型决定源数据的改变
3 toRef
创建一个ref对象,这个响应式对象的value引用了接收的对象的某个响应式属性,且与源数据保持引用关系
// 引入ref函数
import { ref, toRef, reactive } from "vue";
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
// 一个普通的数据
let data = {
num: 0,
o: ["1", 2],
},
str = "hello";
// 为数据添加响应式
let refData = reactive(data);
// 创建一个ref对象,这个对象的value保持对传入对象属性的引用
// 当想要单独提取出响应式对象中的响应式属性时可以使用
let dynamicProp = toRef(refData, "o");
let say = ref(str);
function add() {
data.num++;
dynamicProp.value[dynamicProp.value.length] = "4";
say.value = "Vue3";
console.log(data.o, str);
// 通过检测发现,使用ref定义的响应式数据如果是基本数据类型,
// 那么定义后的数据不保持对源数据的引用.
// 如果定义的是引用数据类型,那么将会保持对对源数据的引用
// 使用toRef后, toRef包装后的value属性保持对源数据的引用,修改响应数据后
// 源数据也会发生改变
}
return { data, add, say, dynamicProp };
},
};
4 toRefs
和toRefs功能差不多,只不过toRefs可以创建多个ref对象
因为当有多个响应式对象需要单独提取的时候,toRef就显得过于复杂了,使用toRefs可以简化这个过程
import { toRefs, reactive } from "vue";
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
// 一个普通的数据
let data = {
num: 0,
o: ["1", 2],
job: {
myJob: "web",
wage: 2000,
},
};
let refData = reactive(data);
// 为每一个属性都添加value引用,
// 通过结构获取到想要的ref对象,
// 这个时候既拿到了响应数据,源数据因为引用关系而改变。
let { o, num, job } = toRefs(refData);
function add() {
num.value++;
o.value.push("233");
job.value.wage += 1000;
}
return { add, o, num, job, data };
},
};
单独拿到了响应数据了,且源数据发生了改变
5 shallowRef
特殊的ref,只处理基本数据类型的响应式,无法为引用数据类型添加响应式
- 添加基本数据
// 引入ref函数
import { reactive, ref, shallowRef } from "vue";
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
// 一个普通的数据
let data = {
num: 0,
o: ["1", 2],
job: {
myJob: "web",
wage: 2000,
},
},
str = "hello";
let refData = shallowRef(data),
dataStr = shallowRef(str);
function add() {
// refData.value.num++;
// refData.value.o.push("233");
// refData.value.job.wage += 1000;
// 稍微更改了添加规则
dataStr.value += " Vue3!";
}
return { add, refData, data, dataStr, str };
},
};
- 添加引用数据
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
// 一个普通的数据
let data = {
num: 0,
o: ["1", 2],
job: {
myJob: "web",
wage: 2000,
},
},
str = "hello";
let refData = shallowRef(data),
dataStr = shallowRef(str);
function add() {
refData.value.num++;
refData.value.o.push("233");
refData.value.job.wage += 1000;
}
return { add, refData, data, dataStr, str };
},
};
注意当同时改变的响应式数据有基本数据和引用数据时,页面也会触发更新
6 shallowReactive
只为对象的第一层属性添加响应式
- 当改变对象响应数据最外层时
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
// 一个普通的数据
let data = {
num: 0,
o: ["1", 2],
job: {
myJob: "web",
wage: 2000,
},
},
str = "hello";
let refData = shallowReactive(data),
dataStr = shallowRef(str);
function add() {
// 第一层的数据
refData.num++;
// 不是第一层的数据
// refData.o.push("233");
// refData.job.wage += 1000;
}
return { add, refData, data, dataStr, str };
},
};
- 当改变响应数据非最外层时
// 引入ref函数
import { shallowReactive, shallowRef } from "vue";
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
// 一个普通的数据
let data = {
num: 0,
o: ["1", 2],
job: {
myJob: "web",
wage: 2000,
},
},
str = "hello";
let refData = shallowReactive(data),
dataStr = shallowRef(str);
function add() {
// 第一层的数据
// refData.num++;
// 不是第一层的数据
refData.o.push("233");
refData.job.wage += 1000;
}
return { add, refData, data, dataStr, str };
},
};
当同时改变的是最外层数据,和非最外层数据时会触发页面更新
7 triggerRef
ref生成的数据强制在页面更新页面
前面在使用shallowRef添加响应对象时,数据不能更新到页面,使用triggerRef便可以强制更新
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
// 一个普通的数据
let data = {
num: 0,
o: ["1", 2],
job: {
myJob: "web",
wage: 2000,
},
};
let refData = shallowRef(data);
function add() {
refData.value.num++;
refData.value.o.push("233");
refData.value.job.wage += 1000;
triggerRef(refData);
}
return { add, refData, data, dataStr, str };
},
};
页面视图发生了更新
8 readony
让一个响应式数据变为深只读
<template>
<div>
<button @click="add">点击数据将会发生改变</button>
<p>响应式数据:{{ readonlyData }}</p>
</div>
</template>
<script>
// 引入ref函数
import { readonly, shallowReadonly, reactive } from "vue";
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
// 一个普通的数据
let data = {
num: 0,
o: ["1", 2],
job: {
myJob: "web",
wage: 2000,
},
};
let refData = reactive(data);
// 将响应式数据添加只读
let readonlyData = readonly(refData);
function add() {
// 更改只读的响应式数据
readonlyData.num++;
readonlyData.o.push("233");
readonlyData.job.wage += 1000;
}
return { add, refData, data, readonlyData };
},
};
</script>
无论是浅层还是深层响应式数据都只能读,不能修改
9 shallowReadony
让一个响应式数据变为浅只读
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
// 一个普通的数据
let data = {
num: 0,
o: ["1", 2],
job: {
myJob: "web",
wage: 2000,
},
};
let refData = reactive(data);
// 将响应式数据添加只读
let shallowReadonlyData = shallowReadonly(refData);
function add() {
// 更改只读的响应式数据
shallowReadonlyData.num++;
shallowReadonlyData.o.push("233");
shallowReadonlyData.job.wage += 1000;
}
return { add, refData, data, shallowReadonlyData };
},
};
点击前
点击后
发现只有浅层数据有只读限制,深层数据并未设置只读,蓝色区域数据发生改变
10 toRaw
将响应式数据变更为非响应式数据(基础数据)
export default {
name: "HelloWorld",
setup(props) {
let reData = reactive({
num: 0,
o: ["1", 2],
job: {
myJob: "web",
wage: 2000,
},
}),
dataStr = reactive("hello");
let strRaw = toRaw(dataStr),
dataRaw = toRaw(reData);
// 从打印可以看出只有使用reactive创建的响应式数据才能转换为普通数据
// 使用ref创建的响应式数据转换后还是ref对象
console.log(dataRaw, strRaw);
// job: {myJob: 'web', wage: 2000}
// num: 0
// o: (2) ['1', 2]
// 'hello'
function add() {
dataRaw.num++;
dataRaw.o.push("233");
dataRaw.job.wage += 1000;
strRaw = "vue3";
console.log("reData: ", reData);
console.log("dataStr: ", strRaw);
}
return { add, dataRaw, strRaw };
},
};
点击前
点击后
页面数据没有更新,但是检测的去响应数据发生了改变
如果比较源数据与转换后的非响应数据,我们会发现它们是一个数据。
11 markRaw
将响应式数据永久变更为非响应式数据
当有一段数据,都是响应式的,现需要添加一段展示数据不需要添加响应式,为了避免性能浪费,可以使用markRaw来永久改变某个数据为非响应式
<template>
<div>
<button @click="add">添加一段非响应式数据</button>
<button @click="upDate">更新响应式数据</button>
<button @click="reData.job.addProp.msg = '更新了'">更新添加的数据</button>
<p>响应数据:{{ reData }}</p>
</div>
</template>
<script>
// 引入ref函数
import { reactive, markRaw } from "vue";
export default {
name: "HelloWorld",
setup(props) {
let reData = reactive({
num: 0,
job: {
myJob: "web",
wage: 2000,
},
});
// 更新数据
function upDate() {
reData.num++;
reData.job.myJob = "www";
reData.job.wage += 100;
}
// 添加数据
function add() {
let newData = {msg: "这是添加的数据"}
reData.job["addProp"] = newData;
}
return { upDate, add, upDataAdd, reData };
},
};
</script>
后添加的数据会成为响应式数据
此时需要使用markRaw将响应数据转为普通数据
// 添加数据
function add() {
let newDate = markRaw({ msg: "这是添加的数据" });
reData.job["addProp"] = newDate;
console.log(newDate);
}
数据无法响应了
12 customRef
自定义ref, 通过基础ref配置满足自定义要求的ref
<template>
<div>
<input type="text" v-model="delayWord" />添加数据
<p>响应数据:{{ delayWord }}</p>
</div>
</template>
<script>
// 引入自定义ref函数
import { customRef } from "vue";
export default {
name: "HelloWorld",
setup() {
// 创建一初始数据
let hotWord = "初始数据";
// 定义自己的ref ---
function myRef(value, delay) {
// 定时器名称
let timer;
// 返回一个customRef
// 相当于一个只有响应式功能的基础ref
// 接收一个回调函数
// 回调函数接收两个参数 track, trigger. 返回一个对象
// 参数 track ---> 通知getter监视返回的数据
// 参数 trigger ---> 通知set更改数据后去更新视图
return customRef((track, trigger) => {
return {
get() {
console.log("获取了" + value);
// 通知检测返回值
track();
return value;
},
set(newValue) {
console.log("设置了: " + value + "\t新值为: " + newValue);
// 清除定时器 --- 用于节流
clearTimeout(timer);
// 更改数据
value = newValue;
timer = setTimeout(() => {
// 通知更新视图
trigger();
}, delay);
},
};
});
}
let delayWord = myRef(hotWord, 1000);
return { delayWord };
},
};
</script>
效果
注意customRef参数的作用
13 补充
检测响应式数据的方法
isRef: 检查一个值是否为一个 ref 对象
isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
更多推荐
所有评论(0)