本章概要
- 生命周期钩子
- 依赖注入
- 逻辑提取和重用
11.4 生命周期钩子
在组合 API 中,生命周期钩子通过调用 onXxx() 函数显示地进行注册。这些生命周期钩子注册函数只能在 setup() 期间同步使用,因为他们一来内部全局状态定位当前活动实例(即其 setup() 正在被调用的组件实例)。
在没有当前活动实例的情况下调用它们将导致错误。组件实例上下文也是在生命周期钩子的同步执行期间设置的,因此在生命周期钩子内同步创建的监听器和计算属性也会在组件卸载时被自动删除。
生命周期选项与组合 API 之间的对应关系如下:
- beforeCreate 和 created 没有对应的 onXxx() 函数,取而代之使用 setup() 函数
- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- update -> onUpdate
- beforeUnmount -> onBeforeUnmount
- unmount -> onUnmount
- activated -> onActivated
- deactivated -> onDeactivated
- errorCaptured -> onErrorCaptured
- renderTracked -> onRenderTracked
- renderTriggered -> onRenderTriggered
组合 API 中对应的生命周期钩子注册函数的名字就是生命周期选项名字首字母大写并添加前缀 on。
以下是一个在单文件组件内使用 组合 API 注册生命周期钩子的示例:
import { onMounted,onUpdate,onUnmounted } from 'vue'
const MyComponent = {
setup(){
onMounted(() => {
console.log('mounted')
}),
onUpdated(() => {
console.log('updated')
}),
onUnmounted(() => {
console.log('unmounted')
})
}
}
11.5 依赖注入
看以下代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>计算属性的getter和setter</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const { provide, inject, ref, onMounted } = Vue;
const app = Vue.createApp({})
const msgKey = Symbol();
const helloKey = Symbol();
app.component('parent',{
setup() {
const msg = ref('今天做核酸了吗?');
const sayHello = function(name){
console.log("Hello,"+ name);
}
// provide() 方法需要指定一个 Symbol 类型的 key
provide(msgKey,msg);
provide(helloKey,sayHello);
return {
msg
}
},
template:'<child/>'
})
app.component('child',{
setup(){
// inject() 方法接受一个可选的默认值作为第二个参数
// 如果没有提供默认值,并且在 provide 上下文中未找到该属性,则 inject 返回 undefined
const message = inject(msgKey,ref('昨天做核酸了'));
const hello = inject(helloKey);
onMounted(() => hello('明天全员核酸吗?'));
return {
message
}
},
// 当自身的数据属性来访问
template:'<p>{{ message }}</p>'
})
const vm = app.mount('#app');
</script>
</body>
</html>
现在如果修改 parent 组件的 msg 属性的值,则会引起 child 组件中注入的 message 属性的更改。
为了便于观察响应式依赖注入的更新,可以将 parent 组件的代码放到根组件实例中,这样在 Console 窗口中就可以通过修改 vm.msg 的值来观察 child 组件的模板内容的重新渲染。
修改上述代码,如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>计算属性的getter和setter</title>
</head>
<body>
<div id="app">
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const { provide, inject, ref, onMounted } = Vue;
const msgKey = Symbol();
const helloKey = Symbol();
const app = Vue.createApp({
setup() {
const msg = ref('今天做核酸了吗?');
const sayHello = function(name){
console.log("Hello,"+ name);
}
// provide() 方法需要指定一个 Symbol 类型的 key
provide(msgKey,msg);
provide(helloKey,sayHello);
return {
msg
}
},
template:'<child/>'
})
app.component('child',{
setup(){
// inject() 方法接受一个可选的默认值作为第二个参数
// 如果没有提供默认值,并且在 provide 上下文中未找到该属性,则 inject 返回 undefined
const message = inject(msgKey,ref('昨天做核酸了'));
const hello = inject(helloKey);
onMounted(() => hello('明天全员核酸吗?'));
return {
message
}
},
// 当自身的数据属性来访问
template:'<p>{{ message }}</p>'
})
const vm = app.mount('#app');
</script>
</body>
</html>
渲染结果如下:
11.6 逻辑提取和重用
当涉及跨组件提取和重用逻辑时,组合 API 非常灵活。组合函数并不依赖于 this,而只依赖于它的参数和全局导入的 Vue API 。
只需要将组件逻辑导出为函数,就可以重用组件逻辑的任何部分,甚至可以通过导出组件的整个 setup() 函数实现扩展功能。
看一个跟踪鼠标的例子,由于跟踪鼠标的功能会在多个组件中用到,所以将它的实现逻辑提取出来,封装为一个函数导出。代码如下:
mouse.js
import { ref,onMounted,onUnmounted } from 'vue'
export function useMousePosition(){
const x = ref(0)
const y = ref(0)
function update(e){
x.value = e.pageX
y.value = e.pageY
}
onMounted(() => {
window.addEventListener('mousemove',update);
})
onUnmounted(() => {
window.removeEventListener('mousemove',update);
})
return { x,y }
}
接下来在组件中使用 useMousePosition() 函数。如下:
import { useMousePosition } from './mouse'
export default {
setup() {
const {x,y} = useMousePosition()
// 其它逻辑...
return {x,y}
}
}
与其它重用组件逻辑的方式相比,使用组合 API 的好处如下:
- 暴露个模板的属性有明确的来源,因为它们是从组合函数返回的值
- 组合函数返回的值可以任意命名,这样就不会发生命名空间冲突
- 不需要为逻辑重用而创建不必要的组件实例