组件化开发基础
🎯 引言
学会这篇文章,你可以轻松理解组件化开发的基础知识,掌握 Vue 单文件组件的结构以及创建和注册组件的方法。你还会学会组件之间如何通信,包括父子组件和跨级组件的通信方式,插槽的使用,以及动态组件的灵活运用。掌握这些技能后,前端开发不再是难题,项目结构更清晰,代码复用度更高。
🧩 组件化开发基础
组件化开发,就是把页面拆分成一个个独立且可复用的功能块。Vue 的核心功能之一组件化,它的单文件组件(.vue 文件)让你能把模板、逻辑、样式写在一个文件里,极大方便了开发和维护。
🐱🏍 单文件组件形式(.vue 结构)
单文件组件是 Vue 生态中特有的格式,文件后缀是 .vue。它将组件的三个核心部分整合在一起:
<template>:定义组件的 HTML 结构<script>:编写组件的逻辑代码<style>:写组件的样式
这里是一个最简单的 .vue 组件示例:
<template>
<div class="hello">
<h1>{{ message }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data() {
return {
message: '欢迎来到组件化开发!',
};
},
};
</script>
<style scoped>
.hello {
color: #42b983;
font-weight: bold;
}
</style>
<style scoped> 表示样式只作用于当前组件,避免污染全局。这个结构让组件职责分明,逻辑和样式都集中管理,方便维护和复用。
🧱 组件创建与注册
组件创建后,我们需要注册它,才能在父组件中使用。Vue 支持局部注册和全局注册两种方式。
局部注册
在父组件的 <script> 中引入并注册:
<script>
import HelloWorld from './HelloWorld.vue';
export default {
components: {
HelloWorld,
},
};
</script>
然后模板里就可以用 <HelloWorld /> 了。
全局注册
在入口文件(如 main.js)中注册:
import Vue from 'vue';
import HelloWorld from './HelloWorld.vue';
Vue.component('HelloWorld', HelloWorld);
这样,无论哪个组件都能直接用 <HelloWorld />。
💬 组件通信
组件之间交流信息是必不可少的。Vue 主流的通信方式是:
父组件向子组件传递数据:props
子组件通过 props 属性接收父组件传来的数据。
父组件:
<template>
<ChildComponent :title="pageTitle" />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: { ChildComponent },
data() {
return {
pageTitle: '我是父组件传来的标题',
};
},
};
</script>
子组件 ChildComponent.vue:
<template>
<h2>{{ title }}</h2>
</template>
<script>
export default {
props: ['title'],
};
</script>
子组件向父组件传递消息:$emit
子组件用 $emit 触发事件,父组件监听事件。
子组件:
<template>
<button @click="$emit('alert', '按钮被点击了')">点击我</button>
</template>
父组件:
<template>
<ChildComponent @alert="handleAlert" />
</template>
<script>
export default {
methods: {
handleAlert(message) {
alert(message);
},
},
};
</script>
props 是单向数据流,父传子。子组件不能直接修改 props,这样保持数据的稳定性。🌉 跨级通信:Provide & Inject
有时候组件嵌套层级很深,父组件数据要传给孙组件甚至更远,这时用 props 传递很麻烦。Vue 提供了 provide 和 inject 解决跨级传值问题。
父组件:
<script>
export default {
provide() {
return {
color: 'red',
};
},
};
</script>
孙组件或更深层组件:
<script>
export default {
inject: ['color'],
mounted() {
console.log('注入的颜色是', this.color); // red
},
};
</script>
provide/inject 并不是响应式的,如果需要响应式数据,注意配合 Vue.observable 或使用状态管理方案。🪟 插槽(默认插槽、具名插槽)
插槽可以让父组件把内容传递给子组件的指定位置,增强组件的灵活性。
默认插槽
子组件模板:
<template>
<div class="wrapper">
<slot></slot>
</div>
</template>
父组件:
<template>
<ChildComponent>
<p>这是默认插槽的内容</p>
</ChildComponent>
</template>
具名插槽
子组件:
<template>
<header>
<slot name="header">默认头部</slot>
</header>
<main>
<slot>默认内容</slot>
</main>
<footer>
<slot name="footer">默认底部</slot>
</footer>
</template>
父组件:
<template>
<ChildComponent>
<template v-slot:header>
<h1>自定义头部</h1>
</template>
<template v-slot:footer>
<small>自定义底部</small>
</template>
默认插槽内容
</ChildComponent>
</template>
🔄 动态组件使用(<component> 标签)
有时候根据不同的数据切换组件,可以用 <component> 标签动态渲染。
<template>
<component :is="currentComponent" />
<button @click="toggle">切换组件</button>
</template>
<script>
import CompA from './CompA.vue';
import CompB from './CompB.vue';
export default {
components: { CompA, CompB },
data() {
return {
currentComponent: 'CompA',
};
},
methods: {
toggle() {
this.currentComponent = this.currentComponent === 'CompA' ? 'CompB' : 'CompA';
},
},
};
</script>
当 currentComponent 变成 CompB,显示的就切换到 CompB 组件了。
🧾 小节总结
.vue单文件组件结构整合模板、逻辑、样式,方便开发和维护。- 组件通过
props和$emit实现父子通信,provide/inject支持跨级传值。 - 插槽提供灵活的内容分发方式,动态组件方便根据需求切换不同模块。
❓ 知识问答(Q&A)
Q:为什么子组件不能直接修改 props?
A:props 是单向数据流,子组件修改会破坏父组件的数据源,导致数据不一致。正确做法是通过事件告诉父组件修改。
Q:provide/inject 的数据是响应式的吗?
A:默认不是响应式的,需要结合 Vue.observable 或其他响应式方案来实现。
Q:动态组件 <component> 适合什么场景?
A:适合内容切换,比如标签页、步骤流程、弹窗内容不同等场景。
🧪 小练习
练习 1:创建一个带有默认插槽和具名插槽的对话框组件
写一个 Dialog.vue 组件:
- 具名插槽
title用作对话框标题 - 默认插槽用来放对话框内容
- 具名插槽
footer用作底部按钮区域
父组件调用这个组件并传入对应内容。
练习 2:实现动态切换的列表显示组件
写一个组件,有两个子组件 ListA.vue 和 ListB.vue,根据按钮点击切换当前显示的列表。
<template>
<div>
<button @click="toggle">切换列表</button>
<component :is="currentList" />
</div>
</template>
<script>
import ListA from './ListA.vue';
import ListB from './ListB.vue';
export default {
components: { ListA, ListB },
data() {
return {
currentList: 'ListA',
};
},
methods: {
toggle() {
// TODO
},
},
};
</script>
🎉 恭喜你已经掌握 单文件组件结构、组件通信、插槽和动态组件 使用技能啦!继续练习,组件化开发将成为你构建复杂页面的利器!
