Vue3-CompositionAPI-响应式基础
一、响应式基础
前提:你会使用
setup
函数或<script setup>
语法
1.reactive
我们可以使用 reactive()
函数创建一个响应式对象或数组:
import { reactive } from 'vue'
const state = reactive({ count: 0 })
- 当我们使用 reactive 函数处理我们的数据之后,数据被使用时就会进行依赖收集;
- 当数据发生改变时,所有收集到的依赖都是进行对应的响应式操作(比如更新界面);
- 事实上,在使用
Options API
时编写的 data 选项,也是在内部交给了 reactive 函数将其变成响应式对象的;
注意:
reactive 函数只能包裹对象或数组,包裹基本数据类型会丧失响应式,并且控制会打印出警告。
2. ref
reactive()
的种种限制归根结底是因为 JavaScript 没有可以作用于所有值类型的 “引用” 机制。为此,Vue 提供了一个 ref()
方法来允许我们创建可以使用任何值类型的响应式 ref:
import { ref } from 'vue'
const countRef = ref(0)
// 使用
console.log(countRef.value)
使用ref()
函数定义的响应式数据,是一个带有.value
的Ref对象,在<script>
中使用该值时,需要.value
赋值或取值。
而在template
模板中,则会自动解包,取出.value
的值。
实例代码:
<template>
<h2>composition API</h2>
<p>ref-simple: {{ simple }}</p>
<p>ref-obj: {{ obj }}</p>
<div>
<p><button @click="simple++">更改simple</button></p>
<p><button @click="obj.name += '1'">更改obj.name</button></p>
<p><button @click="editObjName">更改obj.name</button></p>
</div>
<hr />
<p>reactive-state: {{ state }}</p>
<p>reactive-count: {{ count }}</p>
<div>
<p><button @click="state.firstName += '1'">更改reactive-state</button></p>
<p><button @click="count++">更改reactive-count</button></p>
</div>
</template>
<script>
import { reactive, ref } from 'vue';
export default {
name: 'App',
setup() {
const simple = ref(0);
const obj = ref({ name: 'fct' });
// reactive
const state = reactive({ firstName: 'Steven', secordName: 'Diff' });
const count = reactive(0); // 报警告
function editObjName() {
obj.value.name += '1';
}
return {
simple,
obj,
state,
count,
editObjName
};
}
};
</script>
2.1 获取HTML元素或组件实例
使用ref()
函数,要声明一个同名的 ref。
<template>
<input ref="inputRef" />
</template>
<script setup>
import { ref, onMounted } from 'vue'
// 声明一个 ref 来存放该元素的引用
// 必须和模板里的 ref 同名
const inputRef = ref(null)
onMounted(() => {
inputRef.value.focus()
})
// 如果不使用 <script setup>,需确保从 setup() 返回 ref:
// export default {
// setup() {
// const inputRef = ref(null)
// // ...
// return {
// inputRef
// }
// }
// }
</script>
3. readonly
我们通过reactive()
或者ref()
可以获取到一个响应式的对象,但是某些情况下,我们传入响应式对象给其他地方(组件),希望在另外一个地方(组件)被使用,但是不能被修改,这个时候可以使用readonly()
。
readonly()
接受一个对象 (不论是响应式还是普通的) 或是一个ref
,返回一个原值的只读代理。
实例代码:
const original = reactive({ count: 0 })
const copy = readonly(original)
watchEffect(() => {
// 用来做响应性追踪
console.log(copy.count) // 1.打印 0
})
// 更改源属性会触发其依赖的侦听器
original.count++ // 2. 侦听器打印 1
// 更改该只读副本将会失败,并会得到一个警告
copy.count++ // warning!
二、reactive、ref、readonly相关API
1. reactive判断的API
1.1 isProxy
检查对象是否是由 reactive 或 readonly创建的 proxy
。
1.2 isReactive
- 检查对象
是否是由 reactive 创建的响应式代理
; - 如果
该代理是 readonly 建的
,但包裹了由 reactive 创建的另一个代理,它也会返回 true。
1.3 isReadonly
检查对象是否是由 readonly 创建的只读代理
。
1.4 toRaw
返回 reactive 或 readonly 代理的原始对象
(不建议保留对原始对象的持久引用。请谨慎使用)。
1.5 shallowReactive
创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换
(深层还是原生对象)。
1.6 shallowReadonly
创建一个 proxy,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换
(深层还是可读、可写的)。
2.toRefs
如果我们使用ES6的解构语法,对reactive返回的对象进行解构获取值,那么之后无论是修改结构后的变量,还是修改reactive 返回的state对象,数据都不再是响应式的:
import { reactive } from 'vue';
const state = reactive({
msg: 'fct',
tips: 'vue3'
});
const { msg, tips } = state;
// msg 为非响应式的
Vue为我们提供了一个
toRefs的函数
,可以将reactive返回的对象中的属性都转成ref;那么我们再次进行结构出来的 msg 本身都是 ref的;
import { reactive, toRefs } from 'vue';
const state = reactive({
msg: 'fct',
tips: 'vue3'
});
const { msg, tips } = toRefs(state);
这种做法相当于已经在state.msg
和ref.value
之间建立了链接,任何一个修改都会引起另外一个变化;
3. toRef
如果我们只希望转换一个reactive对象中的属性为ref, 那么可以使用toRef的方法:
import { reactive, toRef } from 'vue';
const state = reactive({
msg: 'fct',
tips: 'vue3'
});
const tips = toRef(state, 'tips');
// tips 为响应式数据
4. ref其他的API
4.1 unref
如果我们想要获取一个ref引用中的value,那么也可以通过unref方法
:
-
如果参数是一个 ref,则返回内部值,否则返回参数本身;
-
这是 val = isRef(val) ? val.value : val 的语法糖函数;
import { ref, unref } from 'vue'; const fct = ref(999); console.log(fct.value, unref(fct));
4.2 isRef
判断值是否是一个ref对象。
4.3 shallowRef
创建一个浅层的ref对象。
// 不能监听 info.name 发生的改变,因为是浅层Ref对象
const info = shallowRef({ name: 'fct' });
// 能监听 num 发生的改变
const num = shallowRef(32);
4.4 triggerRef
手动触发和 shallowRef 相关联的副作用。
<template>
<div>
<p>info: {{ info }}</p>
<p>num: {{ num }}</p>
<button @click="info.name += '1'">更改shallowRef-info中的name</button>
<button @click="editInfoName">triggerRef-更改shallowRef-info中的name</button>
<button @click="num++">shallowRef-num++</button>
</div>
</template>
<script setup>
import { shallowRef, triggerRef } from 'vue';
const info = shallowRef({ name: 'fct' });
const num = shallowRef(32);
function editInfoName(params) {
info.value.name += '1';
// 手动触发更新
triggerRef(info);
}
</script>