Attributes 透传

透传 Attributes 指的是组件中未被显式声明(定义)为 props 的属性(如 idclassstyle、自定义属性等)会自动传递到组件的根元素或通过手动设置传递到指定的 DOM 元素的一种行为

这功能依赖于 Vue 的 $attrs 对象,它存储了所有未被组件声明为 props 的属性

默认透传

如果父组件向子组件传递了某些属性,而子组件没有在 props 中声明这些属性,那么这些属性会自动添加到子组件的根元素上

例:

1
2
3
4
5
6
7
8
<!-- src/components/Parent.vue -->
<template>
<Child id="main" class="highlight" custom-attr="test" />
</template>

<script setup>
import Child from './Child.vue'
</script>
1
2
3
4
<!-- src/components/Child.vue -->
<template>
<div>Child Content</div>
</template>
1
2
3
4
<!-- DOM渲染结果 -->
<div id="main" class="highlight" custom-attr="test">
Child Content
</div>

手动透传

如果组件有多个根节点,或者需要将透传的属性应用到某个特定的元素上,可以使用 $attrs

例:

1
2
3
4
5
6
7
8
<!-- src/components/Parent.vue -->
<template>
<Child id="main" class="highlight" custom-attr="test" />
</template>

<script setup>
import Child from './Child.vue'
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- src/components/Child.vue -->
<template>
<div>
<h1>Title</h1>
<div v-bind="$attrs">Child Content</div> <!-- 将属性绑定到指定元素 -->
</div>
</template>

<script setup>
import { useAttrs } from 'vue';

const attrs = useAttrs(); // 获取 $attrs 对象
console.log(attrs); // { id: "main", class: "highlight", custom-attr: "test" }
</script>
1
2
3
4
5
6
7
<!-- DOM渲染结果 -->
<div>
<h1>Title</h1>
<div id="main" class="highlight" custom-attr="test">
Child Content
</div>
</div>

阻止默认透传

如果不希望未声明的属性透传到组件的根元素,可以通过 inheritAttrs: false 禁止默认透传

例:

1
2
3
4
5
6
7
8
9
10
<!-- src/components/Child.vue -->
<template>
<div>Child Content</div>
</template>

<script>
export default {
inheritAttrs: false, // 禁止默认透传, Vue 不会自动将 $attrs 应用到根元素上, 但 $attrs 本身仍然可以手动使用
};
</script>
1
2
3
4
5
6
7
8
<!-- src/components/Parent.vue -->
<template>
<Child id="main" class="highlight" />
</template>

<script setup>
import Child from './Child.vue'
</script>
1
2
3
<!-- DOM渲染结果 -->
<div>Child Content</div>
<!-- 尽管父组件传递了 id 和 class,但它们不会出现在子组件中 -->

用途:

  • HTML属性透传:动态传递 HTML 元素属性
  • 事件透传:配合 v-on="$attrs",可以将所有未声明的事件绑定到子组件的元素上
  • 透传到指定元素上:如果组件需要将父组件的传递属性分发到非根节点,可以结合 $attrsv-bind 使用

透传机制增强了组件的灵活性,避免了硬编码特定的 props,同时也提供了精细化的透传控制

Slots 插槽

Lifecycle 组件生命周期

组件生命周期是指组件从创建、挂载、更新到卸载的一系列过程。每个阶段都有特定的生命周期钩子函数供开发者执行相关逻辑

简单的说生命周期函数就是会在某一时刻由 Vue 自动执行的函数

组件生命周期分为以下几个阶段:

阶段钩子函数( 选项式API | 组合式API )描述
创建阶段beforeCreate | 无组件实例创建之前执行,此时无法访问到 propsdata
created | setup组件实例创建完成后执行,此时可以访问到 propsdata
挂载阶段beforeMount | onBeforeMount组件挂载到 DOM 之前执行,此时组件实例已经生成了虚拟DOM,但还没有被渲染到实际的DOM元素上
mounted | onMounted组件挂载到 DOM 之后执行,此时组件实例已经被渲染到了实际的DOM元素上
更新阶段beforeUpdate | onBeforeUpdate组件更新之前执行,在此阶段,组件的数据(响应式数据)已经发生了变化,但是DOM节点还没有被重新渲染(未更新视图)
updated | onUpdated组件更新完成后执行,在此阶段,组件的数据(响应式数据)发生了变化,也已经重新渲染了DOM节点(已更新视图)。
卸载阶段beforeUnmount | onBeforeUnmount组件卸载之前执行,在此阶段,组件即将被销毁
unmounted | onUnmounted组件卸载完成后执行,在此阶段,组件已经被销毁,无法访问到组件实例和DOM元素

使用场景:

例1 数据初始化:

1
2
3
4
5
6
7
8
9
10
11
<!-- 在 created 或 onMounted 钩子中执行数据初始化,例如发送网络请求 -->
<script setup>
import { onMounted, ref } from 'vue';

const data = ref([]);

onMounted(async () => {
const response = await fetch('https://api.example.com/data');
data.value = await response.json();
});
</script>

例2 操作DOM:

1
2
3
4
5
6
7
8
9
<!-- 如果需要操作真实DOM,我们可以在 mounted 或 onMounted 狗子中进行 -->
<script setup>
import { onMounted } from 'vue';

onMounted(() => {
const button = document.querySelector('button')
button.addEventListener('click', () => alert('Clicked!'))
})
</script>

例3 事件监听与清理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 在 mounted 或 onMounted 中添加事件监听器,在 unmounted 或 onUnmounted 中清理 -->
<script setup>
import { onMounted, onUnmounted } from 'vue';

onMounted(() => {
window.addEventListener('resize', onResize);
});

onUnmounted(() => {
window.removeEventListener('resize', onResize);
});

const onResize = () => {
console.log('窗口尺寸发生变化');
};
</script>

例4 清理定时器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 在 beforeUnmount 或 onUnmounted 中清理定时器 -->
<script setup>
import { onMounted, onUnmounted, ref } from 'vue';

const count = ref(0);
let timer;

onMounted(() => {
timer = setInterval(() => count.value++, 1000);
});

onUnmounted(() => {
clearInterval(timer);
});
</script>

开发中应根据场景选择合适的钩子,并在适当阶段清理资源,避免内存泄漏

Composables 组合式函数

组合式函数是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数,通常也被称为 Vue Hooks 函数。组合式函数允许将组件内的逻辑拆分并以独立的单元进行复用和组合,

自定义指令 directives

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.修饰符="值" or v-xxx:参数.修饰符="值"

    • binding.instance: 值为指令绑定时所在组件的组件实例

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">
<!-- input 元素自动获取焦点 -->
<input type="text" v-global-focus>
<!-- <input type="text" v-local-focus> -->
</div>

<script>
const { createApp } = Vue

const app = createApp({
// 局部自定义指令, 在组件的 directives 选项中定义
// 只在该组件内部可用
directives: {
localFocus: {
mounted(el) {
el.focus()
}
}
}
})
// 全局自定义指令, 通过 app.directive 方法定义
/*
自定义指令的命名
kebab-case命名 - 如:global-focus
CamelCase命名 - 如:globalFocus
使用:v-global-focus
*/
app.directive('globalFocus', {
mounted(el) {
el.focus()
}
})
const vm = app.mount('#app')
</script>