Vue学习笔记 - 深入组件 - 注册,透传Attribute,异步组件

学完互动教程,已经有个基本印象,再看看文档,基础部分有涉及了,所以看深入组件那部分文档

注册

全局注册

可以使用 Vue 应用实例的 .component() 方法,让组件在当前 Vue 应用中全局可用

1
2
3
4
5
6
7
8
9
10
11
12
import { createApp } from 'vue'

const app = createApp({})

app.component(
// 注册的名字
'MyComponent',
// 组件的实现
{
/* ... */
}
)

如果使用单文件组件,你可以注册被导入的 .vue 文件:

1
2
3
import MyComponent from './App.vue'

app.component('MyComponent', MyComponent)

.component() 方法可以被链式调用:

全局注册的组件可以在此应用的任意组件的模板中使用

局部注册

全局注册的问题

  1. 打包的时候,没有用到的组件也不会被移除(tree-shaking)
  2. 全局注册在大型项目中使项目的依赖关系变得不那么明确

局部注册的组件需要在使用它的父组件中显式导入,并且只能在该父组件中使用。它的优点是使组件之间的依赖关系更加明确,并且对 tree-shaking 更加友好

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
import ComponentA from './ComponentA.vue'

export default {
components: {
ComponentA
}
}
</script>

<template>
<ComponentA />
</template>

昨天学的互动教程里的写法就是局部注册的方式

组件名格式

使用 PascalCase 作为组件名的注册格式

PascalCase 就是大驼峰的命名

  1. PascalCase 是合法的 JavaScript 标识符。这使得在 JavaScript 中导入和注册组件都很容易,同时 IDE 也能提供较好的自动补全。
  2. <PascalCase /> 在模板中更明显地表明了这是一个 Vue 组件,而不是原生 HTML 元素。同时也能够将 Vue 组件和自定义元素 (web components) 区分开来

这两点确实有道理

透传 Attributes

Attributes 继承

“透传 attribute”指的是传递给一个组件,却没有被该组件声明为 propsemitsattribute 或者 v-on 事件监听器。最常见的例子就是 classstyleid

当一个组件以单个元素为根作渲染时,透传的 attribute 会自动被添加到根元素

表述有点书面化,但是看完例子后非常容易理解

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 比如 <MyButton> 的模板是下面这样的 -->
<button>Click Me</button>

<!-- 父组件中使用MyButton组件 -->
<MyButton class="large" />

<!-- 最终渲染的结果: 父组件中声明的class属性透传到MyButton组件了 -->
<button class="large">Click Me</button>

<!-- 如果原来MyButton组件里已经有class属性,最后结果相当于把父组件里的属性再追加上去 -->
<button class="btn">Click Me</button>

<button class="btn large">Click Me</button>

这里有个问题,如果是id怎么办,也是直接加上去?

打开创建的前几天创建的VueDemo工程

1
2
# 报权限错误可以加sudo
npm run dev

App.vueHelloworld.vue的父组件,而且Helloworld.vue 满足单个元素作为根渲染

App.vue 添加id属性

1
2
3
4
5
6
7
8
9
10
11
12
13
...
<HelloWorld msg="You did it!" id="new_attr_id" />

...
<style scoped>
<!-- 如果结果是父组件,则是蓝色 -->
#new_attr_id {
color: blue;
}
#attr_id {
color: green;
}
...

Helloworld.vue

1
2
3
4
...
<div class="greetings" id="attr_id">
...

最终结果显示了父组件

Snip20250409_18.png

去掉父组件里的id,则显示子组件的id

Snip20250409_19.png

小结:

  1. Attribute 透传: 子组件根节点加样式,一般适用于class,id,style。其中class是追加的,而id是使用父组件替换掉子组件

依赖注入

应用层 Provide

1
2
3
4
5
import { createApp } from 'vue'

const app = createApp({})

app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!')

Prop 逐级透传问题

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

17441674003760.jpg

注意,虽然这里的 <Footer> 组件可能根本不关心这些 props,但为了使 <DeepChild> 能访问到它们,仍然需要定义并向下传递。如果组件链路非常长,可能会影响到更多这条路上的组件。这一问题被称为“prop 逐级透传”,显然是我们希望尽量避免的情况。

provideinject 可以帮助我们解决这一问题

17441674841802.jpg

示例图已经比较清楚说明了ProviderInject的用途了

完整示例

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
import Child from './Child.vue'

export default {
components: { Child },
provide() {
return {
message: '传给孙子组件'
}
}
}
</script>

<template>
<Child />
</template>

Child.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
import GrandChild from './GrandChild.vue'

export default {
<!-- 局部注册子组件 -->
components: {
GrandChild
}
}
</script>

<template>
<GrandChild />
</template>

GrandChild.vue

1
2
3
4
5
6
7
8
9
10
11
12
<script>
export default {
<!-- 声明使用父组件传下来的message -->
inject: ['message']
}
</script>

<template>
<p>
Message to grand child: {{ message }}
</p>
</template>

2025-04-09 11.07.39.png

异步组件

基本用法

在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。Vue 提供了 defineAsyncComponent 方法来实现此功能:

1
2
3
4
5
6
7
8
9
import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
// ...从服务器获取组件
resolve(/* 获取到的组件 */)
})
})
// ... 像使用其他一般组件一样使用 `AsyncComp`

导入单文件组件

1
2
3
4
5
import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
import('./components/MyComponent.vue')
)

最后得到的 AsyncComp 是一个外层包装过的组件,仅在页面需要它渲染时才会调用加载内部实际组件的函数。它会将接收到的 props 和插槽传给内部组件,所以你可以使用这个异步的包装组件无缝地替换原始组件,同时实现延迟加载。

加载与错误状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const AsyncComp = defineAsyncComponent({
// 加载函数
loader: () => import('./Foo.vue'),

// 加载异步组件时使用的组件
loadingComponent: LoadingComponent,
// 展示加载组件前的延迟时间,默认为 200ms
delay: 200,

// 加载失败后展示的组件
errorComponent: ErrorComponent,
// 如果提供了一个 timeout 时间限制,并超时了
// 也会显示这里配置的报错组件,默认值是:Infinity
timeout: 3000
})

最后的感想

Vue的文档很清晰,导致这部分大多数是”转译”下原来的文档,价值寥寥,只能算熟悉框架了…..

耐心点,慢慢来

参考

Vue学习笔记 - 插件 Vue学习笔记 - 互动教程 02
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×