十年前我刚入行时,前端开发还停留在jQuery时代,一个main.js文件动辄几千行代码是家常便饭。随着SPA框架的兴起和业务复杂度提升,我们逐渐意识到前端架构的重要性。现代前端架构已经从简单的文件拆分,发展到需要考虑性能优化、团队协作、工程化等综合因素的复杂体系。
最近三年,我在三个不同规模的项目中主导了前端架构改造,从零开始搭建过微前端方案,也经历过遗留系统的渐进式重构。这些实战经历让我深刻体会到:好的前端架构应该像乐高积木,模块之间松耦合但又能完美组合;像城市规划,既要考虑当下需求也要预留发展空间。
我在电商项目中总结的模块划分经验是:当某个功能组件开始出现以下特征时,就应该考虑拆分为独立模块:
一个典型的错误案例是我们曾经把商品详情页的SKU选择器直接写在页面组件里,后来当营销活动需要复用这个组件时,不得不进行痛苦的代码抽取。现在我们会用这样的目录结构组织复杂模块:
code复制modules/
sku-selector/
├── index.vue // 主入口
├── utils/ // 专用工具函数
├── hooks/ // 组合式API
├── types/ // TS类型定义
└── __tests__/ // 单元测试
模块间的通信接口设计直接影响系统可维护性。我们团队现在强制要求:
比如支付模块的接口定义会写成:
typescript复制interface PaymentModuleOptions {
orderId: string
amount: number
currency?: 'CNY' | 'USD'
}
interface PaymentResult {
transactionId: string
status: 'success' | 'failed'
code?: string
}
export const usePayment = (options: PaymentModuleOptions): Promise<PaymentResult> => {
// 实现逻辑...
}
在我们最近的中台项目中,采用了经典的三层架构设计,每层都有明确的职责边界:
展现层:
业务逻辑层:
数据访问层:
这种分层使得测试变得非常清晰:展现层用快照测试,业务层用单元测试,数据层用接口mock测试。
当系统需要整合多个团队开发的模块时,我们评估了三种微前端方案:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Single-SPA | 成熟生态,灵活度高 | 配置复杂,需要自己解决样式隔离 | 技术栈差异大的复杂系统 |
| Module Federation | 原生支持,性能优异 | 要求Webpack5,版本锁定 | 技术栈统一的较新项目 |
| Iframe | 隔离彻底,实现简单 | 通信困难,体验割裂 | 需要完全隔离的第三方模块 |
最终选择了Module Federation方案,因为我们的子应用都是Vue3技术栈。关键配置如下:
javascript复制// webpack.config.js
new ModuleFederationPlugin({
name: 'app-shell',
remotes: {
product: 'product@http://cdn.example.com/product/remoteEntry.js',
order: 'order@http://cdn.example.com/order/remoteEntry.js'
},
shared: {
vue: { singleton: true },
pinia: { singleton: true }
}
})
随着模块增多,构建速度成为痛点。我们通过以下策略将生产构建时间从8分钟降到2分钟:
bash复制# 只构建product模块及其依赖
npm run build -- --module=product
我们的CI流水线包含以下质量关卡:
特别有用的是我们开发的架构守护工具,它会扫描项目并检查:
面对老项目改造,我们采用"外围突破,核心渐进"的策略:
关键技巧是建立"安全网":先为要改造的模块添加完备的测试,再开始动代码。我们曾用这种方式将一个5年历史的jQuery系统平稳迁移到了Vue3。
重要架构决策我们会用ADR文档记录,包含:
例如选择状态管理库时的ADR片段:
code复制## 决策:采用Pinia替代Vuex
**背景**:
当前Vuex模块已超过20个,存在类型支持弱、模块注册繁琐等问题
**候选方案**:
1. Vuex4 + 自定义类型封装
2. Pinia
3. Redux Toolkit
**选择Pinia的原因**:
- 原生TS支持,类型推断完善
- 更简单的API设计
- 与Vue3组合式API风格一致
- 更小的运行时体积(1.5kb vs 10kb)
早期我们曾过度模块化,导致出现大量只有单一引用的"僵尸模块"。现在遵循以下原则:
多模块并行开发时,我们采用如下版本规范:
发布流程:
模块化架构要特别注意:
实测案例:通过调整chunk分割策略,将首屏加载时间从2.4s降到1.1s:
javascript复制optimization: {
splitChunks: {
chunks: 'all',
maxSize: 244 * 1024, // 最大244KB
minSize: 20 * 1024 // 最小20KB
}
}
经过多个项目的实践验证,我认为前端架构的本质是"管理复杂度"。好的架构应该让简单的事情容易做,复杂的事情可能做。当发现团队新人能在两天内理解模块关系并完成功能开发时,就知道这个架构设计是成功的。