1.Reactive 判断的API
isProxy:检查对象是否由reactive或者readonly创建的proxy
isReactive:检查独享是否由reactive创建的响应式代理
isReadonly:检查是否由readonly创建的只读代理
toRow:返回reactive或readonly代理的原始对象
shallowReactive:创建一个响应式代理,它跟踪自身的property响应性,但不执行嵌套对象的深层响应式转化,(深层还是原生对象)
shallowReadonly:创建一个proxy,使其自身的property为只读,但不执行嵌套对象的只读转换
2.toRefs
如果我们使用ES6的解构语法,对reactive返回的对象进行结构获取值,那么之后无论是修改解构后的变量,还是修改 reactive返回的对象,数据都不再是响应式的
const state = reactive({
name:'wjm',
age:18
})
const {name,age} = state
Vue为我们提供了一个toRefs函数,可以将reactive返回的对象中的所有属性都转成ref 那么我们再次进行解构之后,解构出来的name和age都是ref的
const {name,age} = toRefs(state)
这种做法相当于在state.name和ref.value之间建立了连接,任何一个修改都会引起另一个变化
3.toRef
只转换一个属性为ref
const state = reactive({
name:'wjm',
age:18
})
//普通解构
let {name} = state
//refs解构,接受一个响应式对象,将state中的所有属性转换成ref,返回对象,然后解构
let {name} = toRefs(state)
//toRef,只转换name属性,返回单个ref对象
let name = toRef(state,'name')
unRef:如果参数是ref对象,返回内部值,否则直接返回
val = unRef(val)等同于val = isRef(val)?val.value:val
4.shallowRef 与 triggerRef
shallowRef:创建一个浅层的ref对象
当我们不希望使一个对象具有深层响应式时,可以使用shallowRef,这样,我们改变深层的值的时候,不会触发视图更新
同时,Vue3提供了triggerRef方法,接受一个响应式对象,触发该对象的响应式更新,比如:
<script>
import {ref,shallowRef,triggerRef} from 'vue'
export default {
name: '',
setup(props,context) {
const info = shallowRef({
name:"wjm",
friends:['abc','cba','joey']
})
const changeFriends = ()=>{
info.value.friends.pop()
//手动触发info响应式更新
triggerRef(info)
}
return {
info,
changeFriends
}
}
}
</script>
5.customRef
customRef:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制,返回一个带有get和set的对象
useDebounceRef.js
import {customRef} from 'vue'
// 自定义ref
export default function(value){
let timer = null
return customRef((track,trigger)=>{
return {
get(){
//收集依赖
track()
return value
},
set(newValue){
clearTimeout(timer)
timer = setTimeout(() => {
value = newValue
// 触发依赖,更新视图
trigger()
}, 2000);
}
}
})
}
app.vue
<template>
<div>
<input type="text" v-model="message">
{{message}}
</div>
</template>
<script>
// import {ref,customRef} from 'vue'
import useDebounceRef from '../hook/useDebounceRef'
export default {
name: '',
setup(){
const message = useDebounceRef('hello world')
return {
message
}
}
}
</script>
6.computed 计算属性
在vue3中,我们可以在setup中使用computed方法来编写一个计算属性 computed方法接受一个getter函数或一个包含set、get的对象
<script>
import {ref,computed} from 'vue'
export default {
setup(){
const firstName = ref('kobe')
const lastName = ref('bryant')
// computed函数返回的是一个ref对象
// 用法1:传入一个getter
// const fullName = computed(()=>firstName.value +" "+ lastName.value)
// 用法2:传入一个对象,包含getter,setter
const fullName = computed({
get:()=>firstName.value + lastName.value,
set(newValue){
const names = newValue.split(' ')
firstName = names[0]
lastName = names[1]
}
})
const changeFullName = ()=>{
fullName.value = 'james wu'
}
return {
fullName,
changeFullName
}
}
}
</script>
7. watchEffect、watch 侦听数据
在optionAPI中,我们可以通过watch来侦听data和props的数据的变化
在compositionAPI中,我们使用 watchEffect和watch来实现侦听数据变化
watchEffect接受一个回调函数,该函数默认会先执行一次,分析依赖了哪些数据,在这些数据改变时,会执行注册的回调
watch.vue
<script>
import {ref,watchEffect} from 'vue'
export default {
setup(){
const name = ref('wjm')
const age = ref(18)
const changeName = ()=>{
name.value = 'kittle'
}
// watchEffect默认会先执行一次,分析依赖了哪些数据,在这些数据改变时,会执行注册的回调
watchEffect(()=>{
console.log('now name is:',name.value)
})
const changeAge = ()=>age.value++
return {
name,
age,
changeAge,
changeName
}
}
}
</script>
watchEffect会返回一个停止侦听函数stop,执行后会停止该监听
const stop = watchEffect(()=>{
console.log('now name is:',name.value)
})
if(age > 25){
}
watchEffect的清除副作用
比如我们需要在侦听函数中发送请求,但是在请求还没哟到达的时候,我们停止了侦听器,或者侦听器函数被再次执行了,那么上一次的请求就应该被取消掉,这个时候我们就可以清除上一次的副作用;
我们可以在watchEffect传入的函数回调中,可以获取到一个参数,onInvalidate
当副作用即将重新执行或者侦听器被停止时,会执行onInvalidate函数传入的回调函数,我们可以在传入的回调函数中执行一些清理操作
watchEffect((onInvalidate)=>{
const timer = setTimeout(() => {
console.log('now name is:',name.value)
}, 2000);
onInvalidate(()=>{
//在这个回调中执行一些清理操作
clearTimeout(timer)
})
})
watch:等同于optionAPI中的watch,是惰性的,第一次挂载不会触发
import {ref,reactive,watch} from 'vue'
export default {
setup(){
const info = reactive({name:'why',age:18})
// 第一种写法:传入getter函数和回调函数
watch(()=>info.name,(newValue,oldValue)=>{
console.log('newValue:',newValue,'oldValue:',oldValue)
})
// 第二种:传入一个可响应对象:reactive/ref对象
// reactive对象:拿到的newValue和oldValue都是reactive对象
// 如果
// ref对象:获取到的newValue和oldValue都是value,不是ref对象
watch(info,(newValue,oldValue)=>{
console.log('newValue:',newValue,'oldValue:',oldValue)
})
const changeData = ()=>{
info.name = 'kobe'
}
return {
info,
changeData
}
}
}
</script>
第一种监听方式:watch接受两个参数,get函数和回调函数
第二种监听方式:watch接受两个参数,响应式对象(reactive/ref)和回调函数,根据传入的值的类型不同,回调中拿到的值的类型也不同,具体见代码;
watch函数的第一个参数可以接受一个数组,这样,回调中的newValue和oldValue也分别是一个数组,类型可以可不相同
watch的选项
- 深度侦听:当watch传入的是响应式对象,默认深度监听,其他情况中,我们可以给watch传入第三个参数,配置对象
watch(info,(newValue,oldValue)=>{
console.log('newValue:',newValue,'oldValue:',oldValue)
},
{
deep:true, //深度监听
immediate:true //立即执行
})
8.在setup中使用ref
在vue2中我们可以使用this.$refs.xxx获取dom,但在vue3中,setup中不能绑定this,因此我们不能使用这种方式
可以使用ref函数进行绑定
<template>
<div>
<h2 ref="title">哈哈哈</h2>
</div>
</template>
<script>
import {ref,watchEffect} from 'vue'
export default {
setup(){
//调用ref,传入null,dom挂载后会把对应的dom传给title对象
const title = ref(null)
watchEffect(()=>{
console.log(title.value)
},{
//flush的默认值为pre,意为在挂载,更新时都会执行,
// flush:"pre",
// flush:'post'
})
return {
title
}
}
}
</script>