文章目录
环境
- Ubuntu 24.04
- Chrome Version 146.0.7680.164 (Official Build) (64-bit)
- VSCode 1.109.5
- npm 11.6.2
- Vue 3.5.32
准备
创建一个Vue项目。创建过程略,具体可参见 https://blog.csdn.net/duke_ding2/article/details/159510007 。
单文件组件(SFC)
Vue推荐每个组件定义在一个单独的 .vue 文件中,这叫做单文件组件(Single File Component,SFC)。
例如,在 src 目录下,创建 ButtonCounter.vue 文件如下:
javascript
<script>
export default {
data() {
return {
count: 0
}
}
}
</script>
<template>
<button @click="count++">You clicked me {{ count }} times.</button>
</template>
该组件实现了"点击按钮,计数加1"的功能。
在 src 目录下,修改 App.vue 文件如下:
javascript
<script>
import AAA from './ButtonCounter.vue' // 导入模块
export default {
// 注册组件
components: {
BBB: AAA, // 如果同名,则可以简写
}
}
</script>
<template>
<BBB />
</template>
可见,先导入模块,再注册组件,然后就可以使用组件了。
注:为了方便,本例使用了 AAA 、 BBB 的命名方式,关于命名规范,下面有详述。
在项目根目录下,运行 npm run dev :
bash
npm run dev
然后打开浏览器查看效果:

每点击一次按钮,计数就会加1。
细节
构建环境和非构建环境
上例中,使用了构建环境。如果是在非构建环境下,则子组件可以以纯JavaScript对象来定义。例如,(在项目之外)创建 ButtonCounter.js 文件如下:
javascript
export default {
data() {
return {
count: 0
}
},
template: `
<button @click="count++">
You clicked me {{ count }} times.
</button>
`
}
注意:这是一个纯JS文件,不涉及Vue范畴。
(在项目之外)创建 index.html 如下:
html
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">
<aaa/>
</div>
<script type="module">
import MyButtonCounter from './ButtonCounter.js'
Vue.createApp({
components: {
'aaa': MyButtonCounter
}
}).mount('#app')
</script>
注意:这里有两段script,其中第一段脚本全局引入了Vue,而第二段脚本则是引入了其它模块,所以其自身也必须是模块。当然,也可以把第一段脚本修改一下,改为使用模块化方式引入Vue,这样就可以把两段脚本合并在一起了。
注:在非构建环境下,使用模块时,需要显式加上 type="module" 。如果是构建环境,则无需加上 type="module" ,Vue会自动识别。
另:由于引入了模块,所以无法直接通过 file:// 方式访问(同源策略限制),只能在web服务器下访问。在VSCode中启动"Five Server":

定位到 index.html ,就可以看到效果了:

缺省导出和命名导出
先抛开Vue,对JavaScript来说,一个模块可以导出 default ,也可以命名导出。例如:
javascript
export default {
data() {
return {
count: 123
}
}
}
export const AAA = {
data() {
return {
count: 456
}
}
}
export const PI = 3.14
此例中,包含了一个 default 导出和两个命名导出( AAA 和 PI )。
相应的,要想导入 default 模块,则必须提供一个名字(可任意命名),而要想导入命名模块,则必须使用指定的名字。例如:
html
<script type="module">
import AAA from './test1.js' // 导入default模块
import {PI} from './test1.js' // 导入命名模块
import {AAA as BBB} from './test1.js' // 导入命名模块,并且起了个别名
alert(AAA.data().count + ", " + PI + ", " + BBB.data().count)
</script>
注意:在导入 default 模块时,可任意命名,而在导入命名模块时,只能用指定名字,且需要用大括号把名字括起来。
另:为避免冲突(如本例中的 AAA ),可以使用别名( AAA as BBB )。
效果如下:

在最前面的例子中,子组件导出时,使用的是 export default ,所以父组件在导入时可以任意命名。
不过,需要注意的是,在Vue的SFC里,一定要用 export default ,而不要用命名导出,这是因为Vue的编译器只认 export default 来合并 template (除非你不用构建环境,参见上一节)。
注册方式
局部注册
在前面的例子里,父组件里注册子组件,使用的是"局部注册"方式。
html
<script>
import {MyButtonCounter} from './ButtonCounter.vue'
export default {
components: {
BBB: MyButtonCounter,
}
}
</script>
<template>
<BBB />
</template>
局部注册是最常用的方式。
全局注册
如果子组件要用在多个页面里,又不想在每个父组件里都注册一遍,则可以考虑全局注册。
ButtonCounter.vue 不变:
html
<script>
export default {
data() {
return {
count: 0
}
}
}
</script>
<template>
<button @click="count++">You clicked me {{ count }} times.</button>
</template>
打开 main.ts ,如下:
typescript
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
修改如下:
typescript
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import MyButtonCounter from './ButtonCounter.vue' // 导入 MyButtonCounter 组件
const app = createApp(App)
// 全局注册组件
app.component('BBB', MyButtonCounter)
app.mount('#app')
在 App.vue 里就可以直接使用 BBB 组件了,如下:
html
<template>
<BBB />
</template>
注:全局注册的组件,也都可以在彼此内部使用。
不过,在Vue的官网( https://cn.vuejs.org/guide/components/registration.html )里提到:
全局注册虽然很方便,但有以下几个问题:
全局注册,但并没有被使用的组件无法在生产打包时被自动移除 (也叫"tree-shaking")。如果你全局注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的 JS 文件中。
全局注册在大型项目中使项目的依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性。
相比之下,局部注册的组件需要在使用它的父组件中显式导入,并且只能在该父组件中使用。它的优点是使组件之间的依赖关系更加明确,并且对 tree-shaking 更加友好。
所以,还是要慎用全局注册。
不注册(使用动态组件)
即使不注册子组件,父组件也可以通过 <component> 这个特殊元素来实现"动态组件":
javascript
<script>
import AAA from './ButtonCounter.vue'
export default {
data() {
return {
myvar: AAA // 组件名
}
}
}
</script>
<template>
<component :is="myvar" />
</template>
这种使用场景叫做"动态组件", is 属性的值是一个变量或者表达式,当其值变化时,实现动态切换组件的效果。
注意: myvar 的值可以是字符串,也可以是组件名。本例中是组件名(Vue可通过组件名直接找到该组件)。
而假如 myvar 的值是字符串,则必须先注册组件(因为Vue需要根据这个字符串,在components注册表中查找该组件):
javascript
<script>
import AAA from './ButtonCounter.vue'
export default {
components: {AAA}, // 必须注册组件
data() {
return {
myvar: 'AAA' // 字符串
}
}
}
</script>
<template>
<component :is="myvar" />
</template>
注意比较:
myvar: AAA:组件名myvar: 'AAA':字符串
所以,如果使用字符串方式,则必须注册组件。
如果使用组合式API,则更简单一些:
javascript
<script setup>
import AAA from './ButtonCounter.vue'
</script>
<template>
<component :is="AAA" />
</template>
注意本例中的 <script setup> 。如果不是组合式API,则这种方式无效。
总之,在动态组件场景里,在满足一定条件的情况下,无需注册组件。
命名
推荐使用 PascalCase 作为组件名的注册格式。
当一个字符串由多个单词拼接时,常见的格式如下:
PascalCase:每个单词的首字母大写,各个单词之间直接拼接。camelCase:字符串的首字母小写,其它与PascalCase相同kebab-case:每个单词的首字母小写,各个单词之间用连字符-拼接
使用 PascalCase 作为组件名的注册格式的原因:
PascalCase是合法的JavaScript标识符- 容易和原生HTML元素(如
<div>)区分
实际上, PascalCase 、 camelCase 、 kebab-case 是互相兼容的,编译后,最终都转换成 kebab-case 。
注:HTML标签和属性名不区分大小写,浏览器会把任何大写的字符解释为小写。
总之,推荐使用 PascalCase 风格来给组件命名。
总结
- Vue使用SFC(单文件组件)
- 使用
export default来导出组件 - 使用子组件时,分三步走:导入、注册、使用
- 优先使用局部注册方式
- "动态组件"场景下,可以不注册组件(需满足一定条件)
- 推荐使用
PascalCase风格来给组件命名
参考
https://cn.vuejs.org/guide/essentials/component-basics.htmlhttps://cn.vuejs.org/guide/components/registration.html