Vue导入和注册组件

文章目录

环境

  • 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>

可见,先导入模块,再注册组件,然后就可以使用组件了。

注:为了方便,本例使用了 AAABBB 的命名方式,关于命名规范,下面有详述。

在项目根目录下,运行 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 导出和两个命名导出( AAAPI )。

相应的,要想导入 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 )里提到:

全局注册虽然很方便,但有以下几个问题:

  1. 全局注册,但并没有被使用的组件无法在生产打包时被自动移除 (也叫"tree-shaking")。如果你全局注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的 JS 文件中。

  2. 全局注册在大型项目中使项目的依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性。

相比之下,局部注册的组件需要在使用它的父组件中显式导入,并且只能在该父组件中使用。它的优点是使组件之间的依赖关系更加明确,并且对 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> )区分

实际上, PascalCasecamelCasekebab-case 是互相兼容的,编译后,最终都转换成 kebab-case

注:HTML标签和属性名不区分大小写,浏览器会把任何大写的字符解释为小写。

总之,推荐使用 PascalCase 风格来给组件命名。

总结

  • Vue使用SFC(单文件组件)
  • 使用 export default 来导出组件
  • 使用子组件时,分三步走:导入、注册、使用
  • 优先使用局部注册方式
  • "动态组件"场景下,可以不注册组件(需满足一定条件)
  • 推荐使用 PascalCase 风格来给组件命名

参考

  • https://cn.vuejs.org/guide/essentials/component-basics.html
  • https://cn.vuejs.org/guide/components/registration.html
相关推荐
ct9782 小时前
Vue3 状态管理方案:Pinia 全指南
javascript·vue.js
见青..2 小时前
DedeCMS织梦5.7--CSRF漏洞复现
前端·网络安全·csrf·漏洞复现
浩星2 小时前
electron系列3:进程模型深度解析:主进程、渲染进程、预加载脚本
前端·electron·前端框架
恋猫de小郭2 小时前
手机直接运行 Codex/OpenCode/Claude Code ,实时管理你的 AI Coding
前端·openai·ai编程
Canace2 小时前
为什么不要让LLM帮我们写文档
前端·人工智能
之歆2 小时前
前端性能优化:从路由懒加载到打包优化
前端·性能优化
米丘2 小时前
ESTree 规范 (acorn@8.15.0示例)
前端·javascript·编译器
天下权2 小时前
OpenLayers 地图绘制与交互实战:从零构建一个完整的绘制系统
前端·gis
饺子不吃醋2 小时前
深入理解浏览器渲染流程
前端·javascript