前言
Vue3.x正式版发布已经快半年了,相信大家也多多少少也用Vue3.x开发过项目。那么,我们今天就整理下Vue3.x中的响应性API。
响应性API
reactive
作用: 创建一个响应式数据。
本质: 传入数据(复杂类型:数组和json对象)包装成一个Proxy
对象。如果传入其他对象,默认情况下修改对象,界面不会自动更新,如果想更新,可以通过重新赋值(创建一个新的对象)的方式。
<template>
<div class="reactive">
<button @click="fn">点击</button>
<p>{{ state }}</p>
<button @click="fn1">点击1</button>
<p>{{ timeState }}</p>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "Reactive",
setup() {
let state = reactive({
name: "123",
});
function fn() {
console.log(state);
state.name = "456";
}
let timeState = reactive({
time: new Date(),
});
function fn1() {
const newTime = new Date(timeState.time.getTime());
newTime.setDate(timeState.time.getDate() + 1);
timeState.time = newTime;
console.log(timeState.time);
}
return {
state,
fn,
timeState,
fn1,
};
},
};
</script>
ref
作用: 创建一个响应式数据。
本质: (该本质写的不够严谨)针对复杂类型还是reactive
。当我们给ref
函数传递一个值(一般是简单类型)之后,ref
函数底层会自动将ref
转换成reactive
,即ref(123) => reactive({value:123})
,所以修改对应的值必须在后面加上.value
。
注意点: 在template
标签内不用加.value
。
<template>
<div>
<button @click="fn">点击</button>
<p>{{state}}</p>
<button @click="fn1">点击1</button>
<p>{{arrState}}</p>
</div>
</template>
<script>
import {ref} from "vue";
export default {
name:"Ref",
setup(){
let state = ref(123);
function fn () {
state.value = 345;
}
let arrState = ref([]);
function fn1 (){
arrState.value.push("1");
}
return {
state,
fn,
arrState,
fn1
}
}
}
</script>
shallowReactive
作用: 创建一个响应式 proxy
,跟踪其自身 property
的响应性,但不执行嵌套对象的深度响应式转换 (暴露原始值)。
本质: 对于嵌套对象不做响应,值跟踪自身的第一层property
。
<template>
<div>
<button @click="fn">点击</button>
<button @click="fn1">点击1</button>
<p>{{state}}</p>
</div>
</template>
<script>
import { shallowReactive } from "vue"
export default {
name:"ShallowReactive",
setup(){
let state = shallowReactive({
name:"maomin",
age:{
number:20
}
})
function fn(){
state.name = "123"; // 响应性
}
function fn1(){
state.age.number = 23; // 无响应性
}
return {
state,
fn,
fn1
}
}
}
</script>
shallowRef
作用: 创建一个 ref
,它跟踪自己的 .value
更改,但不会使其值成为响应式的。不会将其值转化为Proxy
对象。
<template>
<div>
<button @click="fn">点击</button>
<p>{{ state }}</p>
<button @click="fn1">点击1</button>
<p>{{ state1 }}</p>
</div>
</template>
<script>
import {
shallowRef,
ref,
// triggerRef
} from "vue";
export default {
name: "ShallowRef",
setup() {
let state = shallowRef({
name: "maomin",
});
let state1 = ref({});
function fn() {
state.value.name = "345";
console.log(state.value); // {name: "345"}, 但是UI界面不会变。
// triggerRef(state); // 如果想触发UI界面,可以使用它。
}
function fn1() {
state1.value = {
name: "123",
};
// eslint-disable-next-line no-irregular-whitespace
console.log(state1.value); // Proxy {name: "123"}
}
return {
state,
fn,
state1,
fn1,
};
},
};
</script>
readonly
作用: 获取一个对象 (响应式或纯对象) 或 ref
并返回原始 proxy
的只读 proxy
。只读 proxy
是深层的:访问的任何嵌套 property
也是只读的。
<template>
<div>
<button @click="fn">点击</button>
<p>{{os}}</p>
<p>{{state}}</p>
</div>
</template>
<script>
import {reactive, readonly} from "vue";
export default {
name:"Readonly",
setup(){
let state = reactive({
name:"maomin",
age:{
number:18
}
})
let os = readonly(state);
function fn(){
os.name = "123";
}
return{
os,
state,
fn
}
}
}
</script>
shallowReadonly
作用: 创建一个 proxy
,使其自身的 property
为只读,但不执行嵌套对象的深度只读转换 (暴露原始值)。
<template>
<div>
<button @click="fn">点击</button>
<p>{{os}}</p>
<p>{{state}}</p>
</div>
</template>
<script>
import {
reactive,
shallowReadonly,
isReadonly
} from "vue";
export default {
name:"ShallowReadonly",
setup(){
let state = reactive({
name:"maomin",
age:{
number:18
}
})
let os = shallowReadonly(state);
function fn(){
console.log(isReadonly(os.name)) //false
os.age.number = 20;
}
return{
state,
os,
fn,
}
}
}
</script>
toRaw
作用: 响应式对象转普通对象。
本质:返回由reactive
或 readonly
方法转换成响应式代理的普通对象。这是一个还原方法,可用于临时读取,访问不会被代理/跟踪,写入时也不会触发更改。不建议一直持有原始对象的引用。
<template>
<div>
<button @click="fn">点击</button>
<p>{{state}}</p>
</div>
</template>
<script>
import { reactive, toRaw } from "vue";
export default {
name:"ToRaw",
setup(){
const obj = {
name:"maomin"
};
let state = reactive(obj);
function fn(){
console.log(toRaw(state) === obj); //true
let obj1 = toRaw(state);
obj1.name = "123";
// eslint-disable-next-line no-irregular-whitespace
console.log(state); // Proxy {name: "123"}. 值虽改变,但是页面没有变化。
}
return {
state,
fn
}
}
}
</script>
markRaw
作用: 标记一个对象,使其永远不会转换为 proxy。返回对象本身。
<template>
<div>
<button @click="fn">点击</button>
<p>{{state}}</p>
</div>
</template>
<script>
import {markRaw,reactive} from "vue"
export default {
name:"MarkRaw",
setup(){
let obj = {name:'maomin', age: 20};
obj = markRaw(obj);
let state = reactive(obj);
function fn(){
state.name = '123';
console.log(state); //这里虽然打印出name:123,但是UI界面不会改变。
}
return{
state,
fn
}
}
}
</script>
toRef
如果使用ref
,我们修改响应式的数据是不会影响到原始数据的(复制)。如果使用toRef
,我们修改响应式的数据是会影响到原始数据的(引用)。
作用: 可以用来为源响应式对象上的 property
新创建一个 ref
。然后可以将 ref
传递出去,从而保持对其源 property
的响应式连接。
<template>
<div>
<button @click="fn">点击</button>
<p>{{state}}</p>
<button @click="fn1">点击1</button>
<p>{{state1}}</p>
</div>
</template>
<script>
import {reactive, ref, toRef} from "vue"
export default {
name:"ToRef",
setup(){
let obj = {name:"maomin"};
let os = reactive(obj);
let state = ref(os.name);
let state1 = toRef(os,'name');
// ref
function fn(){
state.value = "123";
console.log(os); // 原始数据不会发生改变
console.log(state);
}
// toRef
function fn1(){
state1.value = "345";
console.log(os); // 原始数据会发生改变
console.log(state1);
}
return {
state,
fn,
state1,
fn1
}
}
}
</script>
toRefs
作用: 将响应式对象转换为普通对象,其中结果对象的每个 property
都是指向原始对象相应 property
的ref
。
用途: 当从合成函数返回响应式对象时,toRefs
非常有用,这样消费组件就可以在不丢失响应性的情况下对返回的对象进行分解/扩散。
<template>
<div>
<button @click="fn">点击</button>
<p>{{state}}</p>
<button @click="fn1">点击1</button>
<p>{{foo}}</p>
</div>
</template>
<script>
import {reactive, toRefs} from "vue"
export default {
name:"ToRefs",
setup(){
let obj = {
name:"maomin",
age:20
}
let os = reactive(obj);
let state = toRefs(os);
function fn(){
state.name.value = "123";
os.name = "234";
console.log(os);
console.log(state);
console.log(state.name.value === os.name); //true
}
const { foo, bar } = useFeatureX();
function fn1(){
foo.value = "2";
}
return {
fn,
state,
foo,
bar,
fn1
}
}
}
function useFeatureX() {
const state = reactive({
foo: 1
})
// 返回时转换为ref
return toRefs(state)
}
</script>
customRef
作用: 创建一个自定义的 ref
,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收 track
和 trigger
函数作为参数,并应返回一个带有 get
和 set
的对象。
<template>
<div>
<button @click="fn">点击</button>
<p>{{state}}</p>
</div>
</template>
<script>
import {customRef} from "vue";
function myRef(value){
return customRef((track, trigger)=>{
return {
get(){
track();
console.log('get',value);
return value;
},
set(newValue){
console.log('set',newValue);
value = newValue;
trigger();
}
}
})
}
export default {
name:"CustomRef",
setup(){
let state = myRef(18);
function fn(){
state.value = 19;
}
return {
state,
fn
}
}
}
</script>
computed
作用: 依赖项变化时,其赋予的值也就相应改变。
注意点: 直接修改computed
是不可以的。
<template>
<div>
<p>{{state}}</p>
<p>{{os}}</p>
<button @click="fn">点击</button>
</div>
</template>
<script>
import {computed,ref} from "vue"
export default {
name: "Computed",
setup(){
let state = ref(12);
let os = computed(() => state.value + 1);
function fn(){
state.value = 23; // os的值也会相应改变
// os.value = 26; // Write operation failed: computed value is readonly
}
return{
state,
os,
fn
}
}
}
</script>
watchEffect
作用: 在响应式地跟踪其依赖项时立即运行一个函数,并在更改依赖项时重新运行它。
<template>
<div>
<button @click="fn">点击</button>
<p>{{state}}</p>
<p>{{state1}}</p>
<p>{{num}}</p>
</div>
</template>
<script>
import { reactive, ref, watchEffect } from "vue"
export default {
name:"WatchEffect",
setup(){
let state = ref(0);
let state1 = reactive({
name:"maomin"
})
let num = 1;
// 首次运行时会执行它,如果响应依赖项改变时,会重新执行它。
watchEffect(()=>{
// console.log(num);
console.log(state.value);
console.log(state1);
})
function fn() {
state.value = 3;
state1.name = "123";
num = 2;
}
return{
fn,
state,
state1,
num
}
}
}
</script>
watch
作用: 默认情况下,它也是惰性的——即回调仅在侦听源发生更改时调用。
<template>
<div>
<button @click="fn">点击</button>
<p>{{state}}</p>
<button @click="fn1">点击1</button>
<p>{{state1}}</p>
</div>
</template>
<script>
import {reactive, ref, watch} from "vue"
export default {
name:"Watch",
setup(){
// reactive
let state = reactive({
name:"maomin"
})
watch(
() => state.name,
(count) =>{
console.log(count); //123
}
)
function fn() {
state.name = "123";
}
//ref
let state1 = ref(1);
watch(
state1,
(count) =>{
console.log(count); //2
}
)
function fn1() {
state1.value = 2;
}
return {
state,
fn,
fn1,
state1
}
}
}
</script>
结语
谢谢阅读,希望没有浪费您的时间。可以结合代码例子自己敲敲代码,响应性API还有很多用处,这里只介绍了九牛一毛。2021年到来,赶快行动起来吧!
欢迎关注我的公众号
前端历劫之路
回复关键词
电子书
,即可获取12本前端热门电子书。回复关键词
红宝书第4版
,即可获取最新《JavaScript高级程序设计》(第四版)电子书。我创建了一个技术交流、文章分享群,群里有很多大厂的前端大佬,关注公众号后,点击下方菜单了解更多即可加我微信,期待你的加入。
本文转转自微信公众号前端历劫之路原创https://mp.weixin.qq.com/s/P7kXe--qPp9Y7fjVPAPSow,如有侵权,请联系删除。