从零基础到最佳实践:Vue.js 系列(3/10):《组件化开发入门》

如果你觉得开发前端界面像拼一个超级复杂的拼图,那组件化开发就是你的"作弊码"------把大问题拆成小块,像搭乐高积木一样简单又有趣。本文将带你从零开始掌握 Vue.js 组件化开发的核心技能。我们会详细聊聊组件是什么、怎么注册、如何传递数据、怎么用插槽,甚至还有一些实用的小例子。不管你是刚入门还是想复习基础,这篇指南都会让你觉得:"原来这么简单!"我们基于 Vue 3,还会顺便介绍当下流行的工具和趋势。


1. 为什么要用组件化开发?

想象一下,你要建一座乐高城堡。如果每块砖、每扇窗都得从头捏,那得多累啊!但如果有了预制的积木,你只需要挑合适的拼起来就行了。Vue.js 的组件化开发就是这个思路:把页面拆成一个个小模块(组件),每个模块都能独立工作,还能重复使用。

组件化开发的好处

  • 省时省力:一个漂亮的按钮组件做好了,哪儿都能用,不用重复写代码。
  • 好维护:改一个组件,其他地方不受影响,找 bug 也更容易。
  • 思路清晰:每个组件只干一件事,代码看起来就像整理好的玩具盒。

在 Vue.js 里,组件是前端开发的"超级英雄",能让你的项目更高效、更整洁。


2. Vue.js 组件是什么?

简单来说,Vue.js 组件就是一个"积木块",里面装了三样东西:

  • 模板<template>):定义组件长什么样,比如按钮、卡片还是列表。
  • 逻辑<script>):写数据和功能,比如点击按钮做什么。
  • 样式<style>):让组件漂漂亮亮,通常加个 scoped 只影响自己。

举个例子:我们做一个简单的按钮组件,看看它长啥样:

vue 复制代码
<template>
  <button class="my-button">点我试试</button>
</template>

<style scoped>
.my-button {
  background-color: #4caf50;
  color: white;
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
}
</style>

这个按钮组件就是一个独立的小单位,做好了就能到处用。是不是很简单?


3. 组件怎么用?全局和局部注册

组件做好了,得告诉 Vue.js 怎么用它。这就涉及到"注册",有两种方式:全局注册局部注册。我们来一个一个看。

3.1 全局注册:哪儿都能用

全局注册就像把组件放进"公共玩具箱",整个项目都能拿来玩。适合那种特别常用的东西,比如按钮、输入框。

怎么做全局注册?

在项目入口(通常是 main.js)里写几行代码:

javascript 复制代码
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import MyButton from './components/MyButton.vue';

const app = createApp(App);
app.component('MyButton', MyButton); // 全局注册
app.mount('#app');

怎么用?

随便哪个组件的模板里,直接写 <MyButton /> 就行了。比如:

vue 复制代码
<template>
  <MyButton /> <!-- 直接用! -->
</template>

特点

  • 好处:到处都能用,省事儿。
  • 注意:别注册太多全局组件,不然容易名字撞车,也不好管理。
3.2 局部注册:专属小助手

局部注册就像给某个组件配个"私人玩具",只有这个组件能用。适合只在特定地方用的小模块。

怎么做局部注册?

直接在需要它的组件里导入,用 <script setup> 超简单:

vue 复制代码
<!-- Parent.vue -->
<template>
  <div>
    <MyLocalButton />
  </div>
</template>

<script setup>
import MyLocalButton from './MyLocalButton.vue'; // 局部导入
</script>

子组件长这样

vue 复制代码
<!-- MyLocalButton.vue -->
<template>
  <button>我是局部按钮</button>
</template>

全局 vs 局部,谁更好?

特点 全局注册 局部注册
使用范围 全项目随便用 只有导入的地方能用
怎么注册 main.js 里搞定 在组件里直接导入
适合场景 通用组件(如按钮) 专用组件(如某个页面独有)
管理难度 多了容易乱 更清晰,更模块化

小贴士

  • 平时多用局部注册,项目更整洁。
  • 组件名建议用大驼峰(MyButton),模板里用短横线(<my-button>)。

4. Props:给组件"喂"点数据

组件有了,怎么让它变得灵活?答案是 Props!Props 就像组件的"嘴巴",父组件可以通过它"喂"数据给子组件。

4.1 怎么用 Props?

子组件 :先声明要吃什么。

<script setup> 的话,超简单:

vue 复制代码
<!-- Greeting.vue -->
<template>
  <p>你好,{{ name }}!</p>
</template>

<script setup>
defineProps({
  name: String // 告诉 Vue 我要接收一个字符串
});
</script>

父组件:喂数据过去。

vue 复制代码
<template>
  <Greeting name="小明" />
</template>

<script setup>
import Greeting from './Greeting.vue';
</script>

结果:页面显示"你好,小明!"。

4.2 Props 还能更聪明

可以加点"规则",确保数据靠谱:

vue 复制代码
<template>
  <p>你好,{{ name }}!</p>
</template>

<script setup>
defineProps({
  name: {
    type: String, // 类型是字符串
    required: true, // 必须传
    default: '陌生人' // 没传就用这个
  }
});
</script>

实际用处

  • 传用户名、颜色、数字啥的。
  • 比如做一个商品卡片,传标题和价格。

注意

  • 子组件别改 Props,就像吃东西不能吐回去。
  • Props 名用小驼峰(userName),模板里用短横线(:user-name)。

5. $emit:子组件喊话父组件

Props 是父组件"喂"子组件,那子组件怎么"说话"呢?用 $emit!它能让子组件触发事件,告诉父组件:"嘿,我这儿有动静!"

5.1 怎么用 $emit?

子组件:喊一声。

vue 复制代码
<!-- Counter.vue -->
<template>
  <button @click="increment">点我加1</button>
</template>

<script setup>
const emit = defineEmits(['count-up']); // 声明事件
function increment() {
  emit('count-up', 1); // 喊:我加了1!
}
</script>

父组件:听着。

vue 复制代码
<template>
  <div>
    <Counter @count-up="addCount" />
    <p>计数:{{ count }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import Counter from './Counter.vue';

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

结果:点按钮,计数加1!

5.2 实际用处
  • 子组件按钮被点了,告诉父组件。
  • 输入框变了,通知父组件更新数据。

小贴士

  • 事件名用短横线(count-up),别用大写。
  • 数据流还是单向的,子组件别直接改父组件的东西。

6. 插槽(Slots):让组件更灵活

插槽就像组件的"插口",父组件可以塞东西进去。Vue 有三种插槽:默认、命名和作用域插槽。

6.1 默认插槽:随便塞

子组件:留个空位。

vue 复制代码
<!-- Card.vue -->
<template>
  <div class="card">
    <slot></slot> <!-- 父组件塞啥我显示啥 -->
  </div>
</template>

<style scoped>
.card {
  border: 1px solid #ddd;
  padding: 10px;
}
</style>

父组件:塞内容。

vue 复制代码
<template>
  <Card>
    <h3>标题</h3>
    <p>一段话</p>
  </Card>
</template>

<script setup>
import Card from './Card.vue';
</script>

结果:卡片里显示标题和一段话。

6.2 命名插槽:指定位置

多个插槽时,用 name 分清楚。

子组件

vue 复制代码
<!-- Layout.vue -->
<template>
  <div>
    <header><slot name="header"></slot></header>
    <main><slot></slot></main>
    <footer><slot name="footer"></slot></footer>
  </div>
</template>

父组件

vue 复制代码
<template>
  <Layout>
    <template v-slot:header>
      <h1>页眉</h1>
    </template>
    <p>正文</p>
    <template v-slot:footer>
      <p>页脚</p>
    </template>
  </Layout>
</template>

<script setup>
import Layout from './Layout.vue';
</script>

用处:布局组件,父组件决定哪儿放啥。

6.3 作用域插槽:带数据的插槽

子组件可以传数据给插槽。

子组件

vue 复制代码
<!-- UserList.vue -->
<template>
  <div>
    <slot v-for="user in users" :user="user" :key="user.id"></slot>
  </div>
</template>

<script setup>
import { ref } from 'vue';
const users = ref([
  { id: 1, name: '小红', age: 20 },
  { id: 2, name: '小刚', age: 25 }
]);
</script>

父组件

vue 复制代码
<template>
  <UserList>
    <template v-slot="{ user }">
      <p>{{ user.name }} - {{ user.age }}岁</p>
    </template>
  </UserList>
</template>

<script setup>
import UserList from './UserList.vue';
</script>

结果:显示"小红 - 20岁"和"小刚 - 25岁"。

用处:自定义列表、表格的每一行。


7. 实战演练:做一个待办事项组件

我们把学的东西合起来,做个待办事项(Todo)组件。

子组件:TodoItem.vue

vue 复制代码
<template>
  <div class="todo">
    <slot name="content">
      <span :class="{ done: isDone }">{{ title }}</span>
    </slot>
    <button @click="toggle">完成/取消</button>
  </div>
</template>

<script setup>
defineProps({
  title: String,
  isDone: Boolean
});
const emit = defineEmits(['toggle']);
function toggle() {
  emit('toggle');
}
</script>

<style scoped>
.todo {
  padding: 8px;
  border-bottom: 1px solid #eee;
}
.done {
  text-decoration: line-through;
  color: #888;
}
</style>

父组件:TodoList.vue

vue 复制代码
<template>
  <div>
    <TodoItem
      v-for="todo in todos"
      :key="todo.id"
      :title="todo.title"
      :is-done="todo.done"
      @toggle="toggleDone(todo.id)"
    >
      <template v-slot:content="{ title, isDone }">
        <strong :class="{ done: isDone }">{{ title }}</strong>
      </template>
    </TodoItem>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import TodoItem from './TodoItem.vue';

const todos = ref([
  { id: 1, title: '买菜', done: false },
  { id: 2, title: '写代码', done: true }
]);

function toggleDone(id) {
  const todo = todos.value.find(t => t.id === id);
  if (todo) todo.done = !todo.done;
}
</script>

效果

  • 显示待办列表,点按钮切换完成状态。
  • 用插槽自定义标题样式。

8. 小技巧和趋势

最佳实践

  • 多用局部注册,少用全局。
  • Props 和事件名要有意义,比如 update-countupdate 清楚。
  • 插槽多用 v-slot,别忘了 v-forkey

2025 年趋势

  • Vue 3 是主流,<script setup> 超方便。
  • Vite 取代 Webpack,启动快得飞起。
  • Composition API(比如 defineProps)比 Options API 更灵活。

9. 总结

恭喜你!现在你已经学会 Vue.js 组件化开发的基本功了:注册组件、用 Props 传数据、用 $emit 通信、用插槽增加灵活性。就像搭乐高一样,你可以用这些"积木"搭出各种酷炫的前端界面。快去试试吧,动手写几个组件,感受一下组件化开发的乐趣!有什么问题,随时回来翻翻这篇指南哦!

相关推荐
菜鸟una1 小时前
【微信小程序 + 高德地图API 】键入关键字搜索地址,获取经纬度等
前端·vue.js·微信小程序·小程序·typescript
进取星辰3 小时前
33、魔法防御术——React 19 安全攻防实战
前端·安全·react.js
海天胜景3 小时前
vue3 el-table 行号
javascript·vue.js·ecmascript
小赖同学啊3 小时前
深度解析 Element Plus
前端·javascript·vue.js
二十雨辰3 小时前
[CSS3]百分比布局
前端·html·css3
大大。3 小时前
Vue3 与 Vue2 区别
前端·面试·职场和发展
职场马喽3 小时前
vue+luckysheet导出功能(解决了样式为null的报错问题)
前端·javascript·vue.js
Mr.app3 小时前
关于Vue自定义组件封装的属性/事件/插槽的透传问题
vue.js
北辰浮光3 小时前
[Vue]路由基础使用和路径传参
前端·javascript·vue.js