好的,我们来深入复习架构设计能力相关的核心知识点。这部分内容直接体现了你作为高级前端工程师的视野、决策能力和解决复杂问题的能力。
1. 微前端 (Micro Frontends)
微前端是一种将前端应用分解为多个更小、更简单、可以独立开发、测试和部署的微应用的架构风格。
各种方案原理与对比
方案 | 原理 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
iframe | 浏览器原生隔离方案,每个微应用一个iframe | 天然JS/CSS隔离,简单易用 | 路由状态管理困难,UI不同步,SEO不友好,通信复杂 | 简单集成第三方独立页面,需要最强隔离性的场景 |
Single-SPA | 应用加载器。是一个顶层路由,根据路由规则动态加载并挂载/卸载微应用(UMD格式)。 | 技术栈无关,纯JS,轻量灵活 | 需要大量改造现有应用(导出生命周期钩子),不解决隔离和依赖问题,需要自行实现 | 适合作为定制化微前端框架的底层引擎, greenfield 项目 |
qiankun | 基于 Single-SPA,提供了完整的运行时解决方案。 | 开箱即用,提供了JS沙箱、样式隔离、资源预加载、全局状态通信等完整能力 | 对 Vite 等非 Webpack 构建的支持需要额外配置(现已有插件) | 中大型企业级应用,需要快速落地微前端方案 |
Webpack 5 Module Federation (模块联邦) | 编译时依赖共享。一个应用(Remote)可以将其部分模块暴露给另一个应用(Host)使用。 | 真正的代码共享,远程模块像本地模块一样使用,极致性能 | 强耦合于 Webpack,需要应用构建工具统一,共享版本需协调 | 技术栈统一或相似的项目群,需要高性能共享组件/模块 |
你必须非常熟悉你项目中用的方案(例如 qiankun),并能阐述:
- 主应用如何注册微应用(配置
entry
,activeRule
,container
)。 - 微应用如何导出生命周期钩子(
bootstrap
,mount
,unmount
)。 - 如何与主应用通信(使用
initGlobalState
)。
核心挑战与解决方案
-
应用隔离
- JS 沙箱 (Sandbox):
- 快照沙箱 (Snapshot):在应用挂载前记录全局状态(
window
),卸载时恢复。适用于不支持Proxy
的浏览器。qiankun 对单实例微应用采用此方案。 - 代理沙箱 (Proxy):使用
Proxy
为每个微应用创建一个假的window
代理对象,所有对其的读写操作都隔离在这个代理对象上。qiankun 对多实例微应用采用此方案。这是目前的主流方案。
- 快照沙箱 (Snapshot):在应用挂载前记录全局状态(
- CSS 隔离:
- Scoped CSS:通过为 CSS 选择器添加特殊属性(如
[data-qiankun-xxx]
)来实现样式隔离。qiankun 默认支持。 - Shadow DOM:最强的隔离方案,将微应用的 DOM 树挂载到一个 Shadow Root 下,实现真正的样式隔离。但可能影响外部样式穿透和组件库的使用。
- Scoped CSS:通过为 CSS 选择器添加特殊属性(如
- JS 沙箱 (Sandbox):
-
数据通信
- 自定义事件 (CustomEvent):使用
window.dispatchEvent
和window.addEventListener
进行简单的父子应用通信。松耦合,但需要规范事件名。 - 状态管理库:在主应用初始化一个全局状态(如
qiankun
的initGlobalState
),微应用通过提供的 API 进行订阅和更新。这是最常用的方式。 - URL 参数:通过路由传递简单参数,适合状态同步要求不高的场景。
- 自定义事件 (CustomEvent):使用
-
公共依赖
- 问题:每个微应用都打包
React
,Vue
,Lodash
等库,导致资源冗余。 - 解决方案:
- Webpack Externals:配置
externals
,让这些库不打包进 bundle,转而通过<script>
标签从 CDN 引入。这是最常见和简单的方案。 - Module Federation:终极方案。主应用作为 Host 提供公共依赖,微应用作为 Remote 直接消费 Host 提供的模块,避免重复加载。
- Webpack Externals:配置
- 问题:每个微应用都打包
你的项目:OMNIEYE 平台
准备一个 STAR 模型来阐述:
- S (Situation):项目背景,为什么需要微前端?(例如:多个团队独立开发不同功能模块,技术栈历史包袱,需要独立部署等)。
- T (Task):你的任务是什么?(例如:技术选型、搭建微前端架构、解决集成难题)。
- A (Action):你采取了什么行动?
- 技术选型:为什么选择
qiankun
?(例如:社区活跃、开箱即用、满足我们的隔离和通信需求)。 - 架构设计:主应用和微应用如何划分?路由设计是怎样的?
- 遇到的坑与解决方案:
- 坑1:微应用样式污染。解决方案:开启 qiankun 的严格样式隔离
strictStyleIsolation
(Shadow DOM)或实验性样式隔离experimentalStyleIsolation
(Scoped CSS)。 - 坑2:路由冲突。解决方案:为每个微应用设置唯一的
activeRule
(路由前缀),主应用使用history.pushState
进行路由跳转。 - 坑3:公共依赖重复。解决方案:将
Vue
,Element-Plus
等配置为externals
,并通过 CDN 引入。
- 坑1:微应用样式污染。解决方案:开启 qiankun 的严格样式隔离
- 技术选型:为什么选择
- R (Result):取得了什么成果?(例如:成功拆分多个微应用,团队能独立开发和部署,性能表现符合预期,提升了开发效率)。
2. 性能优化
指标与测量
-
核心 Web 指标 (Core Web Vitals):
- LCP (Largest Contentful Paint):最大内容绘制,衡量加载性能。应在 2.5 秒内发生。
- FID (First Input Delay):首次输入延迟,衡量交互性。应小于 100 毫秒。现已被 INP (Interaction to Next Paint) 取代作为更全面的指标。
- CLS (Cumulative Layout Shift):累积布局偏移,衡量视觉稳定性。应小于 0.1。
- (FCP - First Contentful Paint):首次内容绘制,测量页面开始加载到任何部分(文本、图像等)呈现在屏幕上的时间。
-
测量工具:
- Lighthouse:实验室工具,在受控环境中模拟测试,提供全面优化建议。
- Chrome DevTools:使用 Performance 面板分析运行时性能,使用 Lighthouse 面板跑分。
- Web Vitals:Google 提供的库,可以真实用户监控 (RUM),在生产环境中测量并上报这些指标。
import { getCLS, getFID, getLCP } from 'web-vitals';
优化方案
网络层面:
- HTTP/2:多路复用、服务器推送、头部压缩,极大提升资源加载效率。
- CDN:将静态资源分发到全球节点,用户从最近的节点获取资源,降低延迟。
- 缓存策略:
- 强缓存:
Cache-Control: max-age=31536000
(一年),Expires
。浏览器直接使用本地缓存,不请求服务器。 - 协商缓存:
Last-Modified
/If-Modified-Since
,ETag
/If-None-Match
。浏览器询问服务器资源是否过期,未过期则返回 304 状态码。
- 强缓存:
- 压缩:
- Gzip:普遍支持的文本压缩格式。
- Brotli(
.br
):比 Gzip 压缩率更高,现代浏览器都支持。首选方案。
- 图片优化:
- 格式:使用 WebP、AVIF 等现代格式,在同等质量下体积更小。
- 懒加载:使用
loading="lazy"
属性,图片进入视口附近再加载。 - 响应式图片:使用
srcset
和sizes
属性,为不同屏幕尺寸提供不同大小的图片。 - 雪碧图 (CSS Sprites):合并多个小图标为一张大图,通过
background-position
显示,减少 HTTP 请求(在 HTTP/2 下收益变小)。
渲染层面:
- 防抖 (Debounce):连续事件结束后等待一段时间再执行(如搜索框输入)。
- 节流 (Throttle):连续事件中,每隔一段时间执行一次(如滚动事件)。
- 虚拟列表 (Virtual List):只渲染可视区域及其附近的元素,大幅减少 DOM 节点数量,用于超长列表渲染。
- 懒执行:将非首屏关键的逻辑(如数据分析、复杂动画)延迟执行或按需执行。
- Web Worker:将计算密集型任务(如数据处理、图像操作)放到后台线程执行,不阻塞主线程的渲染和交互。
框架层面 (Vue):
- 异步组件:使用
defineAsyncComponent
延迟加载非首屏组件,减少初始包体积。 <KeepAlive>
:缓存非活动组件实例,避免重复渲染(如 Tab 切换)。computed
vswatch
:computed
:用于派生状态,依赖变化时才重新计算,有缓存。优先使用。watch
:用于监听数据变化执行副作用(如请求数据、操作 DOM),无缓存。
3. B端项目架构通识
权限系统
-
RBAC 模型 (Role-Based Access Control):
- 用户 (User) -> 角色 (Role) -> 权限 (Permission)。
- 用户关联角色,角色关联权限(通常是菜单、路由、按钮的标识符)。修改用户的角色即可批量修改其权限。
-
前端实现:
- 路由权限:
- 用户登录后,后端返回其拥有的权限列表。
- 方案A (动态路由):前端只定义基本路由(如
/login
)。根据用户权限,动态调用router.addRoute()
添加有权限访问的路由。更安全,更推荐。 - 方案B (全局守卫):前端定义所有路由。在全局路由守卫
router.beforeEach
中判断目标路由是否需要权限以及用户是否有权访问,若无权则跳转到 403 页面。
- 按钮级权限:
- 通常封装一个自定义指令
v-permission
。
// main.js app.directive('permission', {mounted(el, binding) {const { value } = binding; // 指令的值,如 'user:add'const permissions = store.getters.permissions; // 从Vuex/Pinia中获取用户权限列表if (value && !permissions.includes(value)) {el.parentNode && el.parentNode.removeChild(el); // 无权限则移除按钮}} });// 在组件中使用 <button v-permission="'user:delete'">删除用户</button>
- 通常封装一个自定义指令
- 路由权限:
状态管理
复杂中台项目状态管理的关键是模块化 (Modularization)。
-
Pinia (Vue 3 推荐):
- 天然支持模块化,每个 Store 都是一个独立的模块。
- 定义多个 Store(如
useUserStore
,useAppStore
,useProductStore
),在组件中按需引入。 - 结构清晰,TS 支持极好。
-
Vuex (Vue 2):
- 通过
modules
选项进行模块化拆分。
const moduleA = { state: { ... }, mutations: { ... }, ... }; const moduleB = { state: { ... }, ... }; const store = new Vuex.Store({modules: { a: moduleA, b: moduleB } });
- 通过
组件化设计
-
原则:高内聚、低耦合
- 高内聚:一个组件只专注于完成一件事,并且把这件事做好。它的所有相关代码(视图、逻辑、样式)都紧密地组织在一起。
- 低耦合:组件应该尽可能少地依赖外部环境(父组件、全局状态),通过清晰的
props
和events
接口与外界通信。
-
业务组件库设计与开发流程:
- 技术选型:基于
Vue
+TypeScript
+SCSS
。构建工具选择Vite
(开发体验好) 或Webpack
。 - 脚手架:使用
Vue CLI
或Vite
的lib
模式创建项目。或使用更专业的 Bit 或 Lerna 管理多组件。 - 开发规范:
- 目录结构:每个组件一个单独的文件夹(包含
.vue
,.ts
,.scss
文件)。 - 代码规范:统一集成 ESLint, Prettier, Stylelint。
- 文档:使用 Storybook 或 VitePress 为每个组件编写交互式文档和演示用例。这是重中之重。
- 目录结构:每个组件一个单独的文件夹(包含
- 组件设计:
- API 设计:
props
设计要直观且具扩展性,考虑默认值、类型校验、必填项。 - 插槽 (Slots):提供足够的插槽来支持内容定制化。
- 主题定制:提供 CSS 变量或 SCSS 变量来实现主题和样式的定制。
- API 设计:
- 构建与发布:配置构建脚本,生成
umd
和es
格式产物。通过npm publish
发布到私有或公共 npm 仓库。 - 持续集成:自动化执行测试、构建和发布流程。
- 技术选型:基于