一、前置认知:为什么前端需要架构设计?
前十三篇我们完成了从基础开发、工程化到跨端开发的能力构建,这些技能足以支撑中小项目或单一模块的开发。但职场中,当面对"10人以上团队维护、千万级用户访问、年迭代百次以上"的大型项目时,仅关注"功能实现"会导致项目陷入"牵一发而动全身"的困境------比如修改一个通用按钮样式导致多个页面错乱,新增业务模块时需要重构大量原有代码,高并发场景下页面加载卡顿甚至崩溃。
前端架构设计的核心价值在于:以"长期可维护性、高扩展性、高性能、高可用性"为目标,通过抽象化、模块化、规范化的设计,解决大型项目中的复杂性问题,支撑业务持续迭代。这是前端开发者从"中级"迈向"高级"的核心标志,也是企业保障大型项目稳定运行的关键。
职场数据:据字节跳动前端团队实践报告显示,实施规范架构设计的大型项目,新增模块开发效率提升35%,线上故障率降低45%,代码重构成本降低60%;未进行架构设计的项目,在迭代1年后维护成本会增长2-3倍。
二、Day40:架构思维建立------从"开发者"到"设计者"的转变
架构设计的本质是"权衡与决策",而非单纯的技术堆砌。在动手设计前,需先建立架构思维,明确架构设计的核心原则和决策维度,避免陷入"为了架构而架构"的误区。
1. 前端架构设计的4大核心原则
架构设计没有"银弹",但遵循核心原则可确保设计方向不偏离目标,这是所有大型项目架构的共性基础:
-
高内聚低耦合:
-
核心定义:模块内部职责单一且紧密关联(高内聚),模块之间依赖关系清晰且尽量减少(低耦合);
-
实战体现:将"用户登录、注册、信息修改"封装为用户模块,模块内部提供完整的用户管理能力,对外仅暴露标准化接口,其他模块无需关心其内部实现;
-
反例:将用户登录逻辑和商品展示逻辑混在同一组件中,修改登录逻辑时可能影响商品展示。
-
-
开闭原则:
-
核心定义:对扩展开放,对修改关闭------新增功能时通过扩展模块实现,而非修改原有稳定代码;
-
实战体现:设计支付模块时,预留支付方式扩展接口,新增"支付宝支付"时只需开发支付宝支付插件并接入接口,无需修改原有微信支付的核心代码;
-
价值:减少修改原有代码带来的风险,保障核心模块稳定性。
-
-
单一职责原则:
-
核心定义:每个模块、组件、函数只负责一个明确的职责;
-
实战体现:将"数据请求、数据格式化、UI渲染"拆分为三个独立模块------api模块负责请求,utils模块负责格式化,components模块负责渲染;
-
价值:提高代码复用性,便于测试和维护(如修改数据格式时仅需调整utils模块)。
-
-
最小知识原则:
-
核心定义:模块之间仅通过公开接口交互,不深入了解彼此的内部实现;
-
实战体现:订单模块需要用户信息时,通过用户模块提供的`getUserInfo()`接口获取,而非直接读取用户模块的内部状态;
-
价值:降低模块间的依赖风险,当用户模块内部重构时,只要接口不变,订单模块无需修改。
-
2. 架构决策的5大关键维度
架构设计的过程是基于业务需求在多个维度上做决策的过程,不同项目的决策优先级不同,需结合实际场景权衡:
| 决策维度 | 核心关注点 | 高优先级场景 | 决策示例 |
|---|---|---|---|
| 性能 | 页面加载速度、交互响应速度、并发处理能力 | 电商首页、直播页面、高并发活动页 | 采用"CDN加速+懒加载+服务端渲染(SSR)"架构 |
| 可扩展性 | 新增业务模块的成本、技术栈升级的兼容性 | SaaS平台、多业务线聚合平台 | 采用"微前端"架构,支持独立业务模块开发和部署 |
| 可维护性 | 代码可读性、文档完整性、问题定位效率 | 长期迭代的核心业务系统(如交易系统) | 制定统一的代码规范、模块命名规则和接口文档标准 |
| 可用性 | 系统稳定性、容错能力、降级策略 | 支付系统、金融相关业务 | 采用"接口熔断+降级+重试"机制,关键接口多活部署 |
| 开发效率 | 团队协作效率、脚手架支持、复用能力 | 快速迭代的创业项目、营销活动页 | 采用"组件库+模板工程",减少重复开发 |
3. 实战1:架构需求分析------从业务到技术的拆解
架构设计必须基于业务需求,脱离业务的架构是空中楼阁。以"电商平台"为例,进行架构需求拆解:
-
业务需求梳理:
-
核心业务:商品展示、购物车、订单管理、支付、用户中心;
-
非核心业务:营销活动(秒杀、优惠券)、评价系统、物流查询;
-
业务特点:商品和订单数据实时性要求高,秒杀场景并发量大,支付流程安全性要求高。
-
-
技术需求转化:
-
性能需求:商品列表首屏加载时间<1.5s,秒杀场景支持10万用户并发;
-
扩展性需求:支持新增"跨境商品"业务线,支持接入新的支付方式;
-
可用性需求:支付模块可用性>99.99%,支持故障自动降级(如支付失败时提示重试);
-
维护性需求:团队按业务线分工(商品组、订单组、支付组),模块需独立开发和测试。
-
-
架构目标确定:
-
核心目标:支撑高并发秒杀场景,保障支付流程稳定,支持多业务线扩展;
-
次要目标:提升团队协作效率,降低跨模块沟通成本。
-
三、Day41:核心架构模块设计------大型项目的"骨架"搭建
基于架构思维和需求分析,接下来进行核心模块设计。大型前端项目的架构骨架通常由"分层架构+核心模块"组成,分层确保代码职责清晰,核心模块支撑业务落地。
1. 前端通用分层架构设计
分层架构是前端架构的基础,通过纵向分层实现"关注点分离",不同层级负责不同职责,便于维护和扩展。以Vue3项目为例,推荐采用"6层架构":
src/ ├── 1. 接入层(Entry) # 应用入口,负责初始化和环境配置 │ ├── main.js # 应用入口文件(初始化Vue、路由、Pinia等) │ ├── App.vue # 根组件(路由出口、全局布局) │ └── environments/ # 环境配置(开发、测试、生产环境变量) ├── 2. 路由层(Router) # 路由管理,负责页面跳转和权限控制 │ ├── index.js # 路由实例创建 │ ├── routes/ # 路由配置(按业务线拆分) │ └── guards/ # 路由守卫(权限控制、页面拦截) ├── 3. 视图层(View) # 页面组件,负责UI渲染和用户交互 │ ├── common/ # 通用页面(404、登录页) │ ├── product/ # 商品业务页面 │ ├── order/ # 订单业务页面 │ └── user/ # 用户业务页面 ├── 4. 组件层(Component) # 通用和业务组件,负责UI复用 │ ├── common/ # 通用组件(按钮、表格、弹窗) │ ├── product/ # 商品业务组件(商品卡片、详情页组件) │ └── order/ # 订单业务组件(订单列表项、支付弹窗) ├── 5. 业务逻辑层(Service) # 业务逻辑处理,负责数据加工和逻辑封装 │ ├── productService.js # 商品业务逻辑(商品查询、筛选、排序) │ ├── orderService.js # 订单业务逻辑(创建订单、取消订单) │ └── userService.js # 用户业务逻辑(登录、获取用户信息) ├── 6. 数据层(Data) # 数据获取和存储,负责与后端交互和本地存储 │ ├── api/ # 接口请求(按业务线拆分) │ ├── store/ # 状态管理(Pinia模块) │ └── storage/ # 本地存储(localStorage、sessionStorage封装) └── 公共工具层(Common) # 全局通用资源,支撑各层运行 ├── utils/ # 工具函数(格式化、校验、加密) ├── styles/ # 全局样式(主题、变量、混入) └── assets/ # 静态资源(图片、图标、字体)
各层核心职责与交互流程
-
接入层:启动应用,加载全局依赖,初始化环境配置;
-
路由层:接收用户跳转请求,通过路由守卫校验权限后,渲染对应视图层页面;
-
视图层:页面组件通过调用业务逻辑层的方法获取数据,渲染组件层的组件,接收用户交互并触发业务逻辑;
-
业务逻辑层:接收视图层的请求,调用数据层获取原始数据,进行业务加工(如筛选、格式化)后返回给视图层,封装核心业务逻辑;
-
数据层:api模块负责与后端接口交互,store模块负责全局状态管理,storage模块负责本地数据存储;
-
交互流程:用户点击商品详情按钮 → 路由层跳转 → 视图层(商品详情页)渲染 → 调用productService.getProductDetail() → api模块请求后端接口 → 业务逻辑层加工数据 → 视图层渲染商品详情组件。
2. 核心模块设计实战------以电商平台为例
在分层架构基础上,针对核心业务设计独立模块,确保业务逻辑的内聚性。以下是电商平台3个核心模块的设计方案:
实战2:商品模块设计(高并发场景优化)
商品模块是电商平台的核心,面临"高并发访问、数据实时更新、多维度筛选"等需求,设计重点在于"性能优化"和"数据缓存":
# 1. 数据层(api/productApi.js)------ 接口请求封装 import request from '../request'; export const productApi = { // 获取商品列表(支持分页、筛选) getProductList: (params) => { return request({ url: '/api/product/list', method: 'GET', params, // 缓存配置:列表数据缓存5分钟(减少重复请求) cache: { enabled: true, expire: 5 * 60 * 1000, key: `productList_${JSON.stringify(params)}` // 按参数生成唯一缓存key } }); }, // 获取商品详情(实时性要求高,不缓存) getProductDetail: (id) => { return request({ url: `/api/product/${id}`, method: 'GET' }); }, // 秒杀商品接口(高并发场景,添加请求限流) getSeckillProduct: (id) => { return request({ url: `/api/product/seckill/${id}`, method: 'GET', // 限流配置:同一用户1秒内最多请求1次 throttle: { enabled: true, delay: 1000, key: `seckill_${id}_${localStorage.getItem('userId')}` } }); } };
# 2. 业务逻辑层(service/productService.js)------ 业务逻辑封装 import { productApi } from '../data/api/productApi'; import { useProductStore } from '../data/store/productStore'; import { formatPrice, formatDate } from '../../common/utils/format'; export const productService = { // 获取商品列表(带筛选和格式化) async getProductList(params) { const productStore = useProductStore(); // 显示加载中状态 productStore.setLoading(true); try { const res = await productApi.getProductList(params); // 业务数据加工:格式化价格和日期,添加默认图片 const formattedList = res.data.list.map(item => ({ ...item, price: formatPrice(item.price), // 格式化价格为"¥XX.XX" createTime: formatDate(item.createTime), // 格式化日期 coverImage: item.coverImage || 'https://default-product.png' // 默认图片 })); // 存储到状态管理 productStore.setProductList(formattedList); productStore.setTotal(res.data.total); return formattedList; } catch (err) { console.error('获取商品列表失败:', err); // 错误处理:显示错误提示 uni.showToast({ title: '商品加载失败', icon: 'none' }); throw err; } finally { // 隐藏加载中状态 productStore.setLoading(false); } }, // 秒杀商品抢购(核心业务逻辑) async seckillProduct(productId, quantity) { const productStore = useProductStore(); // 1. 校验库存(本地先校验,减少无效请求) const product = productStore.productList.find(item => item.id === productId); if (!product || product.stock < quantity) { uni.showToast({ title: '库存不足', icon: 'none' }); return false; } // 2. 调用秒杀接口 try { await productApi.seckillProduct(productId, { quantity }); // 3. 秒杀成功:更新本地库存,跳转订单页 productStore.updateProductStock(productId, quantity); uni.navigateTo({ url: `/views/order/Create?productId=${productId}&quantity=${quantity}` }); return true; } catch (err) { // 秒杀失败:根据错误类型提示 if (err.response.data.code === 1001) { uni.showToast({ title: '秒杀已结束', icon: 'none' }); } else { uni.showToast({ title: '抢购失败,请重试', icon: 'none' }); } return false; } } };
<!-- 3. 视图层(views/product/List.vue)------ 页面渲染与交互 --> <template> <div class="product-list-page"> <!-- 筛选组件(组件层) --> <ProductFilter @search="handleSearch" /> <!-- 加载中状态 --> <Loading v-if="productStore.loading" /> <!-- 商品列表(组件层) --> <div class="product-list"> <ProductCard v-for="product in productStore.productList" :key="product.id" :product="product" @click="handleGoDetail(product.id)" @seckill="handleSeckill(product.id)" /> </div> <!-- 分页组件(组件层) --> <Pagination :total="productStore.total" :page-size="pageSize" @page-change="handlePageChange" /> </div> </template> <script setup> import { ref, onMounted } from 'vue'; import { useRouter } from 'vue-router'; import { productService } from '../../service/productService'; import { useProductStore } from '../../data/store/productStore'; import ProductFilter from '../../components/product/ProductFilter.vue'; import ProductCard from '../../components/product/ProductCard.vue'; import Loading from '../../components/common/Loading.vue'; import Pagination from '../../components/common/Pagination.vue'; const router = useRouter(); const productStore = useProductStore(); const pageSize = ref(10); const currentPage = ref(1); const searchParams = ref({ category: '', priceRange: '' }); // 页面加载时获取商品列表 onMounted(() => { handleGetProductList(); }); // 获取商品列表(调用业务逻辑层) const handleGetProductList = async () => { await productService.getProductList({ page: currentPage.value, pageSize: pageSize.value, ...searchParams.value }); }; // 筛选事件 const handleSearch = (params) => { searchParams.value = params; currentPage.value = 1; // 重置为第一页 handleGetProductList(); }; // 分页切换 const handlePageChange = (page) => { currentPage.value = page; handleGetProductList(); }; // 跳转到商品详情 const handleGoDetail = (id) => { router.push(`/product/detail/${id}`); }; // 秒杀商品 const handleSeckill = async (id) => { const success = await productService.seckillProduct(id, 1); if (success) { // 秒杀成功逻辑(业务逻辑层已处理跳转) } }; </script>
实战3:权限模块设计(通用型架构)
权限控制是大型项目的必备模块,涉及"路由权限、按钮权限、接口权限",设计重点在于"通用化、可配置",避免在业务代码中散落权限判断逻辑:
# 1. 数据层(store/permissionStore.js)------ 权限状态管理 import { defineStore } from 'pinia'; import { permissionApi } from '../api/permissionApi'; export const usePermissionStore = defineStore('permission', { state: () => ({ roles: [], // 用户角色(如admin、user、merchant) permissions: [] // 用户权限列表(如product:view、order:create) }), actions: { // 初始化权限(登录后调用) async initPermission(userId) { const res = await permissionApi.getUserPermission(userId); this.roles = res.data.roles; this.permissions = res.data.permissions; // 存储到本地,避免刷新丢失 localStorage.setItem('roles', JSON.stringify(this.roles)); localStorage.setItem('permissions', JSON.stringify(this.permissions)); }, // 从本地加载权限(页面刷新时调用) loadPermission() { const roles = localStorage.getItem('roles'); const permissions = localStorage.getItem('permissions'); if (roles && permissions) { this.roles = JSON.parse(roles); this.permissions = JSON.parse(permissions); } }, // 检查是否有指定权限 hasPermission(permission) { // admin角色拥有所有权限 if (this.roles.includes('admin')) return true; return this.permissions.includes(permission); }, // 检查是否有指定角色 hasRole(role) { return this.roles.includes(role); } } });
# 2. 路由层(guards/permissionGuard.js)------ 路由权限守卫 import { usePermissionStore } from '../../data/store/permissionStore'; import { useUserStore } from '../../data/store/userStore'; // 路由权限守卫:控制页面访问权限 export const permissionGuard = (to, from, next) => { const permissionStore = usePermissionStore(); const userStore = useUserStore(); const isLogin = userStore.isLogin; // 1. 未登录:跳转到登录页 if (!isLogin) { next('/login?redirect=' + to.path); return; } // 2. 已登录但未加载权限:加载权限后再判断 if (permissionStore.permissions.length === 0) { permissionStore.initPermission(userStore.userId).then(() => { handlePermissionCheck(); }); return; } // 3. 已加载权限:判断是否有权限访问当前页面 handlePermissionCheck(); // 权限检查核心逻辑 function handlePermissionCheck() { // 无需权限的页面(如首页) if (!to.meta.requirePermission) { next(); return; } // 需要指定权限:检查是否拥有 const requirePermission = to.meta.requirePermission; if (permissionStore.hasPermission(requirePermission)) { next(); } else { // 无权限:跳转到403页面 next('/403'); } } };
<!-- 3. 组件层(components/common/PermissionButton.vue)------ 按钮权限组件 --> <template> <button v-if="hasPermission" :class="className" @click="$emit('click')" :disabled="disabled" > <slot></slot> </button> </template> <script setup> import { computed } from 'vue'; import { usePermissionStore } from '../../data/store/permissionStore'; const permissionStore = usePermissionStore(); // 接收权限参数 const props = defineProps({ permission: { type: String, required: true, // 必须指定需要的权限 default: '' }, className: { type: String, default: '' }, disabled: { type: Boolean, default: false } }); // 计算是否拥有权限 const hasPermission = computed(() => { return permissionStore.hasPermission(props.permission); }); </script>
<!-- 4. 视图层中使用权限按钮 --> <template> <div class="order-page"> <h2>订单管理</h2> <!-- 只有拥有order:create权限的用户才能看到创建订单按钮 --> <PermissionButton permission="order:create" className="create-btn" @click="handleCreateOrder" > 创建订单 </PermissionButton> <!-- 只有拥有order:delete权限的用户才能看到删除按钮 --> <PermissionButton permission="order:delete" className="delete-btn" @click="handleDeleteOrder" :disabled="!selectedOrderId" > 删除订单 </PermissionButton> </div> </template>
四、Day42:架构落地与演进------从"设计"到"实践"的闭环
架构设计不是一成不变的,需要通过"规范落地、技术支撑、持续演进"形成闭环,确保架构在项目迭代中持续发挥价值,避免设计与实践脱节。
1. 架构规范落地------确保团队执行一致性
架构设计需要通过规范固化,让团队所有成员按统一标准执行。大型项目需制定以下核心规范:
-
目录结构规范:
-
明确各层目录的命名规则(如业务模块以业务名称命名,工具函数按功能分类);
-
规定文件命名规则(如组件采用PascalCase,工具函数采用camelCase,常量采用UPPER_SNAKE_CASE);
-
示例:商品业务组件命名为`ProductCard.vue`,工具函数文件命名为`formatUtils.js`,常量文件命名为`CONSTANT.js`。
-
-
接口规范:
-
统一api模块的请求封装(如所有请求携带token,统一错误处理);
-
规定接口命名规则(如获取列表用getXXXList,新增用addXXX,修改用updateXXX);
-
要求接口文档化(使用Swagger或YApi管理接口文档,api模块添加接口注释)。
-
-
代码提交规范:
-
延续工程化阶段的husky+commitlint配置,按"类型(模块): 描述"格式提交(如`feat(product): 新增商品筛选功能`);
-
要求提交时关联需求或bug编号(如`fix(order): 修复订单支付失败问题 #123`),便于追溯。
-
-
架构评审规范:
-
新增业务模块前必须进行架构评审,确认模块在分层架构中的位置和依赖关系;
-
评审重点:是否符合高内聚低耦合原则、是否有扩展能力、是否与现有架构冲突。
-
2. 架构落地的技术支撑工具
通过工具自动化支撑架构规范落地,减少人工约束成本,提升执行效率:
-
脚手架工具:
-
基于Vue CLI或Vite定制项目脚手架,内置分层架构目录、规范配置和基础模块;
-
新增业务模块时,通过脚手架命令快速生成模板(如`npm run new:module product`生成商品模块的目录和基础文件)。
-
-
ESLint定制规则:
-
在ESLint中添加架构相关的自定义规则,如禁止视图层直接调用api模块(必须通过业务逻辑层)、禁止跨模块直接引用组件;
-
示例规则:禁止在.vue文件中直接import api模块:
// .eslintrc.js 自定义规则 ``rules: { `` 'no-direct-api-import': 'error' // 自定义规则:禁止直接导入api模块 ``}
-
-
可视化工具:
-
使用Webpack Bundle Analyzer分析模块依赖关系,发现耦合过高的模块并优化;
-
使用SonarQube监控代码质量,设置架构相关的质量门禁(如模块依赖深度不超过3层)。
-
3. 架构持续演进------应对业务变化的核心策略
业务在不断变化,架构也需要持续演进,避免成为"技术负债"。架构演进需遵循"小步迭代、灰度验证、数据驱动"的原则:
-
演进触发场景:
-
业务扩张:如新增跨境电商业务线,原有商品模块无法支撑多语言、多货币需求;
-
性能瓶颈:如原有单体架构在并发量提升后出现加载卡顿,需要拆分为微前端;
-
技术升级:如Vue2升级到Vue3,需要调整状态管理和组件写法。
-
-
演进实战案例------从单体架构到微前端:
-
原有问题:电商平台初期采用单体架构,商品、订单、用户模块耦合紧密,新增营销模块时需要修改大量原有代码,团队协作冲突频繁;
-
演进方案:采用qiankun微前端架构,按业务线拆分为独立应用:
-
主应用:负责路由分发、权限管理、全局状态共享;
-
子应用:商品应用、订单应用、用户应用、营销应用(独立开发、部署和迭代);
-
通信方式:通过qiankun的全局通信机制实现子应用间数据交互。
-
-
演进步骤:
-
第一步:主应用搭建,实现路由分发和基础权限控制;
-
第二步:将原有单体应用中的商品模块拆分为独立子应用,接入主应用;
-
第三步:依次拆分订单、用户、营销模块为子应用,逐步替换原有功能;
-
第四步:优化子应用间通信和共享依赖,提升性能。
-
-
演进效果:各团队独立开发子应用,协作冲突减少70%,新增营销活动迭代周期从15天缩短至5天。
-
-
演进注意事项:
-
避免"大爆炸式"重构:一次性重构整个架构风险极高,采用"逐步拆分、灰度替换"的方式;
-
保留兼容层:新架构上线初期,保留原有架构的兼容接口,确保业务平滑过渡;
-
数据驱动决策:通过性能监控、错误统计等数据判断架构是否需要演进,避免主观决策。
-
五、3天总结:前端架构设计职场能力清单
-
架构思维:掌握高内聚低耦合、开闭原则等核心架构原则,能基于业务需求进行架构决策,明确性能、扩展性等维度的优先级;
-
分层架构设计:能设计"接入层-路由层-视图层-组件层-业务逻辑层-数据层"的分层架构,明确各层职责和交互流程;
-
核心模块设计:能针对高并发、权限控制等场景设计核心业务模块,实现业务逻辑封装和性能优化;
-
架构落地能力:能制定目录结构、接口、提交等架构规范,通过脚手架、ESLint等工具保障规范落地;
-
架构演进能力:能识别架构演进的触发场景,采用"小步迭代"的方式实现架构升级,如从单体架构迁移到微前端;
-
作业:基于之前的电商项目,完成以下任务:① 按照"6层架构"重构项目目录结构;② 设计并实现权限模块,包含路由守卫和按钮权限控制;③ 撰写架构设计文档,说明架构分层、核心模块设计和规范要求。