Skip to content

组件介绍 👌

要点速览

  • 单文件组件(SFC)由模板、逻辑、样式三部分组成。
  • <script setup> 更简洁,自动向模板暴露变量与组件。
  • 注册方式:全局注册适合基础/通用组件;业务组件推荐局部注册。
  • 样式作用域:<style scoped> 仅影响当前组件;跨组件样式穿透用 :deep() 伪类。
  • 组件名推荐大驼峰;原生 DOM 模板中必须使用短横线命名。
  • 组件三大核心:Props、事件(emit)、插槽;是组件通信与扩展的关键。

组件结构

在 Vue 中支持单文件组件(SFC),一个文件对应一个组件(后缀 .vue)。组件包含模板、逻辑与样式三部分:

vue
<template>
  <button @click="count++">Count is: {{ count }}</button>
  <p>这是一段组件内部的内容</p>
  <!-- 可以有多个根节点(Vue 3) -->
  <span>Another root</span>
</template>

<script setup>
import { ref } from "vue";
const count = ref(0);
</script>

<style scoped>
button {
  padding: 15px;
}
</style>
setup 语法糖的优势
  • 无需 return 暴露变量与方法,模板可直接使用。
  • 更佳的类型推断与 IDE 体验(TS 友好)。
  • 支持顶级 await,简化异步初始化逻辑。
  • 少模板代码(boilerplate),更易读更易维护。

如果你仍使用传统 Options API,也完全兼容:

vue
<script>
import { ref } from "vue";
export default {
  setup() {
    const count = ref(0);
    function add() {
      count.value++;
    }
    return { count, add };
  },
};
</script>

样式作用域要点

  • scoped 会为当前组件的元素添加独特属性选择器,样式不外泄。
  • 想影响子组件内部结构时,使用 :deep(.child) 进行穿透。
  • 设计系统/全局主题请放到全局样式或 CSS 变量中。

除了 .vue 文件,也可以使用对象形式定义组件(适合示例或简单场景):

js
export default {
  setup() {
    const count = ref(0);
    return { count };
  },
  template: `<div>{{ count }}</div>`,
};

在原生 DOM 模板中(例如挂到 <template id="..."> 的字符串模板),同样可以使用:

html
<template id="my-template-element">
  <div>
    <h1>{{ count }}</h1>
    <button @click="count++">Increment</button>
  </div>
  <!-- 注意:此处是原生 DOM 模板 -->
</template>
<script>
  const { createApp, ref } = Vue;
  const App = {
    setup() {
      const count = ref(0);
      return { count };
    },
    template: "#my-template-element",
  };
  createApp(App).mount("#app");
</script>

组件注册

组件注册分为两种:全局注册局部注册

全局注册

使用应用实例的 app.component() 方法进行全局注册,注册后在任意组件内都可用:

js
import { createApp } from "vue";
import MyComponent from "./App.vue";

const app = createApp({});
app.component("MyComponent", MyComponent);

// 支持链式调用
app
  .component("ComponentA", ComponentA)
  .component("ComponentB", ComponentB)
  .component("ComponentC", ComponentC);

不要滥用全局注册

  • 难以进行 tree-shaking,打包体积增大。
  • 组件依赖关系不清晰,降低维护性与可读性。
  • 名称冲突风险更高(大型项目)。

局部注册(推荐)

在使用组件的文件中显式引入并注册,更利于依赖管理与体积优化:

vue
<template>
  <button @click="add">Count is: {{ count }}</button>
  <TestCom />
</template>

<script>
import { ref } from "vue";
import TestCom from "./components/TestCom.vue";

export default {
  components: { TestCom },
  setup() {
    const count = ref(0);
    function add() {
      count.value++;
    }
    return { count, add };
  },
};
</script>

<script setup> 中更简单,导入即用(自动注册到模板):

vue
<template>
  <button @click="add">Count is: {{ count }}</button>
  <TestCom />
</template>

<script setup>
import { ref } from "vue";
import TestCom from "./components/TestCom.vue";

const count = ref(0);
function add() {
  count.value++;
}
</script>

推荐实践

  • UI 库或极通用的基础组件可考虑全局注册(如 BaseButton)。
  • 业务组件保持局部注册,清晰展现依赖关系,利于按需打包。

组件名

推荐使用 大驼峰(PascalCase) 作为组件名,例如:UserCardTaskList

模板中的大小写差异

  • 单文件组件模板中,<UserCard /><user-card /> 都可使用。
  • 原生 DOM 模板或字符串模板中,只能使用短横线命名<user-card></user-card>

示例:

html
<!-- 单文件组件模板中:两种写法均可 -->
<UserCard />
<user-card />

<!-- 原生 DOM 模板(如通过字符串 template 挂载):只能短横线 -->
<user-card></user-card>

常见误区与排查

常见误区

  • <script setup> 中未导入子组件却直接在模板使用,导致组件未定义错误。
  • scoped 样式中直接选择子组件内部元素,样式不生效;需要 :deep() 深度选择器伪类
  • 全局注册后使用 PascalCase 组件名在原生模板中不生效;应改为短横线。
:deep() 使用示例
vue
<style scoped>
.card {
  border: 1px solid #eee;
}
/* 影响子组件内部元素 */
:deep(.child-title) {
  font-weight: bold;
}
</style>

小结与后续

组件在应用层面,有三个核心知识点:

  1. Props(父 → 子传递数据)
  2. 自定义事件(子 → 父反馈与通信)
  3. 插槽(结构与内容扩展)

学习建议

  • 先掌握 SFC 结构、<script setup> 与局部注册。
  • 随后系统学习 Props、事件与插槽,理解组件通信与扩展模式。
  • 最后关注样式作用域与跨组件样式组织,提升可维护性。