在 Vue 中,组件的自定义标签是组件化开发的核心概念之一,通过自定义标签可以将页面拆分为可复用的独立模块。以下是其用法和示例:

一、自定义标签的基本用法

  1. 定义组件:先创建一个组件(可以是全局组件或局部组件)。
  2. 使用自定义标签:在模板中通过组件名作为标签名使用(需遵循 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)的核心意义:
  1. 代码聚合,维护高效一个组件的结构、逻辑、样式不再分散在多个文件中(比如 HTML 模板写在 <script> 字符串里,CSS 写在全局样式表中),而是集中在 .vue 文件内。开发时无需在多个文件间切换,后期维护时能快速定位组件的所有相关代码。

    例如,修改一个组件的样式时,直接在 .vue 的 <style> 中修改,不用担心影响其他组件(通过 scoped 特性);修改逻辑时,直接在 <script> 中调整,无需查找关联的 JS 文件。

  2. 样式隔离,避免冲突.vue 文件的 <style> 标签支持 scoped 属性,能让样式仅作用于当前组件的 DOM,避免全局样式污染。这是传统方式(如在全局 CSS 中写样式)很难优雅解决的问题。

    示例:

    vue

    <style scoped>
    /* 仅作用于当前组件的 .title 元素 */
    .title {
      color: red;
    }
    </style>
    

    即使其他组件也有 .title 类名,也不会相互影响 —— 这才是 <style> 部分的关键价值,而 class 只是样式的载体,可有可无。

  3. 原生支持 Vue 特性,开发体验更好

    • 模板部分(<template>)无需写在字符串里,支持 HTML 语法高亮、自动补全,避免引号转义等问题。
    • 逻辑部分(<script>)可以直接导出组件选项,支持 ES6 模块语法(import/export),便于组件复用和依赖管理。
    • 配合 Vue CLI、Vite 等工具,可实现热重载(修改代码后页面实时更新),极大提升开发效率。
  4. 工程化适配,适合大型项目在大型项目中,组件数量可能成百上千。单文件组件的模块化结构能完美适配工程化工具(如 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 逻辑的框架,需要在两者之间做一层 “自动转换”,让开发者既能符合各自的规范,又不用手动处理命名差异。

具体规则:

  1. 父组件模板中(HTML 环境):传递给子组件的属性名必须用 kebab-case(短横线分隔),例如 parent-msg

  2. 子组件的 props 中(JavaScript 环境):接收属性时必须用 camelCase(驼峰式),例如 parentMsg

  3. 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)”。

举例对比:

  1. 监听 DOM 原生事件(如点击事件):

    vue

    <!-- 简写形式(推荐) -->
    <button @click="handleClick">点击</button>
    
    <!-- 完整形式 -->
    <button v-on:click="handleClick">点击</button>
    
  2. 监听组件自定义事件(如子组件触发的 button-clicked):

    vue

    <!-- 简写形式(推荐) -->
    <child-component @button-clicked="handleChildClick"></child-component>
    
    <!-- 完整形式 -->
    <child-component v-on:button-clicked="handleChildClick"></child-component>
    

@ 的设计目的是简化事件监听的写法,让模板更简洁。在实际开发中,几乎都会使用 @ 而非完整的 v-on: 形式,但两者功能完全一致

  1. 避免与 HTML 内置标签冲突:不要使用 divspan 等原生标签名作为组件名。

通过自定义标签,Vue 实现了组件的复用和模块化,使代码结构更清晰、维护更高效

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐