第6章:组合式API

第16节:生命周期钩子与依赖注入

6.16.1 组合式API中的生命周期

在组合式API中,Vue 3提供了一系列函数来替代选项式API中的生命周期钩子。这些函数可以在组件的不同生命周期阶段执行。

生命周期钩子函数列表

选项式API 组合式API 说明
beforeCreate 无直接对应,使用setup() 组件实例创建前
created 无直接对应,使用setup() 组件实例创建完成
beforeMount onBeforeMount 组件挂载前
mounted onMounted 组件挂载完成
beforeUpdate onBeforeUpdate 组件更新前
updated onUpdated 组件更新完成
beforeUnmount onBeforeUnmount 组件卸载前
unmounted onUnmounted 组件卸载完成
errorCaptured onErrorCaptured 捕获子组件错误
renderTracked onRenderTracked 跟踪虚拟DOM渲染时的依赖
renderTriggered onRenderTriggered 虚拟DOM重新渲染时触发

基本用法

import { onMounted, onUpdated, onUnmounted } from 'vue'

export default {
  setup() {
    // 组件挂载后执行
    onMounted(() => {
      console.log('组件已挂载')
    })
    
    // 组件更新后执行
    onUpdated(() => {
      console.log('组件已更新')
    })
    
    // 组件卸载前执行
    onBeforeUnmount(() => {
      console.log('组件即将卸载')
    })
    
    // 组件卸载后执行
    onUnmounted(() => {
      console.log('组件已卸载')
    })
    
    // 捕获子组件错误
    onErrorCaptured((err, instance, info) => {
      console.error('捕获到子组件错误:', err, instance, info)
      return false // 阻止错误继续传播
    })
  }
}

<script setup>中使用

<template>
  <div>
    <p>count: {{ count }}</p>
    <button @click="count++">增加</button>
  </div>
</template>

<script setup>
import { ref, onMounted, onUpdated, onUnmounted } from 'vue'

const count = ref(0)

onMounted(() => {
  console.log('组件已挂载')
})

onUpdated(() => {
  console.log('组件已更新')
})

onUnmounted(() => {
  console.log('组件已卸载')
})
</script>

生命周期钩子的执行顺序

  1. 组件实例创建 → setup()
  2. 组件挂载前 → onBeforeMount()
  3. 组件挂载完成 → onMounted()
  4. 数据更新 → onBeforeUpdate()
  5. 组件更新完成 → onUpdated()
  6. 组件卸载前 → onBeforeUnmount()
  7. 组件卸载完成 → onUnmounted()

示例:使用生命周期钩子管理资源

<template>
  <div>
    <h3>定时器示例</h3>
    <p>count: {{ count }}</p>
    <button @click="stopTimer">停止定时器</button>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const count = ref(0)
let timer = null

onMounted(() => {
  // 组件挂载后启动定时器
  timer = setInterval(() => {
    count.value++
  }, 1000)
  console.log('定时器已启动')
})

onUnmounted(() => {
  // 组件卸载前清除定时器
  if (timer) {
    clearInterval(timer)
    console.log('定时器已清除')
  }
})

function stopTimer() {
  if (timer) {
    clearInterval(timer)
    timer = null
    console.log('定时器已手动停止')
  }
}
</script>

6.16.2 provideinject依赖注入

provideinject是Vue 3中用于实现组件间依赖注入的API。它们允许祖先组件向所有子孙组件提供数据,而不需要通过props一层一层传递。

基本用法

// 祖先组件
import { provide, ref } from 'vue'

export default {
  setup() {
    const location = ref('North Pole')
    
    // 提供数据给子孙组件
    provide('location', location)
    
    return { location }
  }
}

// 子孙组件
import { inject } from 'vue'

export default {
  setup() {
    // 注入祖先组件提供的数据
    const location = inject('location', '默认值')
    
    return { location }
  }
}

&lt;script setup&gt;中使用

<!-- 祖先组件 App.vue -->
<template>
  <div>
    <h2>祖先组件</h2>
    <p>位置: {{ location }}</p>
    <button @click="location = 'South Pole'">更改位置</button>
    <ChildComponent />
  </div>
</template>

<script setup>
import { ref, provide } from 'vue'
import ChildComponent from './ChildComponent.vue'

const location = ref('North Pole')

// 提供数据
provide('location', location)
</script>

<!-- 子孙组件 GrandchildComponent.vue -->
<template>
  <div>
    <h3>子孙组件</h3>
    <p>从祖先组件获取的位置: {{ location }}</p>
  </div>
</template>

<script setup>
import { inject } from 'vue'

// 注入数据
const location = inject('location', '默认位置')
</script>

provide的注意事项

  1. provide可以提供任何类型的数据:包括基本类型、对象、函数等
  2. provide的数据会被所有子孙组件共享:任何子孙组件都可以注入并访问
  3. provide的数据默认是响应式的:如果提供的是响应式数据,子孙组件会自动更新
  4. provide可以在任何组件中使用:不仅限于根组件

6.16.3 响应式provide/inject

provide提供响应式数据时,inject获取的数据也是响应式的,并且会自动更新。

示例:响应式依赖注入

<!-- 父组件 -->
<template>
  <div>
    <h2>主题设置</h2>
    <select v-model="theme">
      <option value="light">浅色主题</option>
      <option value="dark">深色主题</option>
    </select>
    <ChildComponent />
  </div>
</template>

<script setup>
import { ref, provide } from 'vue'
import ChildComponent from './ChildComponent.vue'

const theme = ref('light')

// 提供响应式主题数据
provide('theme', theme)

// 提供主题切换函数
provide('changeTheme', (newTheme) => {
  theme.value = newTheme
})
</script>

<!-- 子组件 -->
<template>
  <div :class="`theme-${theme}`">
    <h3>子组件</h3>
    <p>当前主题: {{ theme }}</p>
    <button @click="changeTheme('light')">切换到浅色主题</button>
    <button @click="changeTheme('dark')">切换到深色主题</button>
  </div>
</template>

<script setup>
import { inject, computed } from 'vue'

// 注入主题数据
const theme = inject('theme', 'light')
// 注入主题切换函数
const changeTheme = inject('changeTheme', () => {})
</script>

<style>
.theme-light {
  background-color: white;
  color: black;
}

.theme-dark {
  background-color: black;
  color: white;
}
</style>

只读注入数据

如果希望注入的数据是只读的,可以使用readonly函数包装:

import { provide, ref, readonly } from 'vue'

const count = ref(0)

// 提供只读的数据
provide('readonlyCount', readonly(count))

注入时的类型安全

在TypeScript中,可以为注入的数据添加类型:

import { inject, Ref } from 'vue'

// 注入响应式数据并指定类型
const location = inject<Ref<string>>('location', ref('默认值'))

// 注入普通数据并指定类型
const appName = inject<string>('appName', '默认应用名')

总结

Vue 3组合式API提供了一系列生命周期钩子函数和依赖注入API,使组件的生命周期管理和数据共享更加灵活。

  1. 生命周期钩子:使用onMountedonUpdated等函数替代选项式API中的生命周期钩子,可以在组件的不同阶段执行代码
  2. 依赖注入:使用provideinject实现组件间的数据共享,不需要通过props一层一层传递
  3. 响应式注入provide提供的响应式数据,inject获取后仍然保持响应性
  4. 类型安全:在TypeScript中可以为注入的数据添加类型,提高代码的安全性

在下一节中,我们将学习组合式API中的组合式函数(Composables),这是Vue 3中用于复用逻辑的重要特性。

« 上一篇 计算属性与侦听器 下一篇 » 组合式函数(Composables)