<>问题

在初次使用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 方法创建的代理

技术
下载桌面版
GitHub
Gitee
SourceForge
百度网盘(提取码:draw)
云服务器优惠
华为云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信