先取女王后取经,不负如来不负卿--微前端做的架构拆分又嫌弃单页应用(CSR)访问速度过慢?不行,我全都要
背景
初期架构:敏捷优先的技术选择
在项目启动阶段,为快速响应业务迭代需求,我们可能会采用客户端渲染(CSR)架构搭建核心应用,配合微前端框架qiankun实现多团队并行开发与模块解耦。该方案有效支撑了业务高速发展期的高频需求交付,但伴随系统复杂度提升,技术债逐渐显现。
优化瓶颈:CSR的天花板效应
随着用项目稳定性增长与项目优化需求升级,首屏性能(FCP>3s)、搜索引擎可见性、静态资源加载效率等指标成为关键瓶颈。深度分析发现:CSR强依赖浏览器端(即使配合cdn)JS解析的特性,导致首屏渲染链路冗长、TTFB时间不可控,且静态内容缺失严重制约SEO效果。我们测算得出,纯CSR架构的性能优化空间不足较低,需突破性技术重构。
破局之道:混合渲染架构升级
基于「渐进增强」理念,我们重构技术底座为CSR+SSR+SSG三元架构:
- 动态业务层:保留CSR主导的交互密集型模块(ecs 、 vpc 各子应用业务层)
- 服务端渲染层:对SEO强依赖页面(官网首页/产品介绍页)启用SSR|ISR
- 静态生成层:高频访问且内容稳定的帮助中心文档系统采用SSG预生成
技术前瞻:渲染粒度的精细化管控
当前架构支持按路由级/组件级动态选择渲染模式,配合边缘计算节点实现动静资源智能分发。将进一步探索ISR(增量静态再生)、DSG(按需生成)等前沿方案,构建弹性渲染中台。
一、升级策略
1.1 架构升级决策
-
Umi的定位与局限性:中后台场景的「效率优先」与SSR的「断代危机」
- 开发提效:Umi4+Ant Design组合仍是中后台系统的好搭档,内置路由/状态管理等工具链可节省开发周期快速实现CRUD/权限管理等高频需求
- SSR技术弃用:Umi4官方已停止维护SSR模块(文档明确标注「不建议生产使用」),导致服务端渲染能力被锁定在旧版本
-
Next.js的破局优势:全栈渲染的「工业级解决方案」
- 渲染模式全覆盖:成熟的SSR/SSG功能、社区生态
1.2 架构迁移路径
-
阶段二:主应用Next化
-
客户端渲染生态替换
- qiankun插件
- 状态管理库
- 请求管理
- ......
-
微前端融合方案:
- 在Next主应用中集成qiankun,通过自定义
loadMicroApp
完成子应用服务端注册 - 客户端动态加载子应用(文件路由系统 +
middleware
路由劫持)
- 在Next主应用中集成qiankun,通过自定义
-
核心模块重构:
- 首页/产品页
- 登录鉴权模块
- 控制台混合渲染
-
-
阶段二:文档系统SSG化
- 使用
getStaticPaths
+getStaticProps
预生成所有帮助文档页 - 配合CDN边缘缓存,实现访问速度≤300ms
- 内容更新定时触发,自动重建增量页面
- 使用
二. 框架及其层次关系
定义模块内部类的结构和依赖关系
2.1 生态迁移关系
2.2 微前端架构图
2.3 Next15 中的 use server / use client
-
'use server':标记服务器端逻辑(Server Actions 或模块),不渲染 UI。
-
'use client':标记客户端组件,支持交互,通常用于 CSR 或混合渲染。
-
它们与渲染方式(SSR/SSG/ISR/CSR)是正交的,作用是区分代码执行位置,而不是直接决定渲染模式。
-
让整个页面是服务端组件,这样就比较灵活的引入客户端组件和服务端组件
- 服务端组件不可以直接作为客户端组件的子组件
- 服务端组件可以作为客户端组件的属性进行使用(props)
- 尽量将客户端组件下移到组件树中
2.4 Next15 中的 App Router 中 SSG、SSR、ISR 和 CSR 的使用指南
-
静态生成 (SSG - Static Site Generation)
Next.js 15 的 App Router 通过组件类型(服务端/客户端)抽象了传统渲染模式,但 SSG、SSR、 CSR、ISR 的底层机制仍然存在,但是通过混合渲染模式开发者只需关注“服务端逻辑”与“客户端逻辑”的划分,而非显式选择渲染模式
-
静态生成 (SSG - Static Site Generation)
- 在 App Router 中,默认情况下所有路由都是静态生成的。
- 在构建时生成页面
- 使用
fetch
但未指定动态缓存(默认cache: 'force-cache'
)
// 默认渲染就是SSG export default async function Page() { return <div>SSG</div>; } // or // 这个函数在构建时被调用 // app/products/[id]/page.js export async function generateStaticParams() { const res = await fetch('https://api/products'); // 构建时获取所有产品ID const products = await res.json(); return products.map((p) => ({ id: p.id })); // 预生成 /products/1、/products/2 等静态页 } // 使用 `generateStaticParams` 返回的 `params` 静态生成此页面的多个版本 export default async function Page({ params, }: { params: Promise<{ slug: string }> }) { const { slug } = await params // ... }
-
服务器端渲染 (SSR - Server-Side Rendering)
- 要为页面使用服务端渲染,你需要导出一个名为 dynamic = 'force-dynamic' 的字段
- 或者通过
cache: 'no-store'
禁用缓存每次请求时重新生成
export const dynamic = 'force-dynamic'; // 禁用静态生成,每次请求都渲染 export default function Page({ data }) { // 渲染数据... const res = await fetch('https://api/user', { cache: 'no-store', // 或者:禁用缓存,触发每次请求时重新生成 headers: { Cookie: request.headers.get('Cookie') } // 依赖请求头 }); const user = await res.json(); return <div>用户信息:{user.name}</div>; }
-
客户端渲染(CSR - Client-Side Rendering)
-
实现客户端渲染
- 在页面中使用 React 的
useState()
、useEffect()
钩子 - 使用数据获取库,如 SWR,useRequest
- 在页面中使用 React 的
-
在交互时渲染新的内容
'use client'; import React, { useState, useEffect } from 'react' export function Page() { const [data, setData] = useState(null) useEffect(() => { const fetchData = async () => { const response = await fetch('https://api.example.com/data') if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } const result = await response.json() setData(result) } fetchData().catch((e) => { // 根据需要处理错误 console.error('获取数据时发生错误:', e) }) }, []) return <p>{data ? `你的数据:${data}` : '加载中...'}</p> }
-
-
增量静态渲染 (ISR - Incremental Static Regeneration)
-
无需重新构建整个网站就能更新静态内容
-
是 SSG 和 SSR 的混合体
-
工作原理:
- 在
next build
期间,生成所有已知的页面 - 对这些页面的所有请求都被缓存并立即响应
- 1H 过后,下一个请求仍然会显示缓存的 (陈旧的) 页面
- 缓存失效,并在后台开始生成页面的新版本
- 一旦成功生成,Next.js 将显示并缓存更新后的页面
- 如果请求,Next.js 将按需生成并缓存这个页面
- 在
// pages/products.js export default function Products({ products }) { return ( <ul> {products.map((product) => ( <li key={product.id}>{product.name}</li> ))} </ul> ); } export async function generateStaticParams() { const res = await fetch('https://api.example.com/products'); const products = await res.json(); return { props: { products, }, // 每1小时重新验证一次 revalidate: 60 * 60 * 60, }; } // or // 或者不用fetch export const revalidate= 60 * 60 * 60; export default async function Home() { const res = await fetch("http://localhost:3000/api/menudata?city=1", { next:{ revalidate:10 } }).then( (res) => res.json() ); return ( <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]"> {JSON.stringify(res)} </div> ); }
-
四、应用混合渲染设计
Next.js 推荐一种混合方法,允许你根据应用程序中每个页面的需求使用服务器端渲染、静态站点生成和客户端渲染的组合
-
next 中实现 @umi/plugin-qiankun
子应用通过qiankun接受的数据都是静态的,而qiankun本身的通信方式
initGlobalState
与umi/model
的使用方式大相径庭。如果采用qiankun本身的通信方式就需要一定的子应用改造成本。综上,决定自己实现 @umi/plugin-qiankun 插件-
从@umi/plugin-qiankun 到 qiankun:@umi/plugin-qiankun源码分析
- 根据 name 完成子应用的注册和切换
- 通过 stateforSlave(useModel('@@qiankunStateFromMaster')) 完成子应用状态更新
-
技术实现细节
const containerRef = useRef<HTMLDivElement>(null); const microAppRef = useRef<MicroAppType>(null); const register = async () => { // 注册微应用 需要动态注入 const qiankun = await import('qiankun'); const configuration = { globalContext: window, }; if (!containerRef.current) return; microAppRef.current = qiankun.loadMicroApp( { name: 'tenant', entry: 'http://dev4.msxfyun.test:8000/tenant', container: containerRef.current!, props: {}, }, configuration ); }; useEffect(() => { if (microAppRef.current) { microAppRef.current?.update?.({ count, }); } }, [count]); useMount(register);
-
-
微前端通信设计
暂时无法在唯科之家2.0文档外展示此内容
-
首页渲染策略
组件 渲染方式 原因 HomeLayout SSG 静态内容,无需动态数据。 HomeHeader SSR 包含动态用户状态,需快速渲染以优化 SEO 和首屏体验。 HomeFooter SSG 静态内容,无需动态渲染。 HomeSearchBar UI SSR 初始 HTML 需快速呈现,SEO 友好。 HomeSearchBar 交互 CSR 实时输入和动态结果需客户端处理。 HomeSearch Results ISR 热门查询可缓存,定期更新平衡性能和动态性。 UserInfo UI SSR 依赖用户会话,服务器端渲染确保快速呈现。 UserInfo 交互 CSR 模态框和更新用户信息需客户端交互。
-
登录页面
-
主账户登录
组件 渲染方式 原因 Layout SSG 静态内容,无需动态数据 LoginForm UI SSR 初始HTML需快速呈现 LoginForm Client CSR 登录表单需要与客户端交互 TypeChangeButton Clitnt CSR 需要同步浏览器参数 -
其他登录方式
组件 渲染方式 原因 Layout SSG 静态内容,无需动态数据 Header UI SSR 初始HTML需快速呈现 HeaderClinet CSR 切换到首页、主账户登录、注册等需要与浏览器交互 LoginForm UI SSR 初始HTML需快速呈现 LoginFormClient CSR 登录表单需要与客户端交互
-
workbench工作台页面
组件 渲染方式 原因 Layout SSG 静态内容,无需动态数据 快速导航 UI SSR 数据通过调用接口呈现 搜索输入框 Client CSR 搜索数据交互 快捷导航列表 Client CSR 数据获取与路径跳转 最近访问 UI SSG 静态内容,无需动态数据 最近访问列表 Client CSR 客户端交互,数据获取与路径跳转 NavItem 组件 Client CSR 数据渲染 资源概览 UI SSG 静态内容,无需动态数据 ResourceItem 组件 Client CSR 数据渲染和功能交互