文章目录
Reactivity API
响应式数据
vue3的响应式系统是脱离组件的,下面的代码可在js文件中直接执行。
reactive
reactive可以使数据变成响应式。
import {
reactive} from 'vue'
//本质上,下面的代码等于new Poxy({ count: 0 })
const obj = reactive({
count: 0 })
当只需要只读的代理,可以使用readonly,任何被访问的嵌套 property 也是只读的。
import {
readonly} from 'vue'
const obj = readonly({
count: 0 })
readonly也可以传入代理,返回新的代理,新的代理不可修改,新代理与传入的代理是不相同的。
import {
reactive,readonly} from 'vue'
const obj = reactive({
count: 0 })
const newObj = readonly(obj)
reactive和readonly不可以传入基本数据。
ref
我们知道Proxy只能代理对象,当我们需要代理基本数据时,我们可以使用ref。(ref啥都可以传)
import {
ref} from 'vue'
const count = ref(0)
const newRef = ref(count)
console.log(count.value) //0
console.log(newRef.value) //0
console.log(newRef === count) //true
如果ref传入了一个对象,其内部会帮我们调用reactive,返回一个代理。
ref可以传入任何数据。如果传入是一个代理,则直接使用代理,所以传入的代理和新的代理是相同的。
computed
computed传入的是一个函数,返回结果是一个带value属性的对象,当读取value值的时候,会根据情况决定是否要运行函数。
函数的执行时机是在调用返回结果的value时触发。
import {
ref,computed} from 'vue'
const count = ref(1)
const sum = computed(
()=>{
console.log("computed") //当依赖的值没发生变化时,运行多次sum.value,函数内仍然运行一次,只有当依赖发生变化时才会重新运行函数内。
return count.value * 2
}
)
console.log(sum.value) //先输出computed,再输出2
判断数据
vue3针对多种数据,提供了几个api让我们去判断数据的类型。
api | 描述 |
---|---|
isProxy | 判断数据是否是reactive或者readonly创建 |
isReactive | 判断数据是否是reactive创建 |
isReadonly | 判断数据是否是readonly创建 |
isRef | 判断数据是否是一个ref对象 |
转换数据
vue3针对多种数据,提供了几个api去转换数据的类型
api | 描述 |
---|---|
unRef | 本质等同于 isRef(数据) ? 数据.value : 数据 |
toRef | 将响应式对象某个属性转换为ref格式 |
toRefs | 将代理对象的所有属性转换为一个平面对象,每个属性都是ref格式 |
小结
- vue3返回的数据格式可分为两种,一种是proxy,一种是带value属性的对象,即ref object。
- 如果想要让一个对象变为响应式数据,可以使用reactive或ref。
- 如果想要让一个对象的所有属性只读,可以使用readonly。
- 如果想要让一个非对象数据变为响应式,可以使用ref。
- 如果想要根据已知的响应式数据得到一个新的响应式数据,使用computed。
- toRefs主要用于setup导出与导入时的解构,建议return时统一用toRefs包装一层再解构,导入时使用toRefs包装数据再解构出来,确保数据统一。
监听数据
watchEffect
import {
ref, watchEffect, reactive } from 'vue'
const state = reactive({
a: 1, b: 2 })
const count = ref(0)
watchEffect(
() => {
//watch会自动收集依赖,数据变化时会运行该函数
console.log("watch")
console.log(state.a, count.value)
}
)//立即运行
console.log(state.a)//读取a并不会执行watch,所以这里输出1
console.log(state.b)//读取b并不会执行watch,所以这里输出2
state.a++ //这里更改值,导致watch执行,所以先执行watch,输出'watch',2,0
console.log(state.a)//不会执行watch,输出2
state.b++//不触发watch
state.a++//触发watch
watch
watch需要手动指定监听的值,同vue2用法。
import {
ref, watchEffect, reactive } from 'vue'
const state = reactive({
a: 1, b: 2 })
const count = ref(1)
watch(
()=>state.a,
(newValue,oldValue)=>{
console.log(newValue,oldValue)
},
{
imediate:true}) //代理对象需要传函数
watch(
count,
(newValue,oldValue)=>{
console.log(newValue,oldValue)
},
{
imediate:true}) //ref直接传
watch(
[()=>state.a,count],
([newValue1,newValue2],[oldValue1,oldValue2])=>{
},
{
imediate:true}
) //传多个值
小结
- 无论是watchEffect还是watch,当依赖项变化时,回调函数的运行都是加入微队列的。
- 一般情况下推荐使用watchEffect,因为会自动收集依赖。
- 当不希望回调函数立即执行,可以选择watch,虽然watch配置imediate也可以立即执行,watchEffect会立即执行一次。
- watch适合在监听某些数据时,当数据变化时在处理函数里做一些跟监听数据无关的事情(因为与监听数据无关代表着watchEffect没有依赖,就不会触发处理函数)。
Composition API
setup
Reactivity API可以脱离vue使用,而Compositon API与前者不同,其与vue组件深度绑定
setup的基本用法如下:
<script>
export default(){
setup(props,context){
//对于同一个组件,只运行一次。该函数在组件属性被赋值后立刻执行,早于其他生命周期钩子。
//props是一个对象,包含所有组件属性。
//context是一个对象,包含了attrs、slots、emit(同vue2的this.$attrs、this.$slots、this.$emit)等。
// return的对象暴露给模板
//这里不能直接使用ES6的解构,需要使用toRef或toRefs包装再解构,直接解构会消除响应式
return {
XXX,
XXX
}
}
}
</script>
setup里不能使用this
3.2版本之后的vue3新增了setup语法糖,只需要在script里配置setup属性就可以不需要return。
当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容) 都能在模板中直接使用。
<script setup>
// 变量
const msg = 'Hello!'
// 函数
function log() {
console.log(msg)
}
</script>
生命周期
对比vue2,vue3去除beforeCreate、created,新增setup,beforeCreate和create可用setup代替。
除了setup,对比vue2其他生命周期钩子,都是在其前面加on前缀。
在vue3中,销毁是unmount。即去除beforeDestroy和destroyed,改叫onBeforeUnmount和onUnmounted。
与vue2相比,同时还新增了onRenderTracked和onRenderTriggered。
Option API | Composition API |
---|---|
beforeCreate | setup(新增) |
created | setup(新增) |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked(新增) | onRenderTracked(新增) |
renderTriggered(新增) | onRenderTriggered(新增) |
activated | onActivated |
deactivated | onDeactivated |
关于新增钩子函数的说明:
- renderTracked,参数为DebuggerEvent,触发的时机是在vnode收集到每一个依赖时。第一次刷新会触发。
- renderTriggered,参数为DebuggerEvent,触发的时机时某个依赖变化导致组件重新渲染时。第一次刷新并不会触发。
小结
Composition API相比于Option API,优势主要体现以下两方面:
- Composition API可以做到更好的逻辑复用和代码组织。(Option API上下横跳问题等)
- Composition API具有更好的类型推导。(Option API除了this问题还有函数问题都不利于ts推导)
总结
Composition API配合Reactivity API,可以在组件内部做到更加细粒度的控制,使得组件不同功能能做到高度聚合,提升代码的可维护性和可阅读性,同时所有的api变得更加函数式,这有利于类型推导,和ts深度配合。