Composition API
1.判断API
-
isProxy
- 检查对象是否由reactive或readonly创建的proxy
-
isReactive
- 检查对象是否是由reactive创建的响应式代理;
- 如果该代理是readonly,但包裹了由reactive创建另一个代理,它也会返回true
-
isReadonly
- 检查对象是否由readonly创建的只读代理
-
toRaw
- 返回reactive或readonly代理的原始对象(不建议保留对原始对象的持久引用,请谨慎使用!!!)
-
shallowReactive
- 创建一个响应式代理,它跟踪其自身property的响应式,但不执行嵌套对象的深层响应式转换(深层还是原生对象)
-
shallowReadonly
- 创建一个proxy,使其自身的property为只读,但不执行嵌套对象的深度只读转换(深层还是可读可写)
2.toRefs
- 如果使用对象结构对reactive返回的对象进行获取值,然后再进行修改,数据就不是响应式了
- 那么toRefs就是Vue给我们提供的函数,可以将reactive返回的对象中的属性都转成ref;
- 那么再次进行结构的话就是ref
- 这种做法其实相当于在数据之间创立立链接,任何一个修改都会引起另外一个变化
<template>
<div>
<h2>{{info.name}}</h2>
<button @click="changeAge">点我修改</button>
</div>
</template>
<script>
import {reactive,toRefs} from "vue";
export default {
setup() {
//直接进行结构不是响应式的咯
//let {name,age} = reactive({name:"Tim",age:18})
const info = reactive({name:"阿基米德",age:100})
let {name,age} = toRefs(info)
//它其实是这样的
//toRefs({name:ref,age:ref}) 所以结构出来就可以是响应式的用了toRefs之后。
const changeAge = () => {
age.value++
}
//如果修改了info的age也是会使结构出来的值改变
const changeAge = () => {
info.age++
}
return {
info,
age,
name
}
}
}
</script>
3.toRef
- 如果我们只希望转换一个reactive对象中的属性为ref, 那么可以使用toRef的方法:
<template>
<div>
<h2>{{name}}</h2>
<button @click="changeAge">点我修改</button>
</div>
</template>
<script>
import {reactive,toRef} from "vue";
export default {
setup() {
const info = reactive({name:"阿基米德",age:100})
let name = toRef(info,"name")
const changeAge = () => {
info.name = "伽利略"
}
return {
info,
name
}
}
}
</script>
4.补充API
-
unref
-
如果我们想要获取一个ref引用中的value,那么可以通过unref方法:
- 如果参数是一个ref,则返回内部值,否则返回参数本身
- 这是val = isRef(val) ? val.value : val 的语法糖函数;
<script>
export default {
setup() {
const name = ref("tim")
foo(name)
function foo(bar) {
unref(bar) //val = isRef(val) ? val.value : val
}
}
}
</script>
- shalloRef与triggerRef
<script>
export default {
setup() {
const info = shallowRef({name:"Tim"}) //浅层的ref
const changeInfo = () => {
info.value.name = "Yang" //直接修改shallowRef的值是不行滴🙅
triggerRef(info);//手动触发 传入的值是ref
}
}
}
</script>
5.customRef
customRef() 预期接收一个工厂函数作为参数,这个工厂函数接受 track 和 trigger 两个函数作为参数,并返回一个带有 get 和 set 方法的对象。
一般来说,track() 应该在 get() 方法中调用,而 trigger() 应该在 set() 中调用。然而事实上,你对何时调用、是否应该调用他们有完全的控制权。
- 下面用一个案例来学习学习
<template>
<input v-model="message" />
<p>{{message}}</p>
</template>
<script>
import debounceRef from "./hook/cstdebounceRef"
export default {
setup() {
const message = debounceRef("hello world")
return {
message
}
}
}
</script>
<style scoped>
</style>
- 定义hook
//自定义ref
import {customRef} from "vue"
export default function(value,delay=1000) {
let timer = null
return customRef((track,trigger) => {
return {
get() {
track(); //收集对应依赖
return value;
},
set(newValue) {
clearTimeout(timer)
timer = setTimeout(()=> {
value = newValue
trigger()
},delay)
}
}
})
}
6.computed
-
在前面我们讲解过计算属性computed:当我们的某些属性是依赖其他状态时,我们可以使用计算属性来处理
- 我们可以在 setup 函数中使用 computed 方法来编写一个计算属性;
-
如何使用computed呢?
- 接收一个getter函数,并为getter函数返回值,返回一个不变的ref对象
- 接收一个具有get和set的对象,返回一个可变(可读写)ref对象;
<template>
<div>
<h2>{{fullName}</h2>
<button @click="changeName">修改firstName</button>
</div>
</template>
<script>
import {ref,computed} from "vue"
export default {
setup() {
const firstName = ref("yang")
condt lastName = ref("tim")
//第一种方式传入一个getter函数
//返回值是一个ref对象
//const fullName = computed(()=>firstName.value + lastName.value)
//第二种传入对象设置get和set
const fullName = computed({
get:() => fristName.value + " " + lastName.value,
set(newValue){
const names = newValue.split(" ")
firstName.value = names[0],
lastName.value = names[1]
}
})
const changeName = () => {
fullName.value = "kobe Bryant"
}
return {
fullName
}
}
}
</script>
7.watchEffect
-
watchEffect和watch都是用来完成响应式数据监听的
- watchEffect用于自动收集响应式数据依赖
- watch需要手动指定侦听的数据
-
watchEffect传入的函数会被立即执行一次,并且收集执行过程中的依赖
-
watchEffect只有在收集的依赖发生变化时,watchEffect传入的函数才会被再次执行
-
watchEffect的停止侦听(了解)
- 如果要停止监听,就用stop接收,watchEffect返回函数任何进行判断
-
watchEffect清除副作用(了解)
- 如果在开发中需要侦听函数中执行网络请求,但是在网络请求还没有达到的时候,我们停止了侦听器,或者侦听器侦听函数被再次执行了
- 那么上一次的网络请求需要被取消,那么就要清除上一次的副作用
- 在给watchEffect传入的函数被回调时,可以获取到一个参数:onInvalidate(随便起的名字)
- 可以在这个函数中操作清除的副作用
<template>
<div>
<h2>{{name}}---{{age}}</h2>
<button @click="changeName"></button>
<button @click="changeAge"></button>
</div>
</template>
<script>
import {ref,watchEffect} from "vue"
export default {
setup() {
const name = ref("tim")
const age = ref(21)
const changeName =() => {
name.value = "kobe"
}
//立即执行一次
const stop = watchEffect((onInvalidate)=>{
const timer = setTimeout(() => {
console.log("网络请求成功")
},2000)
//清除副作用
onInvalidate(()=> {
clearTimeout(timer)
})
console.log(name.value,age.value)
})
//停止监听,如果岁数超过三十岁,就停止监听
const changeAge =() => {
age.value++
if(age.value > 30) {
stop()
}
}
return {
name,age,changeName,changeAge
}
}
}
</script>
8.watchEffect执行机制
-
在默认情况下,组件的更新会在副作用函数执行之前
- 如果我们下午在副作用函数中获取到元素,是否可行
<template>
<div>
<h2 ref="title">执行机制</h2>
</div>
</template>
<script>
import { ref, watchEffect } from 'vue';
export default {
setup() {
const title = ref(null);
watchEffect(() => {
console.log(title.value);
})
return {
title
}
}
}
</script>
<style scoped>
</style>
-
打印出来会有两次,一次是null一次是元素
- 因为setup函数在执行时就会立即执行传入的副作用函数,这时,DOM并没有被挂载,所以打印出来为null
- 而当DOM挂载时,会给title的ref对象赋值新的值,副作用函数会再次执行,打印出来对应的元素
-
如果希望第一次时就打印出来对应的元素而不是null应该咋办啊
- 需要改变副作用函数的执行时机,
- 它的默认值是pre,会在元素挂载或者更新之前执行
- 改一下这个默认值就可以了有pre,post,sync但是最好不要使用sync
<template>
<div>
<h2 ref="title">哈哈哈</h2>
</div>
</template>
<script>
import { ref, watchEffect } from 'vue';
export default {
setup() {
const title = ref(null);
watchEffect(() => {
console.log(title.value);
}, {
//改变这个默认值:pre为post挂载后使用watchEffect
flush: "post"
})
return {
title
}
}
}
</script>
<style scoped>
</style>
9.watch
-
watch的API完全等同于组件watch选项的Property:
-
watch需要侦听特定的数据源,并在回调函数中执行副作用;
-
默认情况下它是惰性的,只有当被侦听的源发生变化时才会执行回调;
-
与watchEffect的比较:
- 懒执行副作用(第一次不会直接执行)
- 更具体的说明当哪些状态发生变化时,触发侦听器的执行;
- 访问侦听状态变化前后的值
-
-
-
监听单个数据源:
-
watch侦听函数的数据源有两种类型:
- 一个getter函数:但是该getter函数必须引用可响应式对象(reactive|ref)
- 直接写入一个可响应式对象,ref|reactive
-
<template>
<button @click="changeData">
监听watch修改
</button>
</template>
<script>
import {ref,reactive,watch} from "vue";
export default {
setup() {
const info = reactive({name:"Tim",age:21})
// 1.侦听watch时,传入一个getter函数
watch(()=>info.name,(newValue,oldValue) => {
console.log(newValue)
console.log(oldValue)
})
// 2.传入一个可响应式对象: reactive对象/ref对象
// 情况一: reactive对象获取到的newValue和oldValue本身都是reactive对象
// watch(info, (newValue, oldValue) => {
// console.log("newValue:", newValue, "oldValue:", oldValue);
// })
// 如果希望newValue和oldValue是一个普通的对象
// 情况二: ref对象获取newValue和oldValue是value值的本身
// const name = ref("why");
// watch(name, (newValue, oldValue) => {
// console.log("newValue:", newValue, "oldValue:", oldValue);
// })
// 如果希望newValue和oldValue是一个普通的对象
watch(() => {
return {...info}
}, (newValue, oldValue) => {
console.log("newValue:", newValue, "oldValue:", oldValue);
})
const changeData = () => {
info.name = "Yang"
}
return {
info,changeData
}
}
}
</script>
-
监听多个数据源
- 侦听器还可以使用数组同时侦听多个源:
<template>
<div>
<h2 ref="title">{{info.name}}</h2>
<button @click="changeData">修改数据</button>
</div>
</template>
<script>
import { ref, reactive, watch } from 'vue';
export default {
setup() {
// 1.定义可响应式的对象
const info = reactive({name: "tim", age: 18});
const name = ref("tim");
// 2.侦听器watch
watch([info, name], ([newInfo, newName], [oldInfo, oldName]) => {
console.log(newInfo, newName, oldInfo, oldName);
})
const changeData = () => {
info.name = "kobe";
}
return {
changeData,
info
}
}
}
</script>
<style scoped>
</style>
10.watch选项
-
如果我们希望侦听一个深层的侦听,那么依然需要设置 deep 为true:
- 也可以传入 immediate 立即执行;
<template>
<div>
<h2 ref="title">{{info.name}}</h2>
<button @click="changeData">修改数据</button>
</div>
</template>
<script>
import { ref, reactive, watch } from 'vue';
export default {
setup() {
// 1.定义可响应式的对象
const info = reactive({
name: "tim",
age: 18,
friend: {
name: "kobe"
}
});
// 2.侦听器watch
watch(() => ({...info}), (newInfo, oldInfo) => {
console.log(newInfo, oldInfo);
}, {
deep: true,
immediate: true
})
const changeData = () => {
info.friend.name = "james";
}
return {
changeData,
info
}
}
}
</script>
<style scoped>
</style>
11.总结
- 这篇文章是续写上一篇文章的东西,主要还是总结了一些选项式API的东西,vue官网的一些不经常使用的也在这里说明如何使用这些不常用的点。