Skip to content

代码复用

元信息

  • 目标:理解代码复用的三级台阶——模板复用结构、类封装逻辑与状态、组件把结构/行为/样式封装为自包含的 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结构。

<!DOCTYPE html>
<html>
<head>
  <title>Parcel Sandbox</title>
  <meta charset="UTF-8" />
  <link rel="stylesheet" href="/styles.css" />
</head>
<body>
  <h1>Hello world</h1>
</body>
</html>

局限:模板只复用了结构——事件要渲染后手动补挂,样式依赖全局 CSS,数据变了需要整个重新渲染。

2. 类 (Classes)

使用ES6类封装可复用的逻辑和状态。

import "./styles.css";

document.getElementById("app").innerHTML = `
<h1>Hello world</h1>
`;

局限:类封装了逻辑与状态,但 render 出来的仍是"裸 HTML"——事件绑定与样式隔离依然要手工完成,没有解决"状态变 → 视图变"的自动同步。

3. 原生组件:Web Components

不依赖框架,浏览器原生也提供了组件三件套(三大API协同):

import "./styles.css";

document.getElementById("app").innerHTML = `
<h1>Hello world</h1>
`;

<!DOCTYPE html>
<html>
<head>
  <title>Parcel Sandbox</title>
  <meta charset="UTF-8" />
  <link rel="stylesheet" href="/styles.css" />
</head>
<body>
  <h1>Hello world</h1>
</body>
</html>

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的级联特性),实现了状态和事件的管理,形成了基于组合的扩展方式,进而可以构建形成组件库。在现代化前端项目中,组件是最推荐的方式,特别适合团队协作和大型应用

在实际开发中,这几种方式常常结合使用。例如,在组件内部可能使用类来管理业务逻辑,使用模板来定义渲染结构。根据项目需求和团队技术栈做出合适的选择,才是最佳实践。