Appearance
代码复用
元信息
- 目标:理解代码复用的三级台阶——模板复用结构、类封装逻辑与状态、组件把结构/行为/样式封装为自包含的 UI 单元;掌握组件四大核心概念(封装、接口、组合、状态)
- 关键概念:组件、封装、props、事件、组件树、Web Components、Shadow DOM
- 关联阶段:cp4
- 常见误区:以为模板引擎就是组件化(EJS 只复用结构,事件要渲染后手动补挂、样式靠全局 CSS);以为组件化必须依赖框架(Web Components 是浏览器原生三件套);以为组件只是把代码拆到多个文件(没有 props/events 接口契约,拆开的文件依然互相纠缠)
在前端开发中,代码复用是提高开发效率和维护性的关键。以下是三种常见的代码复用方式:模板、类和组件。
组件:现代前端的基石
在展开三种方式之前,先回答"组件到底是什么"。
组件(Component)是 UI 的自定义元素:把一段界面所需的结构(HTML)、行为(JS)、样式(CSS)封装为一个自包含单元,对外只暴露清晰的接口。就像 <input> 是浏览器内建的原生元素,组件让你"发明"自己的 <user-card>、<todo-item>。
组件的四个核心概念:
- 封装(encapsulation):内部实现(DOM 结构、事件绑定、样式)对外不可见。特别是样式——用 scoped CSS 或 Shadow DOM 隔离,避免组件间的级联互相污染(级联见第 5 周)
- 接口(props 与 events):组件像插头一样有明确的"引脚"——通过 props(属性)接收外部数据(自上而下的输入),通过 events(事件)向外部通知变化(自下而上的输出)。父子组件只靠这对契约通信,彼此不需要知道对方的内部实现
- 组合(composition):复杂界面由简单组件逐层拼装而成,形成组件树。页面不再是"一个大文件",而是一棵组件的组成关系图——这是与模板/类复用的本质区别:它们复用"代码片段",组件复用的是"完整的 UI 单元"
- 状态(state):组件持有自己的内部数据,状态变化自动驱动视图更新(即反应式编程)。"状态归属哪个组件"因此成为现代前端架构的核心问题(见前端架构)
这三层封装带来的直接收益是组件库:button、table、dialog 等通用组件一经实现,即可在项目间、团队间复用——Element Plus、Ant Design 等正是把"通用 UI 单元"产品化的产物。
1. 模板 (Templates)
模板是HTML片段的复用方式,通常用于动态生成DOM结构。
局限:模板只复用了结构——事件要渲染后手动补挂,样式依赖全局 CSS,数据变了需要整个重新渲染。
2. 类 (Classes)
使用ES6类封装可复用的逻辑和状态。
局限:类封装了逻辑与状态,但 render 出来的仍是"裸 HTML"——事件绑定与样式隔离依然要手工完成,没有解决"状态变 → 视图变"的自动同步。
3. 原生组件:Web Components
不依赖框架,浏览器原生也提供了组件三件套(三大API协同):
Web Components 展示了组件概念的平台本质:封装、接口、组合并不属于某个框架,而是 UI 工程的通用思想;框架组件(Vue/React)只是在响应式、渲染效率上做得更完善。
4. 框架组件 (Components)
现代前端框架中的组件化方案(以Vue为例)。
vue
<!-- UserCard.vue -->
<template>
<div class="user-card">
<h3>{{ name }}</h3>
<p>{{ email }}</p>
<button @click="follow">关注</button>
</div>
</template>
<script>
export default {
props: ['name', 'email'], // 接口:接收外部数据
methods: {
follow() {
console.log(`已关注 ${this.name}`);
this.$emit('followed', this.name); // 接口:向外部发事件
}
}
}
</script>
<style scoped>
/* Scoped CSS - 样式仅作用于当前组件。注意模板、类等都只能依赖于外部的全局css */
.user-card {
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 16px;
margin: 10px;
}
</style>vue
<!-- 使用组件:像使用原生标签一样 -->
<template>
<user-card name="王五" email="wangwu@example.com" @followed="handleFollow" />
</template>对照前文四个概念,框架组件把每一项都补齐了:scoped style = 封装;props + $emit = 接口;组件树 = 组合;响应式状态 = 状态。
特性比较
| 特性 | 模板 (Templates) | 类 (Classes) | Web Components | 组件 (Components) |
|---|---|---|---|---|
| 复用粒度 | UI结构片段 | 逻辑与状态 | 完整UI单元 | 完整UI+逻辑单元 |
| 样式封装 | ❌ 依赖全局CSS | ❌ 依赖全局CSS | ✅ Shadow DOM | ✅ scoped CSS |
| 状态管理 | 无 | 内置状态管理 | 手工 | 响应式状态管理 |
| 接口契约 | 无 | 无 | 属性+自定义事件 | props + events |
| 维护性 | 简单场景适用 | 中等 | 高 | 高,职责清晰 |
| 依赖 | 无 | 无(ES6标准) | 无(浏览器原生) | 框架(Vue/React等) |
| 适用场景 | 静态内容复用 | 复杂业务逻辑封装 | 跨框架/无框架场景 | 大型应用、团队协作 |
小结
- 模板:复用HTML结构
- 类: 借助js类的封装、继承等机制,实现html、js等代码的复用,对状态的管理等,但对事件、css的封装有限。
- Web Components:浏览器原生的组件方案,证明"封装/接口/组合"是平台能力而非框架专利
- 组件:借助js的复杂编程,采用类似于原生标记的方式,将自定义的html、js、css代码重新封装为一个整体(尤其是scoped css,解耦了css的级联特性),实现了状态和事件的管理,形成了基于组合的扩展方式,进而可以构建形成组件库。在现代化前端项目中,组件是最推荐的方式,特别适合团队协作和大型应用
在实际开发中,这几种方式常常结合使用。例如,在组件内部可能使用类来管理业务逻辑,使用模板来定义渲染结构。根据项目需求和团队技术栈做出合适的选择,才是最佳实践。