Vue 组件的自定义标签的用法和示例
在 Vue 中,组件的自定义标签是组件化开发的核心概念之一,通过自定义标签可以将页面拆分为可复用的独立模块。以下是其用法和示例:
一、自定义标签的基本用法
- 定义组件:先创建一个组件(可以是全局组件或局部组件)。
- 使用自定义标签:在模板中通过组件名作为标签名使用(需遵循 Vue 的命名规范)。
命名规范:
- 组件名推荐使用 kebab-case(短横线分隔)(如
my-component),在模板中直接作为标签使用。 - 若使用 PascalCase(大驼峰,如
MyComponent),在模板中既可以用<MyComponent>也可以用<my-component>(Vue 会自动转换)。
二、示例
1. 全局组件(可在整个项目中使用)
javascript
// 引入 Vue
import Vue from 'vue'
// 定义全局组件(组件名用 kebab-case)
Vue.component('my-global-component', {
template: `
<div class="global-component">
<h3>这是全局组件的自定义标签</h3>
<p>{{ message }}</p>
</div>
`,
data() {
return {
message: '全局组件的数据'
}
}
})
// 在任意 Vue 实例的模板中使用
new Vue({
el: '#app',
template: `
<div>
<!-- 使用全局组件的自定义标签 -->
<my-global-component></my-global-component>
</div>
`
})
2. 局部组件(仅在当前父组件中使用)
javascript
// 定义局部组件(组件名用 PascalCase)
const MyLocalComponent = {
template: `
<div class="local-component">
<h3>这是局部组件的自定义标签</h3>
<p>接收父组件的参数:{{ parentMsg }}</p>
</div>
`,
props: ['parentMsg'] // 接收父组件传递的参数
}
// 在父组件中注册并使用
new Vue({
el: '#app',
components: {
// 注册局部组件(键名可省略,默认与组件变量名一致)
MyLocalComponent
},
template: `
<div>
<!-- 使用局部组件的自定义标签(可混用 kebab-case) -->
<my-local-component parent-msg="来自父组件的消息"></my-local-component>
</div>
`
})
3. 单文件组件(.vue 文件,推荐用法)
MyComponent.vue(子组件):
vue
<template>
<div class="my-component">
<h2>{{ title }}</h2>
<button @click="handleClick">点击我</button>
</div>
</template>
<script>
export default {
name: 'MyComponent', // 组件名(PascalCase)
props: {
title: {
type: String,
default: '默认标题'
}
},
methods: {
handleClick() {
this.$emit('custom-event', '子组件触发的事件');
}
}
}
</script>
Parent.vue(父组件中使用):
vue
<template>
<div class="parent">
<!-- 使用自定义标签,并传递参数和监听事件 -->
<my-component
title="自定义标题"
@custom-event="handleChildEvent"
></my-component>
</div>
</template>
<script>
import MyComponent from './MyComponent.vue'; // 导入组件
export default {
components: {
MyComponent // 注册局部组件
},
methods: {
handleChildEvent(data) {
console.log(data); // 输出:子组件触发的事件
}
}
}
</script>
3.1单文件组件(.vue)的核心意义:
-
代码聚合,维护高效一个组件的结构、逻辑、样式不再分散在多个文件中(比如 HTML 模板写在
<script>字符串里,CSS 写在全局样式表中),而是集中在.vue文件内。开发时无需在多个文件间切换,后期维护时能快速定位组件的所有相关代码。例如,修改一个组件的样式时,直接在
.vue的<style>中修改,不用担心影响其他组件(通过scoped特性);修改逻辑时,直接在<script>中调整,无需查找关联的 JS 文件。 -
样式隔离,避免冲突
.vue文件的<style>标签支持scoped属性,能让样式仅作用于当前组件的 DOM,避免全局样式污染。这是传统方式(如在全局 CSS 中写样式)很难优雅解决的问题。示例:
vue
<style scoped> /* 仅作用于当前组件的 .title 元素 */ .title { color: red; } </style>即使其他组件也有
.title类名,也不会相互影响 —— 这才是<style>部分的关键价值,而class只是样式的载体,可有可无。 -
原生支持 Vue 特性,开发体验更好
- 模板部分(
<template>)无需写在字符串里,支持 HTML 语法高亮、自动补全,避免引号转义等问题。 - 逻辑部分(
<script>)可以直接导出组件选项,支持 ES6 模块语法(import/export),便于组件复用和依赖管理。 - 配合 Vue CLI、Vite 等工具,可实现热重载(修改代码后页面实时更新),极大提升开发效率。
- 模板部分(
-
工程化适配,适合大型项目在大型项目中,组件数量可能成百上千。单文件组件的模块化结构能完美适配工程化工具(如 Webpack、Vite 的打包、Tree-Shaking 等),让代码组织更规范,团队协作更顺畅。
举个对比:不用单文件组件的痛点
如果不使用 .vue 文件,而是用传统方式定义组件,可能会像这样:
javascript
运行
// MyComponent.js
export default {
template: `
<div>
<h2>{{ title }}</h2>
<button @click="handleClick">点击我</button>
</div>
`,
data() { return { title: '标题' } },
methods: { handleClick() {} }
}
// 全局样式.css
/* 这里的样式可能污染其他组件 */
h2 { color: red; }
这种方式的问题:
- 模板写在字符串中,开发时没有语法高亮,容易出错。
- 样式写在全局,容易冲突(比如其他组件的
h2也会被染红)。 - 组件的模板、逻辑、样式分散在不同文件,维护时需要频繁切换。
而单文件组件(.vue)通过 <template>、<script>、<style> 三个标签,将三者有机结合,完美解决了这些问题。
三、注意事项
1 闭合标签:
自定义标签可以是单标签(<my-component/>)或双标签(<my-component></my-component>),Vue 均支持
2 传递数据:
过 props 向子组件传递数据,模板中用 kebab-case(如 parent-msg)对应组件中 props 的 camelCase(如 parentMsg)。
详解:
Vue 中父组件向子组件传递数据时,属性命名风格的转换规则,核心是为了兼顾 HTML 特性的命名习惯和 JavaScript 的命名规范。
为什么需要这种转换?
- HTML 中的标签属性命名习惯是 kebab-case(短横线分隔,全小写),例如
<div data-user-name="xxx">,这是 HTML 规范的一部分。 - JavaScript 中变量 / 属性的命名习惯是 camelCase(驼峰式,首字母小写,后续单词首字母大写),例如
userName,这是 JS 的主流规范。
Vue 作为同时操作 HTML 模板和 JavaScript 逻辑的框架,需要在两者之间做一层 “自动转换”,让开发者既能符合各自的规范,又不用手动处理命名差异。
具体规则:
-
父组件模板中(HTML 环境):传递给子组件的属性名必须用 kebab-case(短横线分隔),例如
parent-msg。 -
子组件的 props 中(JavaScript 环境):接收属性时必须用 camelCase(驼峰式),例如
parentMsg。 -
Vue 会自动转换:父组件的
parent-msg会被 Vue 自动映射到子组件的parentMsg,开发者无需手动处理。
示例说明:
父组件(传递数据):
vue
<template>
<!-- 父组件模板中用 kebab-case:parent-msg -->
<child-component parent-msg="来自父组件的消息"></child-component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: { ChildComponent }
}
</script>
子组件(接收数据):
vue
<template>
<div>
<!-- 子组件模板中可以直接用 camelCase(parentMsg) -->
<p>{{ parentMsg }}</p>
</div>
</template>
<script>
export default {
// 子组件 props 中用 camelCase:parentMsg
props: ['parentMsg']
// 或者更详细的定义:
// props: {
// parentMsg: {
// type: String,
// default: ''
// }
// }
}
</script>
为什么必须这样做?
- 如果父组件用
parentMsg(驼峰)传递,HTML 会自动忽略大小写(把parentMsg解析为parentmsg),导致子组件的parentMsg接收不到数据(因为parentmsg和parentMsg不匹配)。 - 如果子组件用
parent-msg接收,JavaScript 中不允许变量名包含短横线(parent-msg会被视为语法错误)。
因此,Vue 设计了这种 “自动转换” 机制,既遵守了 HTML 和 JavaScript 的各自规范,又简化了开发者的操作。
总结:
- 父传子的属性名,在 HTML 模板中写
kebab-case(如parent-msg)。 - 子组件 props 中写
camelCase(如parentMsg)。 - 两者通过 Vue 自动映射,无需手动处理转换,直接使用即可
3 事件通信:
子组件通过 $emit 触发事件,父组件用 @事件名 监听
详解
@ 是 事件监听的语法糖(简写形式),等价于 v-on: 指令,专门用于监听 DOM 原生事件或组件自定义事件。
具体来说:
@button-clicked="handleChildClick"完全等价于v-on:button-clicked="handleChildClick"。- 这里的
@就是 “监听事件” 的标识符,告诉 Vue:“我要监听后面的事件名(如button-clicked),当事件触发时执行对应的处理函数(如handleChildClick)”。
举例对比:
-
监听 DOM 原生事件(如点击事件):
vue
<!-- 简写形式(推荐) --> <button @click="handleClick">点击</button> <!-- 完整形式 --> <button v-on:click="handleClick">点击</button> -
监听组件自定义事件(如子组件触发的
button-clicked):vue
<!-- 简写形式(推荐) --> <child-component @button-clicked="handleChildClick"></child-component> <!-- 完整形式 --> <child-component v-on:button-clicked="handleChildClick"></child-component>
@ 的设计目的是简化事件监听的写法,让模板更简洁。在实际开发中,几乎都会使用 @ 而非完整的 v-on: 形式,但两者功能完全一致
- 避免与 HTML 内置标签冲突:不要使用
div、span等原生标签名作为组件名。
通过自定义标签,Vue 实现了组件的复用和模块化,使代码结构更清晰、维护更高效
更多推荐


所有评论(0)