@modern-js/main-doc 2.58.2 → 2.59.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. package/docs/en/apis/app/runtime/core/use-loader.mdx +1 -1
  2. package/docs/en/community/blog/_meta.json +1 -6
  3. package/docs/en/components/deploy.mdx +1 -1
  4. package/docs/en/components/init-app.mdx +0 -1
  5. package/docs/en/components/init-rspack-app.mdx +0 -1
  6. package/docs/en/components/ssr-monitor.mdx +3 -0
  7. package/docs/en/configure/_meta.json +1 -1
  8. package/docs/en/configure/app/output/ssg.mdx +52 -141
  9. package/docs/en/configure/app/tools/swc.mdx +1 -1
  10. package/docs/en/configure/app/tools/tailwindcss.mdx +1 -1
  11. package/docs/en/guides/advanced-features/_meta.json +0 -8
  12. package/docs/en/guides/advanced-features/bff/_meta.json +1 -6
  13. package/docs/en/guides/advanced-features/rsbuild-plugin.mdx +2 -2
  14. package/docs/en/guides/advanced-features/rspack-start.mdx +7 -22
  15. package/docs/en/guides/basic-features/_meta.json +31 -9
  16. package/docs/en/guides/basic-features/css/_meta.json +1 -0
  17. package/docs/en/guides/basic-features/css/css-in-js.mdx +34 -0
  18. package/docs/en/guides/basic-features/{css-modules.mdx → css/css-modules.mdx} +0 -4
  19. package/docs/en/guides/basic-features/css/css.mdx +25 -0
  20. package/docs/en/guides/basic-features/{css.mdx → css/tailwindcss.mdx} +5 -66
  21. package/docs/en/guides/basic-features/data/_meta.json +1 -4
  22. package/docs/en/guides/basic-features/data/data-fetch.mdx +134 -235
  23. package/docs/en/guides/basic-features/data/data-write.mdx +66 -77
  24. package/docs/en/guides/basic-features/debug/_meta.json +1 -0
  25. package/docs/en/guides/basic-features/debug/rsdoctor.mdx +57 -0
  26. package/docs/en/guides/{advanced-features → basic-features/debug}/using-storybook.mdx +2 -0
  27. package/docs/en/guides/basic-features/render/_meta.json +1 -0
  28. package/docs/en/guides/basic-features/render/ssg.mdx +208 -0
  29. package/docs/en/guides/{advanced-features/ssr/cache.mdx → basic-features/render/ssr-cache.mdx} +38 -50
  30. package/docs/en/guides/basic-features/render/ssr.mdx +301 -0
  31. package/docs/en/guides/basic-features/render/streaming-ssr.mdx +230 -0
  32. package/docs/en/guides/basic-features/routes.mdx +275 -263
  33. package/docs/en/guides/basic-features/static-assets/_meta.json +1 -0
  34. package/docs/en/guides/basic-features/static-assets.mdx +1 -1
  35. package/docs/en/guides/basic-features/testing/_meta.json +1 -0
  36. package/docs/en/guides/basic-features/testing/cypress.mdx +95 -0
  37. package/docs/en/guides/basic-features/testing/jest.mdx +148 -0
  38. package/docs/en/guides/basic-features/testing/playwright.mdx +111 -0
  39. package/docs/en/guides/basic-features/testing/vitest.mdx +100 -0
  40. package/docs/en/guides/concept/_meta.json +1 -4
  41. package/docs/en/guides/concept/entries.mdx +78 -47
  42. package/docs/en/guides/get-started/_meta.json +1 -7
  43. package/docs/en/guides/get-started/introduction.mdx +1 -1
  44. package/docs/en/guides/get-started/quick-start.mdx +1 -2
  45. package/docs/en/guides/get-started/tech-stack.mdx +4 -6
  46. package/docs/en/guides/get-started/upgrade.mdx +16 -2
  47. package/docs/en/guides/topic-detail/framework-plugin/_meta.json +1 -1
  48. package/docs/en/guides/topic-detail/generator/_meta.json +1 -1
  49. package/docs/en/guides/topic-detail/generator/create/_meta.json +1 -5
  50. package/docs/en/guides/topic-detail/generator/create/config.mdx +0 -10
  51. package/docs/en/guides/topic-detail/generator/create/use.mdx +0 -1
  52. package/docs/en/guides/topic-detail/generator/new/_meta.json +1 -5
  53. package/docs/en/guides/topic-detail/generator/plugin/_meta.json +1 -1
  54. package/docs/en/guides/troubleshooting/_meta.json +1 -6
  55. package/docs/en/tutorials/first-app/c03-css.mdx +1 -1
  56. package/docs/zh/apis/app/runtime/core/use-loader.mdx +1 -1
  57. package/docs/zh/community/blog/_meta.json +1 -6
  58. package/docs/zh/components/deploy.mdx +1 -1
  59. package/docs/zh/components/init-app.mdx +0 -1
  60. package/docs/zh/components/init-rspack-app.mdx +0 -1
  61. package/docs/zh/components/ssr-monitor.mdx +3 -0
  62. package/docs/zh/configure/_meta.json +1 -1
  63. package/docs/zh/configure/app/output/ssg.mdx +49 -139
  64. package/docs/zh/configure/app/tools/swc.mdx +1 -1
  65. package/docs/zh/configure/app/tools/tailwindcss.mdx +1 -1
  66. package/docs/zh/guides/advanced-features/_meta.json +0 -8
  67. package/docs/zh/guides/advanced-features/bff/_meta.json +1 -6
  68. package/docs/zh/guides/advanced-features/rsbuild-plugin.mdx +2 -2
  69. package/docs/zh/guides/advanced-features/rspack-start.mdx +8 -24
  70. package/docs/zh/guides/basic-features/_meta.json +31 -9
  71. package/docs/zh/guides/basic-features/css/_meta.json +1 -0
  72. package/docs/zh/guides/basic-features/css/css-in-js.mdx +34 -0
  73. package/docs/zh/guides/basic-features/css/css.mdx +25 -0
  74. package/docs/zh/guides/basic-features/{css.mdx → css/tailwindcss.mdx} +3 -64
  75. package/docs/zh/guides/basic-features/data/_meta.json +1 -4
  76. package/docs/zh/guides/basic-features/data/data-fetch.mdx +98 -214
  77. package/docs/zh/guides/basic-features/data/data-write.mdx +54 -55
  78. package/docs/zh/guides/basic-features/debug/_meta.json +1 -0
  79. package/docs/zh/guides/basic-features/debug/rsdoctor.mdx +57 -0
  80. package/docs/zh/guides/{advanced-features → basic-features/debug}/using-storybook.mdx +1 -1
  81. package/docs/zh/guides/basic-features/render/_meta.json +1 -0
  82. package/docs/zh/guides/basic-features/render/ssg.mdx +210 -0
  83. package/docs/zh/guides/{advanced-features/ssr/cache.mdx → basic-features/render/ssr-cache.mdx} +16 -26
  84. package/docs/zh/guides/basic-features/render/ssr.mdx +309 -0
  85. package/docs/zh/guides/{advanced-features/ssr/stream.mdx → basic-features/render/streaming-ssr.mdx} +22 -37
  86. package/docs/zh/guides/basic-features/routes.mdx +252 -237
  87. package/docs/zh/guides/basic-features/static-assets/_meta.json +1 -0
  88. package/docs/zh/guides/basic-features/static-assets.mdx +2 -6
  89. package/docs/zh/guides/basic-features/testing/_meta.json +1 -0
  90. package/docs/zh/guides/basic-features/testing/cypress.mdx +95 -0
  91. package/docs/zh/guides/basic-features/testing/jest.mdx +148 -0
  92. package/docs/zh/guides/basic-features/testing/playwright.mdx +112 -0
  93. package/docs/zh/guides/basic-features/testing/vitest.mdx +100 -0
  94. package/docs/zh/guides/concept/_meta.json +1 -4
  95. package/docs/zh/guides/concept/entries.mdx +80 -58
  96. package/docs/zh/guides/get-started/_meta.json +1 -7
  97. package/docs/zh/guides/get-started/introduction.mdx +2 -2
  98. package/docs/zh/guides/get-started/quick-start.mdx +1 -2
  99. package/docs/zh/guides/get-started/tech-stack.mdx +8 -10
  100. package/docs/zh/guides/get-started/upgrade.mdx +15 -1
  101. package/docs/zh/guides/topic-detail/framework-plugin/_meta.json +1 -1
  102. package/docs/zh/guides/topic-detail/generator/_meta.json +1 -1
  103. package/docs/zh/guides/topic-detail/generator/create/_meta.json +1 -5
  104. package/docs/zh/guides/topic-detail/generator/create/config.mdx +0 -10
  105. package/docs/zh/guides/topic-detail/generator/create/use.mdx +0 -1
  106. package/docs/zh/guides/topic-detail/generator/new/_meta.json +1 -5
  107. package/docs/zh/guides/topic-detail/generator/plugin/_meta.json +1 -1
  108. package/docs/zh/guides/troubleshooting/_meta.json +1 -6
  109. package/docs/zh/tutorials/first-app/c03-css.mdx +1 -1
  110. package/i18n.json +16 -4
  111. package/package.json +6 -6
  112. package/rspress.config.ts +1 -1
  113. package/src/components/ContentCard/index.tsx +1 -1
  114. package/src/components/Sandpack/index.tsx +1 -1
  115. package/src/components/ShowcaseList/index.tsx +1 -1
  116. package/src/i18n/index.ts +1 -1
  117. package/src/pages/index.tsx +2 -2
  118. package/docs/en/apis/app/hooks/config/storybook.mdx +0 -37
  119. package/docs/en/guides/advanced-features/ssg.mdx +0 -116
  120. package/docs/en/guides/advanced-features/ssr/_meta.json +0 -5
  121. package/docs/en/guides/advanced-features/ssr/index.mdx +0 -23
  122. package/docs/en/guides/advanced-features/ssr/stream.mdx +0 -248
  123. package/docs/en/guides/advanced-features/ssr/usage.mdx +0 -341
  124. package/docs/en/guides/advanced-features/ssr.mdx +0 -555
  125. package/docs/zh/apis/app/hooks/config/storybook.mdx +0 -38
  126. package/docs/zh/guides/advanced-features/ssg.mdx +0 -116
  127. package/docs/zh/guides/advanced-features/ssr/_meta.json +0 -5
  128. package/docs/zh/guides/advanced-features/ssr/index.mdx +0 -23
  129. package/docs/zh/guides/advanced-features/ssr/usage.mdx +0 -329
  130. /package/docs/en/guides/basic-features/{mock.mdx → debug/mock.mdx} +0 -0
  131. /package/docs/en/guides/basic-features/{proxy.mdx → debug/proxy.mdx} +0 -0
  132. /package/docs/en/guides/basic-features/{json-files.mdx → static-assets/json-files.mdx} +0 -0
  133. /package/docs/en/guides/basic-features/{svg-assets.mdx → static-assets/svg-assets.mdx} +0 -0
  134. /package/docs/en/guides/basic-features/{wasm-assets.mdx → static-assets/wasm-assets.mdx} +0 -0
  135. /package/docs/zh/guides/basic-features/{css-modules.mdx → css/css-modules.mdx} +0 -0
  136. /package/docs/zh/guides/basic-features/{mock.mdx → debug/mock.mdx} +0 -0
  137. /package/docs/zh/guides/basic-features/{proxy.mdx → debug/proxy.mdx} +0 -0
  138. /package/docs/zh/guides/basic-features/{json-files.mdx → static-assets/json-files.mdx} +0 -0
  139. /package/docs/zh/guides/basic-features/{svg-assets.mdx → static-assets/svg-assets.mdx} +0 -0
  140. /package/docs/zh/guides/basic-features/{wasm-assets.mdx → static-assets/wasm-assets.mdx} +0 -0
@@ -1,116 +0,0 @@
1
- ---
2
- sidebar_position: 5
3
- ---
4
-
5
- # 静态站点生成
6
-
7
- SSG(Static Site Generation)是一种基于数据与模板,在构建时渲染完整静态网页的技术解决方案。
8
-
9
- 我们首先需要执行 `pnpm run new` 启用 SSG 功能:
10
-
11
- ```bash
12
- ? 请选择你想要的操作 启用可选功能
13
- ? 请选择功能名称 启用「SSG」功能
14
- ```
15
-
16
- 执行命令后,在 `modern.config.ts` 中注册 SSG 插件:
17
-
18
- ```ts title="modern.config.ts"
19
- import { ssgPlugin } from '@modern-js/plugin-ssg';
20
-
21
- export default defineConfig({
22
- output: {
23
- ssg: true,
24
- },
25
- plugins: [..., ssgPlugin()],
26
- });
27
- ```
28
-
29
- SSG 在**约定式路由**和**自控式路由**下的使用方式不同。
30
-
31
- ### 在约定式路由中使用
32
-
33
- **约定式路由**中, Modern.js 根据入口下的文件结构生成路由,因此框架能够收集完整的路由信息。
34
-
35
- 例如,以下是一个使用约定式路由的项目目录结构:
36
-
37
- ```
38
- .
39
- ├── src
40
- │ └── routes
41
- │ ├── layout.tsx
42
- │ ├── page.tsx
43
- │ └── user
44
- │ ├── layout.tsx
45
- │ ├── page.tsx
46
- │ └── profile
47
- │ └── page.tsx
48
- ```
49
-
50
- 上述文件目录将会生成以下三条路由:
51
-
52
- - `/`
53
- - `/user`
54
- - `/user/profile`
55
-
56
- :::note
57
- 如果还不了解约定式路由的规则,可以先查看[路由方案](/guides/basic-features/routes)。
58
-
59
- :::
60
-
61
- 在 `src/routes/page.tsx` 中添加组件代码:
62
-
63
- ```jsx title="src/routes/page.tsx"
64
- export default () => {
65
- return <div>Index Page</div>;
66
- };
67
- ```
68
-
69
- SSG 也是在 Node.js 环境渲染页面,因此我们可以在**开发阶段开启 SSR**,提前在暴露代码问题,验证 SSG 渲染效果:
70
-
71
- ```ts title="modern.config.ts"
72
- export default defineConfig({
73
- server: {
74
- ssr: process.env.NODE_ENV === 'development',
75
- }
76
- }
77
- ```
78
-
79
- 在项目根路径下执行 `pnpm run dev` 命令,查看 `dist/` 目录,此时只生成一个 HTML 文件 `main/index.html`。
80
-
81
- 在项目根路径下执行 `pnpm run build` 命令,构建完成后,查看 `dist/` 目录,此时生成 `main/index.html`、`main/user/index.html` 和 `main/user/profile/index.html` 三个 HTML 文件,内容分别对应上述三条路由。
82
-
83
- **约定式路由**中的每一条路由,都会生成一个单独的 HTML 文件。查看 `main/index.html`,可以发现包含 `Index Page` 的文本内容,这正是 SSG 的效果。
84
-
85
- 执行 `pnpm run serve` 启动项目后,访问页面,在浏览器我们工具的 Network 窗口,查看请求返回的文档,文档包含组件渲染后的完整页面内容。
86
-
87
- ### 在自控式路由中使用
88
-
89
- **自控式路由**是通过组件代码定义路由,需要应用运行起来才能获取准确的路由信息。因此,无法开箱即用的使用 SSG 功能。此时需要用户提前告知 Modern.js 框架,哪些路由需要开启 SSG 功能。
90
-
91
- 例如有以下代码,包含多条路由,设置 `output.ssg` 为 `true` 时,默认只会渲染入口路由即 `/`:
92
-
93
- import SelfRouteExample from '@site-docs/components/self-route-example';
94
-
95
- <SelfRouteExample />
96
-
97
- 如果我们希望同时开启 `/about` 的 SSG 功能,可以配置 `output.ssg`,告知 Modern.js 开启指定路由的 SSG 功能。
98
-
99
- ```ts title="modern.config.ts"
100
- export default defineConfig({
101
- output: {
102
- ssg: {
103
- routes: ['/', '/about'],
104
- },
105
- },
106
- });
107
- ```
108
-
109
- 执行 `pnpm run build` 与 `pnpm run serve` 后,访问 `http://localhost:8080/about`,在 Preview 视图中可以看到页面已经完成渲染。
110
-
111
- 查看构建产物文件,可以看到 `dist/` 目录中,新增了一个 `main/about/index.html` 文件。
112
-
113
- :::info
114
- 以上仅介绍了单入口的情况,更多相关内容可以查看 [API 文档](/configure/app/output/ssg)。
115
-
116
- :::
@@ -1,5 +0,0 @@
1
- [
2
- "usage",
3
- "stream",
4
- "cache"
5
- ]
@@ -1,23 +0,0 @@
1
- # 服务端渲染
2
-
3
- 通过在服务器端将网页的 HTML 内容渲染成完整的网页(Server-Side Rendering,简称 SSR),然后将生成的网页发送到客户端,客户端只需要显示网页即可,不需要再进行额外的渲染。
4
-
5
- 它主要的优势在于
6
-
7
- - 提高首屏加载速度:SSR 可以在服务器端生成完整的网页,客户端只需要下载网页内容即可,不需要再进行额外的渲染,从而提高了首屏加载速度。
8
- - 提高用户体验:SSR 可以提高网页的响应速度,从而提高用户体验。
9
- - 有利于 SEO:SSR 可以生成完整的 HTML 内容,搜索引擎可以直接索引 HTML 内容,从而提高网站的排名。
10
-
11
- 如果你有以下场景的需求,开发者可以考虑使用 SSR 来渲染你的页面:
12
-
13
- 1. 对首屏加载速度要求较高的网站,如电商网站、新闻网站等。
14
- 2. 对用户体验要求较高的网站,如社交网站、游戏网站等。
15
- 3. 对 SEO 要求较高的网站,如企业官网、博客等。
16
-
17
- 在 Modern.js 中,SSR 也是开箱即用的。开发者无需为 SSR 编写复杂的服务端逻辑,也无需关心 SSR 的运维,或是创建单独的服务。
18
-
19
- 除了开箱即用的 SSR 服务,为了保证开发者的开发体验,我们还具备:
20
-
21
- - 完备的 SSR 降级策略,保证页面能够安全运行。
22
- - 自动分割子路由,按需加载,减少首屏资源体积。
23
- - 内置缓存系统,解决服务端负载高的问题。
@@ -1,329 +0,0 @@
1
- ---
2
- sidebar_position: 1
3
- title: 基础使用
4
- ---
5
-
6
- # 基础使用
7
-
8
- 启用 SSR 非常简单,只需要设置 [`server.ssr`](/configure/app/server/ssr) 为 `true` 即可:
9
-
10
- ```ts title="modern.config.ts"
11
- import { defineConfig } from '@modern-js/app-tools';
12
-
13
- export default defineConfig({
14
- server: {
15
- ssr: true,
16
- },
17
- });
18
- ```
19
-
20
- ## SSR 时的数据获取
21
-
22
- Modern.js 中提供了 Data Loader,方便开发者在 SSR、CSR 下同构地获取数据。每个路由模块,如 `layout.tsx` 和 `page.tsx` 都可以定义自己的 Data Loader:
23
-
24
- ```ts title="src/routes/page.data.ts"
25
- export const loader = () => {
26
- return {
27
- message: 'Hello World',
28
- };
29
- };
30
- ```
31
-
32
- 在组件中可以通过 Hooks API 的方式获取 `loader` 函数返回的数据:
33
-
34
- ```tsx
35
- import { useLoaderData } from '@modern-js/runtime/router';
36
- export default () => {
37
- const data = useLoaderData();
38
- return <div>{data.message}</div>;
39
- };
40
- ```
41
-
42
- Modern.js 打破传统的 SSR 开发模式,提供了用户无感的 SSR 开发体验。并且提供了优雅的降级处理,一旦 SSR 请求失败,会自动降级在浏览器端重新发起请求。
43
-
44
- 不过,开发者仍然需要关注数据的兜底处理,例如 `null` 值或不符合预期的数据返回。避免在 SSR 时产生 React 渲染错误或是返回凌乱的渲染结果。
45
-
46
- :::info 补充信息
47
-
48
- 1. 当以客户端路由的方式请求页面时,Modern.js 会发送一个 HTTP 请求,服务端接收到请求后执行页面对应的 Data Loader 函数,然后将执行结果作为请求的响应返回浏览器。
49
-
50
- 2. 使用 Data Loader 时,数据获取发生在渲染前,Modern.js 也仍然支持在组件渲染时获取数据。更多相关内容可以查看[数据获取](/guides/basic-features/data/data-fetch)。
51
-
52
- :::
53
-
54
- ## 保持渲染一致
55
-
56
- 有些业务中,UI 展示会和用户设备有关,例如 [UA](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) 信息。如果处理不够仔细,此时很有可能出现不符合预期的渲染结果。
57
-
58
- 这里通过一个例子,演示当 SSR 与 CSR 渲染不一致时出现的问题,在组件中添加以下代码:
59
-
60
- ```tsx
61
- {
62
- typeof window !== 'undefined' ? <div>browser content</div> : null;
63
- }
64
- ```
65
-
66
- 启动应用后,访问页面,会发现浏览器控制台抛出警告信息:
67
-
68
- ```sh
69
- Warning: Expected server HTML to contain a matching <div> in <div>.
70
- ```
71
-
72
- 这是 React hydrate 结果与 SSR 渲染结果不一致造成的。虽然当前页面表现正常,但在复杂应用中,很有可能因此出现 DOM 层级混乱、样式混乱等问题。
73
-
74
- :::info
75
- 关于 hydrate (注水)逻辑请参考[这里](https://zh-hans.react.dev/reference/react-dom/hydrate)。
76
-
77
- :::
78
-
79
- 应用需要保持 SSR 与 CSR 渲染结果的一致性,如果存在不一致的情况,说明这部分内容无需在 SSR 中进行渲染。Modern.js 为这类在 SSR 中不需要渲染的内容提供 [`<NoSSR>` 工具组件](/apis/app/runtime/core/use-runtime-context):
80
-
81
- ```ts
82
- import { NoSSR } from '@modern-js/runtime/ssr';
83
- ```
84
-
85
- 在不需要进行 SSR 的元素外部,用 `NoSSR` 组件包裹:
86
-
87
- ```tsx
88
- <NoSSR>
89
- <div>client content</div>
90
- </NoSSR>
91
- ```
92
-
93
- 修改代码后,刷新页发现之前的 Waring 消失。打开浏览器开发者工具的 Network 窗口,查看返回的 HTML 文档是不包含 `NoSSR` 组件包裹的内容的。
94
-
95
- :::info 补充信息
96
- [`useRuntimeContext`](/apis/app/runtime/core/use-runtime-context) 可以获取完整的请求信息,可以利用它保证 SSR 与 CSR 的渲染结果一致。
97
-
98
- :::
99
-
100
- ## 关注内存泄漏
101
-
102
- :::warning 警告
103
- 在 SSR 场景下,开发者需要特别关注内存泄露问题,即使是微小的内存泄露,在大量的访问后也会对服务造成影响。
104
-
105
- :::
106
-
107
- SSR 时,浏览器的每次请求,都会触发服务端重新执行一次组件渲染逻辑。所以,需要避免在全局定义任何可能不断增长的数据结构,或在全局进行事件订阅,或创建不会被销毁的流。
108
-
109
- 例如以下代码,使用 [redux-observable](https://redux-observable.js.org/) 时,习惯了 CSR 的开发者通常会在组件中这样编码:
110
-
111
- ```tsx
112
- /* 代码仅作为示例,不可运行 */
113
- import { createEpicMiddleware, combineEpics } from 'redux-observable';
114
-
115
- const epicMiddleware = createEpicMiddleware();
116
- const rootEpic = combineEpics();
117
-
118
- export default function Test() {
119
- epicMiddleware.run(rootEpic);
120
- return <div>Hello Modern.js</div>;
121
- }
122
- ```
123
-
124
- 在组件外层创建 Middleware 实例 `epicMiddleware`,并在组件内部调用 `epicMiddleware.run`。
125
-
126
- 在浏览器端,这段代码不会造成任何问题,但是在 SSR 时,Middleware 实例会一直无法被销毁。每次渲染组件,调用 `epicMiddleware.run(rootEpic)` 时,都会在内部添加新的事件绑定,导致整个对象不断变大,最终对应用性能造成影响。
127
-
128
- CSR 中这类问题不易被发觉,因此从 CSR 切换到 SSR 时,如果不确定应用是否存在这类隐患,可以对应用进行压测。
129
-
130
- ## 收敛服务端数据
131
-
132
- 为了使浏览器端能够直接使用 SSR 阶段请求的数据,Modern.js 会将渲染过程中收集的数据与状态注入到 HTML 内。但是,CSR 应用常常存在接口数据量大、组件状态未收敛的情况,这时如果直接使用 SSR,渲染得到的 HTML 体积可能会存在过大的问题。此时,SSR 不仅无法为应用带来用户体验上的提升,反而可能起到相反的作用。
133
-
134
- 因此,使用 SSR 时,**开发者需要为应用做合理的瘦身**:
135
-
136
- 1. 关注首屏,SSR 中可以只请求首屏需要的数据,并在浏览器端渲染剩余的部分。
137
- 2. 将与渲染无关的数据,从接口返回数据中剔除。
138
-
139
- ## Serverless Pre-render
140
-
141
- :::warning
142
- x.43.0+ 已废弃,请使用 [SSR Cache](guides/advanced-features/ssr/cache) 替代
143
- :::
144
-
145
- Modern.js 提供 Serverless Pre-rendering (SPR) 这一特性来提升 SSR 性能。
146
-
147
- SPR 利用预渲染与缓存技术,为 SSR 页面提供静态 Web 的响应性能。它让 SSR 应用拥有静态 Web 页面的响应速度与稳定性,同时还能保持数据的动态更新。
148
-
149
- 在 Modern.js 中使用 SPR 非常简单,只需要在组件中新增 `PreRender` 组件,该组件所在的页面就会自动开启 SPR。
150
-
151
- 这里模拟一个使用 `useLoaderData` API 的组件,Data Loader 中的请求需要消耗 2s 时间。
152
-
153
- ```tsx title="page.data.ts"
154
- export const loader = async () => {
155
- await new Promise((resolve, reject) => {
156
- setTimeout(() => {
157
- resolve(null);
158
- }, 2000);
159
- });
160
-
161
- return {
162
- message: 'Hello Modern.js',
163
- };
164
- };
165
- ```
166
-
167
- ```tsx title="page.tsx"
168
- import { useLoaderData } from '@modern-js/runtime/router';
169
-
170
- export default () => {
171
- const data = useLoaderData();
172
- return <div>{data?.message}</div>;
173
- };
174
- ```
175
-
176
- 执行 `dev` 命令后,打开页面,可以明显的察觉到页面需要等到 2s 后才返回。
177
-
178
- 接下来使用 `PreRender` 组件来进行优化,该组件可以直接从 `@modern-js/runtime/ssr` 中导出:
179
-
180
- ```ts
181
- import { PreRender } from '@modern-js/runtime/ssr';
182
- ```
183
-
184
- 在路由组件内使用 `PreRender` 组件,并设置参数 `interval`,用于表示该次渲染结果的过期时间为 5s:
185
-
186
- ```tsx
187
- <PreRender interval={5} />
188
- ```
189
-
190
- 修改后,执行 `pnpm run build && pnpm run serve` 启动应用,并打开页面。
191
-
192
- 首次打开时,和之前的渲染并没有什么不同,同样存在 2s 延迟。点击刷新,页面瞬间打开,但此时,页面数据并没有因为刷新发生变化,这是因为缓存还没有过期。
193
-
194
- 等待 5s,重新刷新页面,页面的数据仍然没有变化。再一次刷新页面数据发生变化,但是页面仍然几乎是瞬间响应的。
195
- 这是因为在之前的请求时,SPR 已经在后台异步获取了新的渲染结果,本次请求到的页面是已经缓存在服务器中的版本。
196
-
197
- 可以想象,当 `interval` 设置为 1 时,用户可以在感知到实时数据的同时,拥有静态页面的响应体验。
198
-
199
- :::info 补充信息
200
- `PreRender` 的详细使用可以参考[这里](/apis/app/runtime/ssr/pre-render)。
201
-
202
- :::
203
-
204
- ## Treeshaking
205
-
206
- 开启 SSR 时,Modern.js 会用相同的入口,构建出 SSR Bundle 和 CSR Bundle 两份产物。因此,在 SSR Bundle 中存在 Web API,或是在 CSR Bundle 中存在 Node API 时,都可能导致运行出错。
207
-
208
- 在组件中引入 Web API,通常情况下是要做一些全局监听,或是获取浏览器相关的数据,例如:
209
-
210
- ```tsx
211
- document.addEventListener('load', () => {
212
- console.log('document load');
213
- });
214
- const App = () => {
215
- return <div>Hello World</div>;
216
- };
217
- export default App;
218
- ```
219
-
220
- 在组件文件中引入 Node API,通常情况下是因为使用了 `useLoader`,例如:
221
-
222
- ```ts
223
- import fse from 'fs-extra';
224
- import { useLoader } from '@modern-js/runtime'
225
-
226
- const App = () => {
227
- const { data } = useLoader(async () => {
228
- const file = fse.readFileSync('./myfile');
229
- return {
230
- ...
231
- };
232
- })
233
-
234
- return <div>Hello World</div>;
235
- };
236
- export default App;
237
- ```
238
-
239
- ### 环境变量区分
240
-
241
- 对于第一种情况,我们可以直接使用 Modern.js 内置的环境变量 `MODERN_TARGET` 进行判断,在构建时删除无用代码:
242
-
243
- ```ts
244
- if (process.env.MODERN_TARGET === 'browser') {
245
- document.addEventListener('load', () => {
246
- console.log('document load');
247
- });
248
- }
249
- ```
250
-
251
- 开发环境打包后,SSR 产物和 CSR 产物会被编译成以下内容。因此 SSR 环境中不会再因为 Web API 报错:
252
-
253
- ```ts
254
- // SSR 产物
255
- if (false) {
256
- }
257
-
258
- // CSR 产物
259
- if (true) {
260
- document.addEventListener('load', () => {
261
- console.log('document load');
262
- });
263
- }
264
- ```
265
-
266
- :::note
267
- 更多内容可以查看[环境变量](/guides/basic-features/env-vars)。
268
- :::
269
-
270
- ### 文件后缀区分
271
-
272
- 但例如第二种情况,在代码中引入了 `fs-extra`,它内部有使用了 Node API 的副作用,如果直接引用到组件中,会造成 CSR 加载报错。
273
-
274
- 环境变量的方式并不能在这种情况下生效,Modern.js 也支持通过 `.node.` 后缀的文件来区分 SSR Bundle 和 CSR Bundle 产物的打包文件。
275
-
276
- 可以创建同名的 `.ts` 和 `.node.ts` 文件做一层代理:
277
-
278
- ```ts title="compat.ts"
279
- export const readFileSync: any = () => {};
280
- ```
281
-
282
- ```ts title="compat.node.ts"
283
- export { readFileSync } from 'fs-extra';
284
- ```
285
-
286
- 在文件中直接引入 `./compat`,此时 SSR 环境下会优先使用 `.node.ts` 后缀的文件,CSR 环境下会使用 `.ts` 后缀的文件。
287
-
288
- ```ts title="App.tsx"
289
- import { readFileSync } from './compat'
290
-
291
- export const loader = () => {
292
- const file = readFileSync('./myfile');
293
- return {
294
- ...
295
- };
296
- };
297
- ```
298
-
299
- ### 独立文件
300
-
301
- 上述两种方式,都会为开发者带来一些心智负担。在真实的业务中,我们发现大多数的 Node / Web 代码混用都出现在数据请求中。
302
-
303
- 因此,Modern.js 基于[嵌套路由](/guides/basic-features/routes)开发设计了[更简单的方案](/guides/basic-features/data/data-fetch)来分离 CSR 和 SSR 的代码。
304
-
305
- 我们可以通过独立文件来分离**数据请求**与**组件代码**。在 `routes/page.tsx` 中编写组件逻辑,在 `routes/page.data.ts` 中编写数据请求逻辑。
306
-
307
- ```ts title="routes/page.tsx"
308
- export default Page = () => {
309
- return <div>Hello World<div>
310
- }
311
- ```
312
-
313
- ```ts title="routes/page.data.tsx"
314
- import fse from 'fs-extra';
315
- export const loader = () => {
316
- const file = fse.readFileSync('./myfile');
317
- return {
318
- ...
319
- };
320
- }
321
- ```
322
-
323
- ## 接口请求
324
-
325
- 在 SSR 中发起接口请求时,开发者有时自己封装了同构的请求工具。部分接口需要传递用户 Cookie,开发者可以通过 [`useRuntimeContext`](/guides/basic-features/data/data-fetch#route-loader) API 获取到请求头来实现。
326
-
327
- 需要注意的是,此时获取到的是 HTML 请求的请求头,不一定适用于接口请求,因此**千万不能**透传所有请求头。并且,一些后端接口,或是通用网关,会根据请求头中的信息做校验,全量透传容易出现各种难以排查的问题,推荐**按需透传**。
328
-
329
- 如果实在需要透传所有请求头,请务必过滤 `host` 字段。