[RFC] 优化 SSR 及 SSG 下的 HTML 生成 #12060
Replies: 2 comments 1 reply
-
这个是不是可以像 nextjs 一样提供 RootLayout 定制组件,让用户自定义 |
Beta Was this translation helpful? Give feedback.
-
以上这两个问题是我认为现在 umi 4 的 ssr 存在的最大问题。 |
Beta Was this translation helpful? Give feedback.
-
问题
目前 Umi 中的 HTML 生成一共有 4 套之多,分别是:
headScripts
、scripts
、styles
、links
、metas
、favicons
配置项,内部生成默认 HTML 内容,再调用modifyHTML
钩子,最后输出文件umi.server.js
产物的 middleware 返回 HTML,内容由 server 的默认 HTML 内容 +metadataLoader
的内容组成modifyExportHTMLFiles
阶段通过umi.server.js
获取对应路由的预渲染内容,然后将 helmet 的片段、body
中的片段以及html
之后的模板片段拼接到 1 的结果上HtmlWebpackPlugin
直接生成该 RFC 只看前 3 种,它们在 HTML 生成的行为差异大致如下:
metadataLoader
html
#root
html
上述差异主要会导致 3 个问题:
useId
值不同导致水合失败umi.server.js
提供的 middleware,其 HTML 产物内容和 SSG 不一致对于问题 2, @MadCcc 最初在 #12002 做了反向尝试,即把 SSG 渲染的根节点从
html
换成#root
,后来发现 React 官方是要求流从是一整个 HTML,转而采用正向方案,即 client 渲染也从html
开始的方案,目前仍存在 html lang 不一致的警告,详情可查看 #12002 。解决方案
整体思路是尽可能地对齐 SSR 及 SSG/client 的行为,大致如下:
metadataLoader
html
html
(SSG & SSR only)html
主要做 3 点改造:
scripts
& etc.),把配置值一起打进umi.server.js
modifyHTML
钩子,如果用到打出 warningexportStatic
时 ban 掉modifyExportHTMLFiles
钩子,如果用到打出 warningmodifyExportHTMLFiles
,但里面理论上只插入对水合无副作用的内容,比如head
中的标签,或者body
里的script
html
,套空的html
组件确保结构与 SSR 一致、不会影响注水即可新方案的已知问题:
modifyExportHTMLFiles
仅在 SSG 时执行,可能造成水合失败,需要用户自己保证metadataLoader
仅在 SSR 时执行,所以可能造成水合警告,需要用户自己保证(比如自定义了lang
导致 SSR/CSR 渲染结果不一致,但不会影响水合结果)以下为细分的改造点。
SSR 支持 HTML 相关的静态配置项
对于
head
中的配置项,getClientRootComponent
已有的metadata
数据应该可以描述,思路:server.tpl
里把相关配置序列化成metadata
配置项,传给requestHander
和generator
,关键位置:umi/packages/preset-umi/templates/server.tpl
Line 43 in 8d55ffb
generator
生成metadata
时从opts.metadata
读取作为默认值,注意解引用,关键位置:umi/packages/server/src/ssr.ts
Line 108 in 60896f5
对于
body
中的配置项,只能给getClientRootComponent
新增scripts
配置项了,思路:scripts
数据,关键位置:umi/packages/renderer-react/src/server.tsx
Line 20 in e63fed3
ban 掉 API
ssr
启用时 ban 掉modifyHTML
,关键位置:umi/packages/preset-umi/src/commands/dev/getMarkupArgs.ts
Lines 42 to 51 in c068d9a
opts.api.config.ssr
可以判断ssr
配置项是否开启opts.api.service.hooks.modifyHTML?.length
可以判断是否有其他插件注册了这个 API,有的话抛出警告ssr
启用但exportStatic
未启用时 ban 掉modifyExportHTMLFiles
,关键位置:umi/packages/preset-umi/src/commands/build.ts
Lines 185 to 194 in 13dd25b
步骤省略,和上面类似
SSG 的基础 HTML 改为 SSR 的结果
在 exportStatic 中,原逻辑是基于框架默认的 HTML 骨架,把 SSR HTML 结果中的 Helmet、body 和 html 外的模板 3 个部分插入到默认 HTML 中,但只要有换行之类的问题都会导致水合失败,所以这次尝试直接用 SSR 的 HTML 产物:
publicPath
的逻辑都挪到『SSR 支持 HTML 相关的静态配置项』的改造里,确保配置项生成的时候已经处理过相对路径了,关键位置:umi/packages/preset-umi/src/features/exportStatic/exportStatic.ts
Lines 162 to 220 in 60896f5
umi/packages/preset-umi/src/features/exportStatic/exportStatic.ts
Lines 154 to 160 in 60896f5
getPreRenderedHTML
移除对htmlTpl
的依赖,直接返回预渲染的内容作为 HTML,它已经是完整的了CSR hydrate 从
html
开始已在 #12002 跟进,在
hydrate
时走特殊逻辑,套上 HTML 外壳确保和 SSR 的结构一致,diff:https://github.com/umijs/umi/pull/12002/files#diff-b555a0512a6215972d217c16fb3b0b0389d4c8c1654d15609cbd0c0c7d8f26f1L339-R345Beta Was this translation helpful? Give feedback.
All reactions