前端面经-VUE3篇(二)--vue3组件知识(二)依赖注入、异步组件、生命周期、组合式函数、插件

目录

一、依赖注入

[1、 依赖注入是什么?](#1、 依赖注入是什么?)

2、最基础的使用

3、为什么使用依赖注入?

[4、 使用 Symbol 作注入名](#4、 使用 Symbol 作注入名)

二、异步组件

1、什么是异步组件?

2、最基础用法:defineAsyncComponent

3、在模板中使用异步组件

[4、配置加载状态:加载提示 / 超时 / 错误处理](#4、配置加载状态:加载提示 / 超时 / 错误处理)

三、生命周期

1、生命周期的本质:组件从"出生"到"死亡"的过程

2、生命周期钩子总览

[3、 各生命周期钩子的详细解释](#3、 各生命周期钩子的详细解释)

[1. setup()](#1. setup())

[2. onBeforeMount()](#2. onBeforeMount())

[3. onMounted()](#3. onMounted())

[4. onBeforeUpdate()](#4. onBeforeUpdate())

5.onUpdated()

[6. onBeforeUnmount()](#6. onBeforeUnmount())

[7. onUnmounted()](#7. onUnmounted())

4、钩子参数​

四、组合式函数

1、什么是组合式函数?

2、组合式函数的结构

3、组合式函数的核心特征

4、组合式函数能做什么?

5、实战场景汇总

6、总结一句话

五、插件

[1、什么是 Vue 插件?](#1、什么是 Vue 插件?)

2、插件的核心格式

3、注册方式

4、插件内部能做的事(功能总结)

5、注意事项

6、总结一句话


一、依赖注入

通常情况下,当我们需要从父组件向子组件传递数据时,会使用 props。想象一下这样的结构:有一些多层级嵌套的组件,形成了一棵巨大的组件树,而某个深层的子组件需要一个较远的祖先组件中的部分数据。在这种情况下,如果仅使用 props 则必须将其沿着组件链逐级传递下去,这会非常麻烦:

1、 依赖注入是什么?

依赖注入是一种让"祖先组件提供 "和"后代组件注入使用 "的机制,使用 provideinject 配合实现。

  • provide(key, value)祖先组件中声明可供后代使用的内容

  • inject(key)后代组件中声明需要注入的内容

2、最基础的使用

复制代码
父组件(祖先)使用 provide
<script setup>
import { provide } from 'vue'
provide('color', 'blue')
</script>

子组件(任意后代)使用 inject
<script setup>
import { inject } from 'vue'

const color = inject('color')
</script>
<template>
  <p style="color: {{ color }}">我是注入的颜色</p>
</template>
✅ 这样,子组件就无需 props,可以跨越多层组件拿到祖先组件提供的数据。

如果提供的值是一个 ref,注入进来的会是该 ref 对象,而不会自动解包为其内部的值。这使得注入方组件能够通过 ref 对象保持了和供给方的响应性链接。

3、为什么使用依赖注入?

传统方式 问题
使用 props 需要每层组件手动传递,维护困难
使用 Vuex / pinia 重,但适合全局状态
使用 event bus 不推荐,易维护混乱

✅ 依赖注入适合:

  • 状态共享(如配置、主题、权限)

  • 插件式封装(如表单、组件注册)

  • 面向组合逻辑的解耦

默认值与类型检查

如果子组件注入的 key 不存在,返回值为 undefined,你可以设置默认值:

const theme = inject('theme', 'light') // 默认值 fallback

你还可以传入工厂函数作为默认值:

const config = inject('config', () => ({ color: 'blue' }))

4、 使用 Symbol 作注入名

但如果你正在构建大型的应用,包含非常多的依赖提供,或者你正在编写提供给其他开发者使用的组件库,建议最好使用 Symbol 来作为注入名以避免潜在的冲突。

复制代码
// keys.js
export const myInjectionKey = Symbol()


// 在供给方组件中
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'
provide(myInjectionKey, { 
  /* 要提供的数据 */
})


// 注入方组件
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'

const injected = inject(myInjectionKey)

二、异步组件

Vue 的 异步组件(Async Components) 是一种优化性能与用户体验的机制,它允许你在需要时按需加载某个组件,而不是在应用启动时一次性加载全部组件。

异步组件在构建大型应用、提升首屏加载速度、实现懒加载等场景中非常重要,Vue 为此提供了专门的语法和加载配置。

1、什么是异步组件?

异步组件是指:组件不是立即加载的,而是在需要时才异步地被加载

它的核心思想是:"用的时候才加载",这对以下场景非常有用:

  • 页面拆分(如路由页面)

  • 大型组件库懒加载

  • 多个组件中只有部分在运行中用到

2、最基础用法:defineAsyncComponent

Vue 提供了官方函数 defineAsyncComponent() 来创建异步组件。

基本语法:

复制代码
import { defineAsyncComponent } from 'vue'

const AsyncComponent = defineAsyncComponent(() =>
  import('./MyHeavyComponent.vue')
)

这个组件会被延迟加载 ,只有在模板中首次渲染它时,Vue 才会触发 import() 加载它对应的文件。

3、在模板中使用异步组件

复制代码
<template>
  <div>
    <AsyncComponent />
  </div>
</template>

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

const AsyncComponent = defineAsyncComponent(() =>
  import('./MyHeavyComponent.vue')
)
</script>

4、配置加载状态:加载提示 / 超时 / 错误处理

defineAsyncComponent 还可以接受一个对象,配置异步加载的过程行为。

复制代码
import { defineAsyncComponent } from 'vue'

const AsyncComponent = defineAsyncComponent({
  loader: () => import('./MyHeavyComponent.vue'),

  // 加载时展示的组件
  loadingComponent: LoadingSpinner,

  // 出错时展示的组件
  errorComponent: ErrorMessage,

  // 延迟多长时间显示 loadingComponent(毫秒)
  delay: 200,

  // 加载超时时间(毫秒)
  timeout: 3000,

  // 出错时重试机制
  onError(error, retry, fail, attempts) {
    if (attempts <= 3) {
      retry()
    } else {
      fail()
    }
  }
})

各配置解释:

选项 功能
loader 必填。返回组件的异步加载 Promise(即 import()
loadingComponent 加载过程中显示的 UI
errorComponent 加载失败后显示的 UI
delay 加载多少毫秒后才显示 loading
timeout 超过此时间未加载完成视为失败
onError 异步加载失败时自定义处理(可自动重试)

注意:Vue Router 内部会自动处理 loading 状态,不需要你手动 defineAsyncComponent

三、生命周期

Vue 的生命周期(Lifecycle)指的是 Vue 组件从创建到销毁过程中经历的一系列阶段性钩子函数,你可以在这些钩子中执行逻辑,如数据初始化、DOM 操作、网络请求、清理等。

每个 Vue 组件实例在其创建过程中都会经历一系列初始化步骤,例如设置数据监听、编译模板、挂载 DOM,直到被销毁。Vue 提供了生命周期钩子函数,让我们有机会在不同阶段插入代码。

1、生命周期的本质:组件从"出生"到"死亡"的过程

整个过程大致如下:

创建阶段 → 挂载阶段 → 更新阶段 → 销毁阶段

2、生命周期钩子总览

阶段 钩子函数 作用
创建前 setup() Vue 3 中唯一入口
挂载前 onBeforeMount() 组件尚未挂载,DOM 还未生成
挂载后 onMounted() 组件已挂载,DOM 可操作
更新前 onBeforeUpdate() 响应式数据变更,但 DOM 还未更新
更新后 onUpdated() DOM 更新完毕,可做响应处理
卸载前 onBeforeUnmount() 组件将被销毁,做清理前工作
卸载后 onUnmounted() 已销毁,清理资源、解绑事件等

3、 各生命周期钩子的详细解释

1. setup()

阶段 :组件创建阶段(最先执行)
调用时机 :组件实例刚创建,datapropsmethods 都还没绑定到 this
作用

  • 是组合式 API 的入口;

  • 用来声明响应式数据、props、emit、watch、计算属性等;

  • 不能访问 this

  • 返回的变量会暴露给模板使用。

2. onBeforeMount()

阶段 :挂载前
调用时机 :组件即将挂载到 DOM(el 创建完毕但未插入 DOM)
作用

  • 此时模板已编译,但 DOM 未挂载;

  • 可以进行某些准备工作或日志记录。

常见用途

  • 初始化数据日志;

  • 注册未依赖 DOM 的第三方逻辑。

从组件"出生到上场"来理解生命周期

你可以把组件想象成一个"舞台演员":

阶段 比喻 生命周期钩子
写好剧本 编译模板等准备工作 setup()
穿好衣服化妆 DOM 虚拟结构准备就绪 onBeforeMount()
走上舞台 挂载 DOM,出现在页面 onMounted()

所以 onBeforeMount() 就是演员 刚准备好,马上就要上场 的那一刻。

Vue 在内部的执行流程:

setup() → 编译模板 → 创建虚拟 DOM →

→ 🔹onBeforeMount() →

→ 渲染并插入真实 DOM →

→ 🔹onMounted()

onBeforeMount 做了什么?

  • 组件已经准备好数据(比如 propsref);

  • 已经生成了虚拟 DOM(VNode);

  • 但还没有插入真实页面中

  • 所以此时你不能直接操作 DOM 元素

能干什么?

虽然还不能访问 DOM,但可以:

  • 准备数据(如果依赖 DOM 可延迟);

  • 进行日志埋点或调试;

  • 记录组件加载时间;

  • 检查组件状态是否完整;

  • 准备动画过渡参数(比如进入动画前的状态)。

3. onMounted()

阶段 :挂载后
调用时机首次 DOM 渲染完成 ,所有 DOM 元素可操作
作用

  • 执行需要访问 DOM 的逻辑;

  • 发起初始请求;

  • 注册事件监听器;

  • 引入第三方库(如 ECharts、Swiper)。

常见用途

  • 页面初始化请求;

  • DOM 操作(如获取滚动容器、焦点);

  • 动画执行。

你可以把它理解为:

"我的组件已经出现在页面上了,现在可以操作它的 DOM、做异步请求、初始化动画、注册事件等。"

可以用 onMounted() 做哪些事?

用途 说明
✅ 操作 DOM 可以安全地访问 document.querySelectorref 引用的元素
✅ 发起 API 请求 初始化组件数据,如加载表格数据、详情数据等
✅ 注册监听器 注册 scrollresizekeyboard 等全局事件
✅ 使用第三方库 比如挂载图表(ECharts)、富文本编辑器(Quill)等
✅ 初始化动画 初始化进入动画或过渡效果
✅ 上报埋点 发送统计日志、埋点监控等

4. onBeforeUpdate()

阶段 :更新前
调用时机 :响应式状态变化,DOM 还未更新
作用

  • 可用于比较新旧状态;

  • 做一些缓存或临时逻辑。

注意事项

  • 不建议操作 DOM;

  • 不要进行异步逻辑处理。

onBeforeUpdate() 是 Vue 组件中的一个更新阶段生命周期钩子 ,它在 响应式数据发生变化、DOM 还没有更新之前 被调用。

你可以把它理解为:

"组件数据变了,DOM 马上要重新渲染了,现在是我插手干预的最后机会。"

onBeforeUpdate() 的作用

用途 说明
✅ 获取旧的 DOM 状态 DOM 还没变,你可以访问"变化前"的样子
✅ 计算更新前的一些值 比如滚动高度、旧位置
✅ 清除旧状态标记 如之前的样式、数据缓存
❌ 不适合做异步请求 更新前阶段不能引起新响应式更新(否则会递归)

5.onUpdated()

阶段 :更新后
调用时机 :响应式数据更新完成,DOM 也已更新
作用

  • 可以对新 DOM 做处理;

  • 常用于监听局部 DOM 变化后执行逻辑。

onUpdated() 是 Vue 组件在 响应式数据变更导致 DOM 更新完成后 自动调用的生命周期钩子。

你可以理解为:

"Vue 已经把新数据同步到了页面上,现在你可以看到更新后的 DOM,想操作它、获取它的新状态、触发动画等等------就靠 onUpdated()。"

onUpdated() 能做什么?

能做的事 应用场景
✅ 读取更新后的 DOM 状态 比如新的高度、内容、布局
✅ 执行过渡动画、样式变化 比如 fade-in、滚动动画等
✅ 动态调整布局 比如等高布局、表格自适应列宽
✅ 与旧值对比 onBeforeUpdate() 配合使用
❌ 不推荐用来发请求 页面每次更新都会触发,可能频繁调用

onBeforeUpdate区别

项目 onBeforeUpdate() onUpdated()
调用时机 DOM 更新前 DOM 更新后
访问的是 旧 DOM 新 DOM
场景 缓存、准备、清理 更新后调整布局、动画
能否操作新结构 ❌ 不行 ✅ 可以

理解类比:装修房子

你可以把 Vue 更新比喻成"家装翻新":

  • onBeforeUpdate():工人刚拿到设计图纸,准备改造,但还没开始动工;

  • onUpdated():已经把墙刷好、灯装上、地板换了,可以检查结果了。

6. onBeforeUnmount()

阶段 :卸载前
调用时机 :组件即将从页面上被卸载
作用

  • 做清理工作,如清除定时器、取消监听器;

  • 防止内存泄漏;

  • 通知外部容器取消操作。

onBeforeUnmount() 是 Vue 组件即将被销毁前调用的生命周期钩子。

你可以理解为:

"Vue 准备把这个组件从页面中移除,我现在有一个机会做一些清理工作或资源释放。"

适合用 onBeforeUnmount() 做哪些事?

用途 举例
✅ 清除定时器 clearInterval()clearTimeout()
✅ 取消网络请求 调用 AbortController.abort() 或取消 token
✅ 移除事件监听器 removeEventListener()
✅ 停止动画或任务 比如取消动画帧 cancelAnimationFrame()
✅ 通知父组件或全局状态 通知组件退出、清除缓存等

7. onUnmounted()

阶段 :卸载后
调用时机 :组件 DOM 被完全移除
作用

  • 彻底释放资源;

  • 日志记录、事件通知等。

注意事项

  • 通常清理逻辑建议放在 onBeforeUnmount()onUnmounted() 比较少用。

onUnmounted() 是 Vue 组件在 被销毁之后 自动调用的生命周期钩子。

你可以理解为:

"Vue 已经把这个组件从页面中移除了,DOM 不再存在,响应式状态也都释放了。这是我最后一次处理事情的机会。"

onUnmounted() 的典型用途

用途 示例
✅ 日志记录 / 调试 组件销毁后打 log
✅ 通知外部状态 如 Vuex、Pinia 中的状态重置
✅ 执行异步销毁操作 提交状态到后端、更新埋点日志等
✅ 销毁订阅 / 解耦逻辑清理 比如 event bus 或 WebSocket 等注册事件

onBeforeUnmount() 的区别

钩子 时机 是否可以访问 DOM? 用途
onBeforeUnmount() 销毁前 ✅ 是 清除定时器/监听器
onUnmounted() 销毁后 ❌ 否 日志、通知、埋点、最后状态更新

一个指令的定义对象可以提供几种钩子函数 (都是可选的):

复制代码
const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode) {},
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode) {},
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode) {}
}

4、钩子参数

指令的钩子会传递以下几种参数:

  • el:指令绑定到的元素。这可以用于直接操作 DOM。

  • binding:一个对象,包含以下属性。

    • value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2
    • oldValue:之前的值,仅在 beforeUpdateupdated 中可用。无论值是否更改,它都可用。
    • arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"
    • modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }
    • instance:使用该指令的组件实例。
    • dir:指令的定义对象。
  • vnode:代表绑定元素的底层 VNode。

  • prevVnode:代表之前的渲染中指令所绑定元素的 VNode。仅在 beforeUpdateupdated 钩子中可用。

四、组合式函数

Vue 3 中的组合式函数(Composables)是基于组合式 API 构建的一个逻辑复用机制,它允许你将组件的状态逻辑抽离成可重用的函数,这种方式比 Vue 2 的 Mixin、HOC 更清晰、更灵活、更易维护。

1、什么是组合式函数?

组合式函数 是一个以 useXxx() 命名的普通 JavaScript 函数,在其中使用 Vue 的组合式 API(如 ref, reactive, watch, computed 等),然后将这些逻辑封装并返回供多个组件使用。

简化理解就是:

"把多个组件中重复的响应式逻辑、状态、侦听器等代码封装成函数,复用它们。"

2、组合式函数的结构

一个组合式函数的基本结构如下:

复制代码
// useCounter.js
import { ref } from 'vue'

export function useCounter() {
  const count = ref(0)

  function increment() {
    count.value++
  }

  return {
    count,
    increment
  }
}
在组件中使用它:
<script setup>
import { useCounter } from './useCounter'

const { count, increment } = useCounter()
</script>

<template>
  <button @click="increment">点击:{{ count }}</button>
</template>

3、组合式函数的核心特征

特征 描述
✅ 可重用 不同组件可调用相同的逻辑函数
✅ 组合式 API 支持 内部可以使用任何 ref、reactive、watch、computed 等
✅ 与组件解耦 逻辑不再依赖组件上下文
✅ 类型推导友好 TypeScript 支持极好
✅ 易测试 普通函数,易于单元测试

4、组合式函数能做什么?

复制代码
1. 封装状态逻辑

export function useToggle(initial = false) {
  const state = ref(initial)
  const toggle = () => (state.value = !state.value)
  return { state, toggle }
}
2. 封装异步请求逻辑

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)

  fetch(url)
    .then(res => res.json())
    .then(json => (data.value = json))
    .catch(err => (error.value = err))

  return { data, error }
}
3. 封装监听器逻辑

export function useMouse() {
  const x = ref(0)
  const y = ref(0)

  function update(e) {
    x.value = e.pageX
    y.value = e.pageY
  }

  onMounted(() => window.addEventListener('mousemove', update))
  onBeforeUnmount(() => window.removeEventListener('mousemove', update))

  return { x, y }
}

5、实战场景汇总

场景 封装为组合式函数
表单验证 useFormValidation()
网络请求 useFetch(), useAxios()
用户状态管理 useUser()
页面权限控制 usePermission()
响应式缓存 useLocalStorage(), useSessionStorage()
主题/系统设置 useTheme(), useSystemConfig()
响应式计数器 useCounter()

6、总结一句话

组合式函数是 Vue 3 中最推荐的逻辑复用机制 ,它允许你把多个组件重复的状态、侦听器、生命周期逻辑提取为独立函数,提高代码复用性、可维护性和清晰度

五、插件

1、什么是 Vue 插件?

Vue 插件(Plugin)是一个可以通过 app.use() 方法注册到 Vue 应用中的扩展模块。

它通常用于:

  • 向应用注入功能(如添加全局方法、指令、组件等)

  • 对整个应用进行配置或初始化

  • 集成第三方库(如 Vue Router、Vuex、Pinia、Element Plus 等)

2、插件的核心格式

Vue 插件其实就是一个具有 install(app, options) 方法的对象或函数:

插件的标准写法(对象形式):

复制代码
// MyPlugin.js
export default {
  install(app, options) {
    // 1. 添加全局方法
    app.config.globalProperties.$hello = () => {
      console.log('Hello from plugin!')
    }

    // 2. 注册全局组件
    app.component('MyGlobalComp', {
      template: `<div>I am global</div>`
    })

    // 3. 添加全局指令
    app.directive('focus', {
      mounted(el) {
        el.focus()
      }
    })

    // 4. 注入 provide/inject 数据
    app.provide('pluginName', 'MyPlugin')
  }
}

3、注册方式

复制代码
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import MyPlugin from './MyPlugin'

const app = createApp(App)
app.use(MyPlugin, { customOption: true }) // 安装插件
app.mount('#app')

4、插件内部能做的事(功能总结)

功能 用法
注册全局组件 app.component('Xxx', Component)
注册全局指令 app.directive('xxx', definition)
添加全局属性 app.config.globalProperties.$xxx = fn
使用 provide/inject 传递全局依赖 app.provide('xxx', value)
启动外部库或执行初始化 如初始化配置项、加载配置文件等

5、注意事项

注意点 说明
插件必须使用 app.use() 安装 否则不会执行其 install() 方法
不要滥用全局属性 app.config.globalProperties 会污染组件上下文
插件之间避免命名冲突 推荐用命名空间前缀(如 $myXxx
插件应保持纯函数和无副作用 避免影响应用的稳定性

6、总结一句话

Vue 插件是通过 app.use() 安装的功能扩展模块,适合封装和共享全局逻辑,如注册组件、添加方法、集成第三方库等,是构建大型项目或封装组件库的重要机制。

相关推荐
买了一束花23 分钟前
二、机器学习中Python变量基础
开发语言·python·机器学习·conda
heyCHEEMS30 分钟前
最大子段和 Java
java·开发语言·算法
magic 2451 小时前
Spring 命名空间注入:p、c 与 .util 的深度解析
java·前端·spring
钢铁男儿1 小时前
Python基本语法(函数partial)
前端·javascript·python
风清云淡_A1 小时前
【angular19】入门基础教程(三):关于angular里面的响应式数据入门使用
前端·angular.js
green_pine_2 小时前
Vue3学习笔记2——路由守卫
前端·vue.js·笔记·学习
bj32812 小时前
树的同构问题--Python
开发语言·python·算法
空中湖2 小时前
纯前端专业PDF在线浏览器查看器工具
前端·pdf
七灵微2 小时前
ES6入门---第二单元 模块二:关于数组新增
前端·javascript·es6
八股文领域大手子2 小时前
单机 vs 分布式:Java 后端限流的选择题
java·开发语言·数据结构·算法·spring