Vue.js (三) 进阶API
Attributes 透传
透传 Attributes 指的是组件中未被显式声明(定义)为 props
的属性(如 id
、class
、style
、自定义属性等)会自动传递到组件的根元素或通过手动设置传递到指定的 DOM 元素的一种行为
这功能依赖于 Vue 的 $attrs
对象,它存储了所有未被组件声明为 props
的属性
默认透传
如果父组件向子组件传递了某些属性,而子组件没有在 props
中声明这些属性,那么这些属性会自动添加到子组件的根元素上
例:
1 | <!-- src/components/Parent.vue --> |
1 | <!-- src/components/Child.vue --> |
1 | <!-- DOM渲染结果 --> |
手动透传
如果组件有多个根节点,或者需要将透传的属性应用到某个特定的元素上,可以使用 $attrs
例:
1 | <!-- src/components/Parent.vue --> |
1 | <!-- src/components/Child.vue --> |
1 | <!-- DOM渲染结果 --> |
阻止默认透传
如果不希望未声明的属性透传到组件的根元素,可以通过 inheritAttrs: false
禁止默认透传
例:
1 | <!-- src/components/Child.vue --> |
1 | <!-- src/components/Parent.vue --> |
1 | <!-- DOM渲染结果 --> |
用途:
- HTML属性透传: 动态传递 HTML 元素属性
- 事件透传: 配合
v-on="$attrs"
,可以将所有未声明的事件绑定到子组件的元素上 - 透传到指定元素上: 如果组件需要将父组件的传递属性分发到非根节点,可以结合
$attrs
和v-bind
使用
透传机制增强了组件的灵活性,避免了硬编码特定的 props
,同时也提供了精细化的透传控制
Slots 插槽
Vue 插槽 (Slot) 是用来让父组件向子组件中传递结构化的 DOM 内容。它的作用就像“占位符”,父组件可以把内容填充到子组件的指定位置
默认插槽
例:
子组件中定义插槽位置:
1 | <!-- Child.vue --> |
父组件使用:
1 | <template> |
渲染 DOM 结果:
1 | <div class="child"> |
具名插槽
通过 <slot>
上的 name 属性区分插槽,通过 v-slot:<插槽名>
将 html 插入到指定插槽
例:
子组件:
1 | <!-- Child.vue --> |
父组件:
1 | <Child> |
作用域插槽
插槽不仅能传结构,还能传数据。子组件可以向插槽暴露一些数据,父组件可以接收并渲染
例:
子组件:
1 | <!-- Child.vue --> |
父组件:
1 | <Child v-slot="{ msg }"> |
渲染 DOM 结果:
1 | <div> |
Lifecycle 组件生命周期
组件生命周期是指组件从创建、挂载、更新到卸载的一系列过程。每个阶段都有特定的生命周期钩子函数( Hooks Function )供开发者执行相关逻辑
简单的说生命周期函数就是会在某一时刻由 Vue 自动执行的函数
组件生命周期分为以下几个阶段:
阶段 | 钩子函数( 选项式API | 组合式API ) | 描述 |
---|---|---|
创建阶段 | beforeCreate | 无 | 组件实例创建之前执行,此时无法访问到 props 和 data |
created | setup | 组件实例创建完成后执行,此时可以访问到 props 和 data | |
挂载阶段 | beforeMount | onBeforeMount | 组件挂载到 DOM 之前执行,此时组件实例已经生成了虚拟DOM,但还没有被渲染到实际的DOM元素上 |
mounted | onMounted | 组件挂载到 DOM 之后执行,此时组件实例已经被渲染到了实际的DOM元素上 | |
更新阶段 | beforeUpdate | onBeforeUpdate | 组件更新之前执行,在此阶段,组件的数据(响应式数据)已经发生了变化,但是DOM节点还没有被重新渲染(未更新视图) |
updated | onUpdated | 组件更新完成后执行,在此阶段,组件的数据(响应式数据)发生了变化,也已经重新渲染了DOM节点(已更新视图)。 | |
卸载阶段 | beforeUnmount | onBeforeUnmount | 组件卸载之前执行,在此阶段,组件即将被销毁 |
unmounted | onUnmounted | 组件卸载完成后执行,在此阶段,组件已经被销毁,无法访问到组件实例和DOM元素 |
生命周期钩子使用场景
例1 数据初始化:
1 | <!-- 在 created 或 onMounted 钩子中执行数据初始化,例如发送网络请求 --> |
例2 操作DOM:
1 | <!-- 如果需要操作真实DOM,我们可以在 mounted 或 onMounted 狗子中进行 --> |
例3 事件监听与清理:
1 | <!-- 在 mounted 或 onMounted 中添加事件监听器,在 unmounted 或 onUnmounted 中清理 --> |
例4 清理定时器:
1 | <!-- 在 beforeUnmount 或 onUnmounted 中清理定时器 --> |
开发中应根据场景选择合适的钩子,并在适当阶段清理资源,避免内存泄漏
Composables 组合式函数
组合式函数是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数,通常也被称为 Vue Hooks 函数。组合式函数允许将组件内的逻辑拆分并以独立的单元进行复用和组合
例:
封装鼠标位置监听:
1 | // useMousePosition.js |
在组件中使用:
1 | <script setup> |
组合式函数最佳实践
优先封装逻辑到组合式函数:
- 如果某段逻辑可能被多处使用(如窗口监听、数据请求、定时器管理),优先封装为组合式函数
- 保持组件内的代码精简,集中于组件本身的职责
清晰命名:
- 组合式函数通常以
useXxx
的命名规范命名 - 组合式函数的名称应体现其用途,如
useMousePosition
- 组合式函数通常以
避免滥用:
- 组合式函数应用于逻辑复用或复杂功能,不适合将简单逻辑(如计算属性)过度抽象
自定义指令(Custom Directive)
Vue 提供了自定义指令的功能,用于给 HTML 元素添加额外的行为。自定义指令可以在模板中直接使用,并在指令的生命周期中执行相应的操作
指令的生命周期函数:
beforeMount
: 在指令绑定的元素挂载 DOM 之前被调用mounted
: 在指令绑定的元素被插入到 DOM 中后被调用beforeUpdate
: 在指令所在的组件更新之前被调用updated
: 在指令所在的组件更新之后被调用beforeUnmount
: 在指令所在的组件卸载之前被调用unmounted
: 在指令所在的组件卸载之后被调用
每个钩子函数都包含以下参数:
el
: 指令所绑定的元素binding
: 值为一个对象,用于获取指令绑定的相关信息,其中包括:binding.value
: 指令绑定的值,该值就是指令等于号( = )后面的值,语法:v-xxx="值"
binding.oldValue
: 指令上一次绑定的值( = )binding.arg
: 指令绑定的参数, 参数就是指令冒号( : )后面的内容,语法:v-xxx:参数="值"
binding.modifiers
: 指令绑定的修饰符,修饰符是以点开头的特殊后缀,用于给指令添加额外功能和修改行为,语法:v-xxx.修饰符="值"
orv-xxx:参数.修饰符="值"
binding.instance
: 值为指令绑定时所在组件的组件实例
例:
1 | /* src/main.js */ |
在模板中使用 focus 指令:
1 | <template> |
插件(Plugins)
插件是用来增强 Vue 应用功能的一种机制,它不是单纯的一个组件或函数,而是一种批量注册功能,全局注入能力的工具
插件的作用
插件通常用来为 Vue 添加全局功能,如:
- 全局注册组件(不需要在每个文件里 import)
- 全局指令(如 v-focus、v-permission)
- 全局方法 / 属性(如
$message
、$confirm
)
Vue 的官方库或部分第三方库都是基于 Vue 插件机制封装的,如:
- 官方:Vue Router、Vuex
- 第三方:国际化 i18n、Element Plus、Axios
插件的结构
一个 Vue 插件只要暴露一个 install 方法就行,Vue 在 app.use()
时会调用它
1 | // myPlugin.js |
1 | // main.js |
ref 模板引用
ref
是模板上的一个特殊 attribute,用于从模板中访问对应的 DOM 元素或子组件实例
DOM 元素引用
1 | <template> |
nextTick 响应式数据更新到 DOM 后的等待函数:
1 | import { ref, nextTick } from 'vue' |
子组件实例引用(配合 defineExpose 宏)
在子组件使用 <script setup>
时,默认是私有的;父组件想通过 ref
调用其方法,必须由子组件用 defineExpose()
显式暴露。并且要在任何 await
语法之前调用 defineExpose()
,否则会报错
例:
1 | <!-- 子组件 Child.vue --> |
1 | <!-- 父组件 Parent.vue --> |
Tips:
- 只能在挂载后访问模板引用,在挂载之前访问结果会是
null/undefined
v-if
切换为false
会把元素/子组件卸载,对应引用会回到null
,可以使用 ES6 可选链 (.?
) 防止空值报错
动态组件(<component>
)
缓存组件(KeepAlive)
过度与动画
当一个元素进入/离开页面,或者某个状态发生变化时,我们希望它平滑变化,而不是瞬间切换,Vue 为了让这种效果更简单,内置了 <transition>
和 <transition-group>
组件,它可以实现元素 进入/离开时的过渡/动画 和 列表的过渡/动画
<Transition>
组件:包裹需要动画效果的单个元素-
<transition-group>
:包裹需要动画效果的多个元素,每个元素都需要配置 Key 属性
Vue 在元素 插入、移除 时,会自动给它加上不同阶段的 class ( 类名 ),你只需要定义对应的 CSS 即可
不同时刻样式类名:
控制元素进入的样式
v-enter-from
进入的起点v-enter-active
进入过程中v-enter-to
进入的终点
控制元素离开的样式
v-leave-from
离开的起点v-leave-active
离开过程中v-leave-to
离开的终点
例1 transition 过度:
1 | <transition name="fade"> |
例2 animation 动画:
1 | <transition name="bounce"> |
过度/动画的区别与联系
- 过渡:适合简单的“从 A 到 B”变化(淡入、移动、缩放等),依赖 CSS 的
transition
- 动画:适合复杂的连续动作,可以自定义关键帧(CSS
@keyframes
),依赖 CSS 的animation
/@keyframes
- Vue
Transition
组件同时支持过渡和动画,只要你写对应的 CSS 即可