问题
在初次使用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来创建基本数据的响应式,
import { ref } from "vue";
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
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) {
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) {
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返回的基本类型数据不会改变源数据,但是修改引用乐行数据会改变源数据
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";
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 };
},
};
源数据中,对象源数据发生改变,而基本数据类型数据没有改变
3 toRef
创建一个ref对象,这个响应式对象的value引用了接收的对象的某个响应式属性,且与源数据保持引用关系
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);
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);
}
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);
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,只处理基本数据类型的响应式,无法为引用数据类型添加响应式
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() {
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++;
}
return { add, refData, data, dataStr, str };
},
};
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.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>
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);
console.log(dataRaw, strRaw);
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>
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>
import { customRef } from "vue";
export default {
name: "HelloWorld",
setup() {
let hotWord = "初始数据";
function myRef(value, delay) {
let timer;
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 方法创建的代理
|