Composition API
什么是 Composition API?
Composition API = Reactivity API + Lifecycle hooks
响应式加上生命周期就组成了基础的 组合式API
const { reactive, watchEffect, onMounted } = Vue
export default {
setup () {
const state = reactive({ count: 0 })
watchEffect(() => console.log(state.count))
onMounted(() => console.log('mounted'))
return {
state,
increment: () => { state.count++ }
}
}
}
一个简单的例子,所有内容只需要写到setup函数中,为什么还要有个setup,vue作者是为了考虑options选项的兼容性,方便选项式API更好的迁移
在最后return你需要使用的到模板上
生命周期
组件的生命周期从render函数开始,执行setup函数中的组合式API,把template转成render,然后触发beforeCreate hook (vue3的组合式API生命周期hook中没有),然后初始化inject、methods、data、computed、watch、provide这些Options API,然后触发created hook,查看是否存在预编译的模板(预编译的模板是在构建过程中已经被编译为可直接生成虚拟 DOM 的渲染函数,这样在页面渲染时就无需再进行模板的编译,可以直接生成虚拟 DOM,进而提高页面的渲染速度),触发beforeMount hook,render渲染组件和子组件,触发mounted hook,完成一个基本的加载
当数据发生变化时触发beforeUpdate hook,render更新进入patch 进行diff算法计算出最小的改动,然后触发updated hook。
最后当组件被卸载之前(可能是跳转页面、v-if、v-for等数据改变导致该组件不存在了),触发beforeUnmount,卸载子组件,停止相关的响应式(计算属性和侦听器),最后触发unmounted hook。
父子触发的生命周期流程
父setup > 父beforeCreate > 父created> 父beforeMount > 子setup > 子beforeCreate > 子created > 子beforeMount > 子mounted > 父mounted
父beforeUpdate > 子beforeUpdate > 子updated > 父updated
父beforeUnmount > 子beforeUnmount > 子unmounted > 父unmounted
watchEffect和watch区别
watchEffect首次会执行一次(因为要收集依赖),只要里面有任何值的改动都会重新执行。
watch 需要定义一个监听源,然后返回里带有新值和老值,首次不会执行。
举个例子:
只需要把fetch方法写入watchEffect内部即可自动根据传入的props.id 更新
使用watchEffect实现自动fetch
源代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">
</div>
<script>
const { createApp, ref, watchEffect, toRef } = Vue
function usePost (getId, getA) {
console.log('usePost', getId());
return useFetch(() => `https://jsonplaceholder.typicode.com/todos/${getId()}`, () => getA())
}
function useFetch (getUrl, getA) {
const data = ref(null)
const error = ref(null)
const isPending = ref(true)
watchEffect(() => {
console.log(getA())
console.log(getUrl())
// console.log(data.value); 这里不能调用否则会递归
data.value = null
error.value = null
isPending.value = true
fetch(getUrl())
.then(res => res.json())
.then(_data => {
data.value = _data
console.log(data.value); // 这里可以调用
isPending.value = false
})
.catch(err => {
error.value = err
isPending.value = false
})
})
return {
data,
error,
isPending
}
}
const Post = {
props: ['id', 'a'],
setup (props) {
console.log('props', props.id);
const { data, error, isPending } = usePost(() => props.id, () => props.a)
const foo = ref('foo')
return {
data,
error,
isPending,
}
},
template: `
<div v-if="isPending">loading...</div>
<div v-else-if="data">{{ data }}</div>
<div v-else-if="error">Error: {{ error.message }}</div>
`
}
const App = {
components: { Post },
data () {
return {
id: 1,
a: 1,
}
},
template: `
<button @click="id++">change ID</button>
<button @click="a++">change a</button>
<Post :id="id" :a="a"/>
`
}
createApp(App).mount('#app')
</script>
</body>
</html>
拆分解耦
使用Composition API更好的拆分,如果你有很多地方都有共同的逻辑处理,你甚至可以把onmounted方法都提取出去,作为一个公共的方法
当功能多了后不同的功能分散在不同的地方,你会需要来回跳转和滚动,不便于查看和封装
使用Composition API后