组合式 API
setup
-
setup执行的时机
- 在beforeCreate之前执行一次,this是undefined。
-
setup的参数
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
// 第一种情况 <script> export default { props: { num: Number }, setup(props) { console.log(props.num) return } } </script> // 第二种情况 <script setup> const props = defineProps({ num: Number }) console.log(props.num) </script>
-
context:上下文对象
-
context对象不是响应式的,可以被安全地解构: { attrs, slots, emit, expose }
-
attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
this.$attrs
。 -
slots: 收到的插槽内容, 相当于
this.$slots
。 -
emit: 分发自定义事件的函数, 相当于
this.$emit
。
// 模板 <template> <div> {{num}} <button @click="emit('change')">num++</button> </div> </template> // 第一种情况 <script> export default { emits: ['change'], props: { num: Number }, setup(p, context) { console.log(p, context.emit) return { emit: context.emit } } } </script> // 第二种情况 <script setup> const props = defineProps({ num: Number }) const emit = defineEmits(['change']) </script>
-
响应式 API
ref
- 作用: 定义一个响应式的数据
- 语法:
const xxx = ref(initValue)
- 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
- JS中操作数据:
xxx.value
- 模板中读取数据: 不需要.value,直接:
<div>{{xxx}}</div>
- 备注:
- 接收的数据可以是:基本类型、也可以是对象类型。
- 基本类型的数据:响应式依然是靠
Object.defineProperty()
的get
与set
完成的。 - 对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数——
reactive
函数。
reactive
- 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用
ref
函数) - 语法:
const 代理对象= reactive(源对象)
接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象) - reactive定义的响应式数据是“深层次的”。
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
shallowReactive与shallowRef
- shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
- shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
readonly与shallowReadonly
- 接受一个对象(反应式或普通)或一个ref并返回一个只读代理
- readonly: 让一个响应式数据变为只读的(深只读)。
- shallowReadonly:让一个响应式数据变为只读的(浅只读)。
import { reactive, readonly, watchEffect } from 'vue'
const original = reactive({ count: 0 })
const copy = readonly(original)
watchEffect(() => {
console.log(copy.count)
})
original.count++
copy.count++ // warning!
computed
import {computed} from 'vue'
setup(){
...
//计算属性——简写
let fullName = computed(()=>{
return person.firstName + '-' + person.lastName
})
//计算属性——完整
let fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
}
watch
-
与Vue2.x中watch配置功能一致
-
两个小“坑”:
- 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
- 监视reactive定义的响应式数据中某个属性时:deep配置有效。
//情况一:监视ref定义的响应式数据
watch(sum,(newValue,oldValue)=>{
console.log(newValue,oldValue)
},{immediate:true})
//情况二:监视多个ref定义的响应式数据
watch([sum,msg],(newValue,oldValue)=>{
console.log(newValue,oldValue)
})
/* 情况三:监视reactive定义的响应式数据
若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
若watch监视的是reactive定义的响应式数据,则强制开启了深度监视
*/
watch(person,(newValue,oldValue)=>{
console.log(newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>person.job,(newValue,oldValue)=>{
console.log(newValue,oldValue)
},{immediate:true,deep:true})
//特殊情况
watch(()=>person.age,(newValue,oldValue)=>{
console.log(newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
watchEffect
-
不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
-
watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
watchEffect(()=>{
const num1 = sum.value
const num2 = person.age
console.log('watchEffect')
})
toRef与toRefs
- 将响应式对象中的某个属性单独提供给外部使用时
const name = toRef(person,'name')
return {
...toRefs(obj),
msg
}
Checks API
- isRef:检查一个值是否是一个 ref 对象。
- unref:如果参数是 ref,则返回内部值,否则返回参数本身。这是一个糖函数(val = isRef(val) ? val.value : val)
- isProxy:检查对象是否是由reactive, readonly, shallowReactive,shallowReadonly创建的代理。
- isReactive:检查对象是否是reactive,shallowReactive
- isReadonly:检查对象是否是readonly,shallowReadonly
其他
- 例子:创建一个 debounced ref,它只在最近一次 set 调用后的某个超时后更新值:
- track 方法放在 get 中,用于提示这个数据是需要追踪变化的
- vue重新解析模板 触发事件 trigger,告诉vue触发页面更新
<template>
<input type="text" v-model="keyword">
<h3>{{keyword}}</h3>
</template>
<script>
import {ref,customRef} from 'vue'
export default {
name:'Demo',
setup(){
// let keyword = ref('hello')
function myRef(value,delay){
let timer
return customRef((track,trigger)=>{
return{
get(){
track()
return value
},
set(newValue){
clearTimeout(timer)
timer = setTimeout(()=>{
value = newValue
trigger()
},delay)
}
}
})
}
let keyword = myRef('hello',500)
return {
keyword
}
}
}
</script>
toRaw 与 markRaw
-
toRaw:
- 作用:将一个由
reactive
生成的响应式对象转为普通对象。 - 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
const foo = {} const reactiveFoo = reactive(foo) console.log(toRaw(reactiveFoo) === foo) // true
- 作用:将一个由
-
markRaw:
- 作用:标记一个对象,使其永远不会再成为响应式对象。
- 应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类库等。
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
const foo = markRaw({}) console.log(isReactive(reactive(foo))) // false // also works when nested inside other reactive objects const bar = reactive({ foo }) console.log(isReactive(bar.foo)) // false
生命周期
- Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
beforeDestroy
改名为beforeUnmount
destroyed
改名为unmounted
- Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
beforeCreate
===>setup()
created
=======>setup()
beforeMount
===>onBeforeMount
mounted
=======>onMounted
beforeUpdate
===>onBeforeUpdate
updated
=======>onUpdated
beforeUnmount
==>onBeforeUnmount
unmounted
=====>onUnmounted
当捕获到从后代组件传播的错误时,注册要调用的钩子。
- 其他生命周期
provide 与 inject
-
作用:实现祖与后代组件间通信
-
父组件有一个
provide
选项来提供数据,后代组件有一个inject
选项来开始使用这些数据
// 祖级
setup(){
const add=()=>{
num.value++
}
provide('add',add)
}
// 后代
setup(){
const add=inject('add')
return{
add
}
}
errorcaptured)
provide 与 inject
[外链图片转存中…(img-28sDWGYu-1651830543792)]
-
作用:实现祖与后代组件间通信
-
父组件有一个
provide
选项来提供数据,后代组件有一个inject
选项来开始使用这些数据
// 祖级
setup(){
const add=()=>{
num.value++
}
provide('add',add)
}
// 后代
setup(){
const add=inject('add')
return{
add
}
}