@esmx/core 3.0.0-rc.60 → 3.0.0-rc.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/README.zh-CN.md +4 -4
- package/dist/app.d.ts +27 -27
- package/dist/core.d.ts +274 -272
- package/dist/core.mjs +235 -232
- package/dist/pack-config.d.ts +92 -92
- package/dist/render-context.d.ts +465 -465
- package/dist/render-context.mjs +338 -338
- package/dist/utils/cache.d.ts +15 -15
- package/dist/utils/import-map.d.ts +31 -1
- package/dist/utils/import-map.mjs +18 -0
- package/dist/utils/import-map.test.mjs +577 -1
- package/dist/utils/middleware.d.ts +19 -19
- package/dist/utils/static-import-lexer.d.ts +12 -12
- package/dist/utils/static-import-lexer.mjs +1 -1
- package/package.json +3 -3
- package/src/app.ts +34 -34
- package/src/core.ts +320 -317
- package/src/pack-config.ts +92 -92
- package/src/render-context.ts +465 -465
- package/src/utils/cache.ts +15 -15
- package/src/utils/import-map.test.ts +713 -1
- package/src/utils/import-map.ts +53 -1
- package/src/utils/middleware.ts +19 -19
- package/src/utils/static-import-lexer.ts +18 -18
package/src/render-context.ts
CHANGED
|
@@ -3,107 +3,107 @@ import serialize from 'serialize-javascript';
|
|
|
3
3
|
import type { Esmx } from './core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* RenderContext
|
|
6
|
+
* Configuration options interface for RenderContext
|
|
7
7
|
*
|
|
8
8
|
* @description
|
|
9
|
-
* RenderContextOptions
|
|
9
|
+
* RenderContextOptions is used to configure the behavior of RenderContext instances, including base path, entry name, parameters, and import map mode.
|
|
10
10
|
*
|
|
11
11
|
* @example
|
|
12
12
|
* ```ts
|
|
13
|
-
* // 1.
|
|
14
|
-
* //
|
|
13
|
+
* // 1. Base path configuration example
|
|
14
|
+
* // Supports deploying static assets to different paths
|
|
15
15
|
* const rc = await esmx.render({
|
|
16
|
-
* //
|
|
16
|
+
* // Set base path to /esmx, all static assets will be loaded based on this path
|
|
17
17
|
* base: '/esmx',
|
|
18
|
-
* //
|
|
18
|
+
* // Other configurations...
|
|
19
19
|
* });
|
|
20
20
|
*
|
|
21
|
-
* // 2.
|
|
22
|
-
* //
|
|
21
|
+
* // 2. Multi-language site deployment example
|
|
22
|
+
* // Support multi-language sites through different base paths
|
|
23
23
|
* const rc = await esmx.render({
|
|
24
|
-
* base: '/cn', //
|
|
24
|
+
* base: '/cn', // Chinese site
|
|
25
25
|
* params: { lang: 'zh-CN' }
|
|
26
26
|
* });
|
|
27
27
|
*
|
|
28
|
-
* // 3.
|
|
28
|
+
* // 3. Import map mode configuration example
|
|
29
29
|
* const rc = await esmx.render({
|
|
30
|
-
* //
|
|
30
|
+
* // Use inline mode, suitable for small applications
|
|
31
31
|
* importmapMode: 'inline',
|
|
32
|
-
* //
|
|
32
|
+
* // Other configurations...
|
|
33
33
|
* });
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
36
|
export interface RenderContextOptions {
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
38
|
+
* Base path for static assets
|
|
39
39
|
* @description
|
|
40
|
-
* -
|
|
41
|
-
* -
|
|
42
|
-
* -
|
|
43
|
-
* -
|
|
40
|
+
* - Defaults to empty string
|
|
41
|
+
* - All static assets (JS, CSS, images, etc.) will be loaded based on this path
|
|
42
|
+
* - Supports runtime dynamic configuration without rebuilding
|
|
43
|
+
* - Commonly used for multi-language sites, micro-frontends, and other scenarios
|
|
44
44
|
*/
|
|
45
45
|
base?: string;
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
48
|
+
* Server-side rendering entry name
|
|
49
49
|
* @description
|
|
50
|
-
* -
|
|
51
|
-
* -
|
|
52
|
-
* -
|
|
50
|
+
* - Defaults to 'default'
|
|
51
|
+
* - Used to specify the entry function used during server-side rendering
|
|
52
|
+
* - Used when a module exports multiple rendering functions
|
|
53
53
|
*/
|
|
54
54
|
entryName?: string;
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
*
|
|
57
|
+
* Rendering parameters
|
|
58
58
|
* @description
|
|
59
|
-
* -
|
|
60
|
-
* -
|
|
61
|
-
* -
|
|
59
|
+
* - Can pass parameters of any type to the rendering function
|
|
60
|
+
* - Commonly used to pass request information (URL, query parameters, etc.)
|
|
61
|
+
* - Can be accessed through rc.params during server-side rendering
|
|
62
62
|
*/
|
|
63
63
|
params?: Record<string, any>;
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
66
|
+
* Define the generation mode for importmap
|
|
67
67
|
*
|
|
68
68
|
* @description
|
|
69
|
-
* ImportmapMode
|
|
70
|
-
* - `inline`:
|
|
71
|
-
* -
|
|
72
|
-
* -
|
|
73
|
-
* -
|
|
74
|
-
* - `js`:
|
|
75
|
-
* -
|
|
76
|
-
* -
|
|
77
|
-
* -
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* 1.
|
|
81
|
-
* -
|
|
82
|
-
* -
|
|
83
|
-
* -
|
|
84
|
-
* 2.
|
|
85
|
-
* -
|
|
86
|
-
* -
|
|
87
|
-
* -
|
|
88
|
-
* 3.
|
|
89
|
-
* -
|
|
90
|
-
* -
|
|
91
|
-
* -
|
|
69
|
+
* ImportmapMode is used to control the generation method of importmap, supporting two modes:
|
|
70
|
+
* - `inline`: Inline importmap content directly into HTML (default value), suitable for the following scenarios:
|
|
71
|
+
* - Need to reduce the number of HTTP requests
|
|
72
|
+
* - Importmap content is small
|
|
73
|
+
* - High requirements for first-screen loading performance
|
|
74
|
+
* - `js`: Generate importmap content as an independent JS file, suitable for the following scenarios:
|
|
75
|
+
* - Importmap content is large
|
|
76
|
+
* - Need to utilize browser caching mechanisms
|
|
77
|
+
* - Multiple pages share the same importmap
|
|
78
|
+
*
|
|
79
|
+
* Reasons for choosing 'inline' as the default value:
|
|
80
|
+
* 1. Simple and direct
|
|
81
|
+
* - Reduce additional HTTP requests
|
|
82
|
+
* - No additional resource management required
|
|
83
|
+
* - Suitable for most application scenarios
|
|
84
|
+
* 2. First-screen performance
|
|
85
|
+
* - Avoid additional network requests
|
|
86
|
+
* - Ensure import maps are immediately available
|
|
87
|
+
* - Reduce page loading time
|
|
88
|
+
* 3. Easy to debug
|
|
89
|
+
* - Import maps are directly visible
|
|
90
|
+
* - Facilitate problem diagnosis
|
|
91
|
+
* - Simplify development process
|
|
92
92
|
*
|
|
93
93
|
* @example
|
|
94
94
|
* ```ts
|
|
95
|
-
* //
|
|
95
|
+
* // Use inline mode (default)
|
|
96
96
|
* const rc = await esmx.render({
|
|
97
97
|
* params: { url: req.url }
|
|
98
98
|
* });
|
|
99
99
|
*
|
|
100
|
-
* //
|
|
100
|
+
* // Explicitly specify inline mode
|
|
101
101
|
* const rc = await esmx.render({
|
|
102
102
|
* importmapMode: 'inline',
|
|
103
103
|
* params: { url: req.url }
|
|
104
104
|
* });
|
|
105
105
|
*
|
|
106
|
-
* //
|
|
106
|
+
* // Use JS file mode
|
|
107
107
|
* const rc = await esmx.render({
|
|
108
108
|
* importmapMode: 'js',
|
|
109
109
|
* params: { url: req.url }
|
|
@@ -114,48 +114,48 @@ export interface RenderContextOptions {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
/**
|
|
117
|
-
*
|
|
117
|
+
* Server-side rendering function
|
|
118
118
|
*/
|
|
119
119
|
export type ServerRenderHandle = (rc: RenderContext) => Promise<void>;
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
|
-
*
|
|
122
|
+
* Render resource file list interface
|
|
123
123
|
* @description
|
|
124
|
-
* RenderFiles
|
|
124
|
+
* The RenderFiles interface defines various static assets collected during the server-side rendering process:
|
|
125
125
|
*
|
|
126
|
-
* 1.
|
|
127
|
-
* - css:
|
|
128
|
-
* - modulepreload:
|
|
129
|
-
* - js: JavaScript
|
|
130
|
-
* - resources:
|
|
126
|
+
* 1. **Resource Types**
|
|
127
|
+
* - css: List of stylesheet files
|
|
128
|
+
* - modulepreload: List of ESM modules that need to be preloaded
|
|
129
|
+
* - js: List of JavaScript files
|
|
130
|
+
* - resources: List of other resource files
|
|
131
131
|
*
|
|
132
|
-
* 2.
|
|
133
|
-
* -
|
|
134
|
-
* -
|
|
135
|
-
* -
|
|
132
|
+
* 2. **Use Cases**
|
|
133
|
+
* - Automatically collected in the commit() method
|
|
134
|
+
* - Injected through methods like preload(), css(), etc.
|
|
135
|
+
* - Supports base path configuration
|
|
136
136
|
*
|
|
137
137
|
* @example
|
|
138
138
|
* ```ts
|
|
139
|
-
* // 1.
|
|
139
|
+
* // 1. Resource collection
|
|
140
140
|
* await rc.commit();
|
|
141
141
|
*
|
|
142
|
-
* // 2.
|
|
142
|
+
* // 2. Resource injection
|
|
143
143
|
* rc.html = `
|
|
144
144
|
* <!DOCTYPE html>
|
|
145
145
|
* <html>
|
|
146
146
|
* <head>
|
|
147
|
-
* <!--
|
|
147
|
+
* <!-- Preload resources -->
|
|
148
148
|
* ${rc.preload()}
|
|
149
|
-
* <!--
|
|
149
|
+
* <!-- Inject stylesheets -->
|
|
150
150
|
* ${rc.css()}
|
|
151
151
|
* </head>
|
|
152
152
|
* <body>
|
|
153
153
|
* ${html}
|
|
154
|
-
* <!--
|
|
154
|
+
* <!-- Inject import map -->
|
|
155
155
|
* ${rc.importmap()}
|
|
156
|
-
* <!--
|
|
156
|
+
* <!-- Inject client entry -->
|
|
157
157
|
* ${rc.moduleEntry()}
|
|
158
|
-
* <!--
|
|
158
|
+
* <!-- Preload modules -->
|
|
159
159
|
* ${rc.modulePreload()}
|
|
160
160
|
* </body>
|
|
161
161
|
* </html>
|
|
@@ -164,65 +164,65 @@ export type ServerRenderHandle = (rc: RenderContext) => Promise<void>;
|
|
|
164
164
|
*/
|
|
165
165
|
export interface RenderFiles {
|
|
166
166
|
/**
|
|
167
|
-
* JavaScript
|
|
167
|
+
* List of JavaScript files
|
|
168
168
|
*/
|
|
169
169
|
js: string[];
|
|
170
170
|
/**
|
|
171
|
-
* CSS
|
|
171
|
+
* List of CSS files
|
|
172
172
|
*/
|
|
173
173
|
css: string[];
|
|
174
174
|
/**
|
|
175
|
-
*
|
|
175
|
+
* List of ESM modules that need to be preloaded
|
|
176
176
|
*/
|
|
177
177
|
modulepreload: string[];
|
|
178
178
|
/**
|
|
179
|
-
*
|
|
179
|
+
* List of other resource files (images, fonts, etc.)
|
|
180
180
|
*/
|
|
181
181
|
resources: string[];
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
/**
|
|
185
|
-
*
|
|
185
|
+
* Define the generation mode for importmap
|
|
186
186
|
*
|
|
187
187
|
* @description
|
|
188
|
-
* ImportmapMode
|
|
189
|
-
* - `inline`:
|
|
190
|
-
* -
|
|
191
|
-
* -
|
|
192
|
-
* -
|
|
193
|
-
* - `js`:
|
|
194
|
-
* -
|
|
195
|
-
* -
|
|
196
|
-
* -
|
|
188
|
+
* ImportmapMode is used to control the generation method of importmap, supporting two modes:
|
|
189
|
+
* - `inline`: Inline importmap content directly into HTML (default value), suitable for the following scenarios:
|
|
190
|
+
* - Need to reduce the number of HTTP requests
|
|
191
|
+
* - Importmap content is small
|
|
192
|
+
* - High requirements for first-screen loading performance
|
|
193
|
+
* - `js`: Generate importmap content as an independent JS file, suitable for the following scenarios:
|
|
194
|
+
* - Importmap content is large
|
|
195
|
+
* - Need to utilize browser caching mechanisms
|
|
196
|
+
* - Multiple pages share the same importmap
|
|
197
197
|
*
|
|
198
|
-
*
|
|
199
|
-
* 1.
|
|
200
|
-
* -
|
|
201
|
-
* -
|
|
202
|
-
* -
|
|
203
|
-
* 2.
|
|
204
|
-
* -
|
|
205
|
-
* -
|
|
206
|
-
* -
|
|
207
|
-
* 3.
|
|
208
|
-
* -
|
|
209
|
-
* -
|
|
210
|
-
* -
|
|
198
|
+
* Reasons for choosing 'inline' as the default value:
|
|
199
|
+
* 1. Simple and direct
|
|
200
|
+
* - Reduce additional HTTP requests
|
|
201
|
+
* - No additional resource management required
|
|
202
|
+
* - Suitable for most application scenarios
|
|
203
|
+
* 2. First-screen performance
|
|
204
|
+
* - Avoid additional network requests
|
|
205
|
+
* - Ensure import maps are immediately available
|
|
206
|
+
* - Reduce page loading time
|
|
207
|
+
* 3. Easy to debug
|
|
208
|
+
* - Import maps are directly visible
|
|
209
|
+
* - Facilitate problem diagnosis
|
|
210
|
+
* - Simplify development process
|
|
211
211
|
*
|
|
212
212
|
* @example
|
|
213
213
|
* ```ts
|
|
214
|
-
* //
|
|
214
|
+
* // Use inline mode (default)
|
|
215
215
|
* const rc = await esmx.render({
|
|
216
216
|
* params: { url: req.url }
|
|
217
217
|
* });
|
|
218
218
|
*
|
|
219
|
-
* //
|
|
219
|
+
* // Explicitly specify inline mode
|
|
220
220
|
* const rc = await esmx.render({
|
|
221
221
|
* importmapMode: 'inline',
|
|
222
222
|
* params: { url: req.url }
|
|
223
223
|
* });
|
|
224
224
|
*
|
|
225
|
-
* //
|
|
225
|
+
* // Use JS file mode
|
|
226
226
|
* const rc = await esmx.render({
|
|
227
227
|
* importmapMode: 'js',
|
|
228
228
|
* params: { url: req.url }
|
|
@@ -232,59 +232,59 @@ export interface RenderFiles {
|
|
|
232
232
|
export type ImportmapMode = 'inline' | 'js';
|
|
233
233
|
|
|
234
234
|
/**
|
|
235
|
-
* RenderContext
|
|
235
|
+
* RenderContext is the core class in the Esmx framework, responsible for resource management and HTML generation during server-side rendering (SSR)
|
|
236
236
|
*
|
|
237
237
|
* @description
|
|
238
|
-
* RenderContext
|
|
239
|
-
* 1.
|
|
240
|
-
* -
|
|
241
|
-
* -
|
|
242
|
-
* -
|
|
238
|
+
* RenderContext has the following core features:
|
|
239
|
+
* 1. **ESM-based module system**
|
|
240
|
+
* - Adopts modern ECMAScript Modules standard
|
|
241
|
+
* - Supports native module imports and exports
|
|
242
|
+
* - Implements better code splitting and on-demand loading
|
|
243
243
|
*
|
|
244
|
-
* 2.
|
|
245
|
-
* -
|
|
246
|
-
* -
|
|
247
|
-
* -
|
|
244
|
+
* 2. **Intelligent dependency collection**
|
|
245
|
+
* - Dynamically collects dependencies based on actual rendering paths
|
|
246
|
+
* - Avoids unnecessary resource loading
|
|
247
|
+
* - Supports async components and dynamic imports
|
|
248
248
|
*
|
|
249
|
-
* 3.
|
|
250
|
-
* -
|
|
251
|
-
* -
|
|
252
|
-
* -
|
|
249
|
+
* 3. **Precise resource injection**
|
|
250
|
+
* - Strictly controls resource loading order
|
|
251
|
+
* - Optimizes first-screen loading performance
|
|
252
|
+
* - Ensures reliability of client-side hydration
|
|
253
253
|
*
|
|
254
|
-
* 4.
|
|
255
|
-
* -
|
|
256
|
-
* -
|
|
257
|
-
* -
|
|
254
|
+
* 4. **Flexible configuration mechanism**
|
|
255
|
+
* - Supports dynamic base path configuration
|
|
256
|
+
* - Provides multiple import map modes
|
|
257
|
+
* - Adapts to different deployment scenarios
|
|
258
258
|
*
|
|
259
259
|
* @example
|
|
260
260
|
* ```ts
|
|
261
261
|
* export default async (rc: RenderContext) => {
|
|
262
|
-
* // 1.
|
|
262
|
+
* // 1. Render page content and collect dependencies
|
|
263
263
|
* const app = createApp();
|
|
264
264
|
* const html = await renderToString(app, {
|
|
265
265
|
* importMetaSet: rc.importMetaSet
|
|
266
266
|
* });
|
|
267
267
|
*
|
|
268
|
-
* // 2.
|
|
268
|
+
* // 2. Commit dependency collection
|
|
269
269
|
* await rc.commit();
|
|
270
270
|
*
|
|
271
|
-
* // 3.
|
|
271
|
+
* // 3. Generate complete HTML
|
|
272
272
|
* rc.html = `
|
|
273
273
|
* <!DOCTYPE html>
|
|
274
274
|
* <html>
|
|
275
275
|
* <head>
|
|
276
|
-
* <!--
|
|
276
|
+
* <!-- Preload CSS and JS resources to start loading early for performance optimization -->
|
|
277
277
|
* ${rc.preload()}
|
|
278
|
-
* <!--
|
|
278
|
+
* <!-- Inject first-screen stylesheets to avoid page flickering -->
|
|
279
279
|
* ${rc.css()}
|
|
280
280
|
* </head>
|
|
281
281
|
* <body>
|
|
282
282
|
* ${html}
|
|
283
|
-
* <!--
|
|
283
|
+
* <!-- Inject module import map to define path resolution rules for ESM modules -->
|
|
284
284
|
* ${rc.importmap()}
|
|
285
|
-
* <!--
|
|
285
|
+
* <!-- Inject client entry module, must be executed after importmap -->
|
|
286
286
|
* ${rc.moduleEntry()}
|
|
287
|
-
* <!--
|
|
287
|
+
* <!-- Preload module dependencies, optimized loading based on dependencies collected during actual rendering -->
|
|
288
288
|
* ${rc.modulePreload()}
|
|
289
289
|
* </body>
|
|
290
290
|
* </html>
|
|
@@ -295,35 +295,35 @@ export type ImportmapMode = 'inline' | 'js';
|
|
|
295
295
|
export class RenderContext {
|
|
296
296
|
public esmx: Esmx;
|
|
297
297
|
/**
|
|
298
|
-
*
|
|
298
|
+
* Redirect address
|
|
299
299
|
* @description
|
|
300
|
-
* -
|
|
301
|
-
* -
|
|
302
|
-
* -
|
|
300
|
+
* - Defaults to null, indicating no redirect
|
|
301
|
+
* - When set, the server can perform HTTP redirection based on this value
|
|
302
|
+
* - Commonly used for scenarios like login verification, permission control, etc.
|
|
303
303
|
*
|
|
304
304
|
* @example
|
|
305
305
|
* ```ts
|
|
306
|
-
* // 1.
|
|
306
|
+
* // 1. Login verification example
|
|
307
307
|
* export default async (rc: RenderContext) => {
|
|
308
308
|
* if (!isLoggedIn()) {
|
|
309
309
|
* rc.redirect = '/login';
|
|
310
310
|
* rc.status = 302;
|
|
311
311
|
* return;
|
|
312
312
|
* }
|
|
313
|
-
* //
|
|
313
|
+
* // Continue rendering page...
|
|
314
314
|
* };
|
|
315
315
|
*
|
|
316
|
-
* // 2.
|
|
316
|
+
* // 2. Permission control example
|
|
317
317
|
* export default async (rc: RenderContext) => {
|
|
318
318
|
* if (!hasPermission()) {
|
|
319
319
|
* rc.redirect = '/403';
|
|
320
320
|
* rc.status = 403;
|
|
321
321
|
* return;
|
|
322
322
|
* }
|
|
323
|
-
* //
|
|
323
|
+
* // Continue rendering page...
|
|
324
324
|
* };
|
|
325
325
|
*
|
|
326
|
-
* // 3.
|
|
326
|
+
* // 3. Server-side processing example
|
|
327
327
|
* app.use(async (req, res) => {
|
|
328
328
|
* const rc = await esmx.render({
|
|
329
329
|
* params: {
|
|
@@ -331,7 +331,7 @@ export class RenderContext {
|
|
|
331
331
|
* }
|
|
332
332
|
* });
|
|
333
333
|
*
|
|
334
|
-
* //
|
|
334
|
+
* // Handle redirect
|
|
335
335
|
* if (rc.redirect) {
|
|
336
336
|
* res.statusCode = rc.status || 302;
|
|
337
337
|
* res.setHeader('Location', rc.redirect);
|
|
@@ -339,12 +339,12 @@ export class RenderContext {
|
|
|
339
339
|
* return;
|
|
340
340
|
* }
|
|
341
341
|
*
|
|
342
|
-
* //
|
|
342
|
+
* // Set status code
|
|
343
343
|
* if (rc.status) {
|
|
344
344
|
* res.statusCode = rc.status;
|
|
345
345
|
* }
|
|
346
346
|
*
|
|
347
|
-
* //
|
|
347
|
+
* // Respond with HTML content
|
|
348
348
|
* res.end(rc.html);
|
|
349
349
|
* });
|
|
350
350
|
* ```
|
|
@@ -352,37 +352,37 @@ export class RenderContext {
|
|
|
352
352
|
public redirect: string | null = null;
|
|
353
353
|
|
|
354
354
|
/**
|
|
355
|
-
* HTTP
|
|
355
|
+
* HTTP response status code
|
|
356
356
|
* @description
|
|
357
|
-
* -
|
|
358
|
-
* -
|
|
359
|
-
* -
|
|
360
|
-
* -
|
|
357
|
+
* - Defaults to null, indicating use of 200 status code
|
|
358
|
+
* - Can set any valid HTTP status code
|
|
359
|
+
* - Commonly used for scenarios like error handling, redirection, etc.
|
|
360
|
+
* - Usually used in conjunction with the redirect property
|
|
361
361
|
*
|
|
362
362
|
* @example
|
|
363
363
|
* ```ts
|
|
364
|
-
* // 1. 404
|
|
364
|
+
* // 1. 404 error handling example
|
|
365
365
|
* export default async (rc: RenderContext) => {
|
|
366
366
|
* const page = await findPage(rc.params.url);
|
|
367
367
|
* if (!page) {
|
|
368
368
|
* rc.status = 404;
|
|
369
|
-
* //
|
|
369
|
+
* // Render 404 page...
|
|
370
370
|
* return;
|
|
371
371
|
* }
|
|
372
|
-
* //
|
|
372
|
+
* // Continue rendering page...
|
|
373
373
|
* };
|
|
374
374
|
*
|
|
375
|
-
* // 2.
|
|
375
|
+
* // 2. Temporary redirect example
|
|
376
376
|
* export default async (rc: RenderContext) => {
|
|
377
377
|
* if (needMaintenance()) {
|
|
378
378
|
* rc.redirect = '/maintenance';
|
|
379
|
-
* rc.status = 307; //
|
|
379
|
+
* rc.status = 307; // Temporary redirect, keep request method unchanged
|
|
380
380
|
* return;
|
|
381
381
|
* }
|
|
382
|
-
* //
|
|
382
|
+
* // Continue rendering page...
|
|
383
383
|
* };
|
|
384
384
|
*
|
|
385
|
-
* // 3.
|
|
385
|
+
* // 3. Server-side processing example
|
|
386
386
|
* app.use(async (req, res) => {
|
|
387
387
|
* const rc = await esmx.render({
|
|
388
388
|
* params: {
|
|
@@ -390,7 +390,7 @@ export class RenderContext {
|
|
|
390
390
|
* }
|
|
391
391
|
* });
|
|
392
392
|
*
|
|
393
|
-
* //
|
|
393
|
+
* // Handle redirect
|
|
394
394
|
* if (rc.redirect) {
|
|
395
395
|
* res.statusCode = rc.status || 302;
|
|
396
396
|
* res.setHeader('Location', rc.redirect);
|
|
@@ -398,12 +398,12 @@ export class RenderContext {
|
|
|
398
398
|
* return;
|
|
399
399
|
* }
|
|
400
400
|
*
|
|
401
|
-
* //
|
|
401
|
+
* // Set status code
|
|
402
402
|
* if (rc.status) {
|
|
403
403
|
* res.statusCode = rc.status;
|
|
404
404
|
* }
|
|
405
405
|
*
|
|
406
|
-
* //
|
|
406
|
+
* // Respond with HTML content
|
|
407
407
|
* res.end(rc.html);
|
|
408
408
|
* });
|
|
409
409
|
* ```
|
|
@@ -411,80 +411,80 @@ export class RenderContext {
|
|
|
411
411
|
public status: number | null = null;
|
|
412
412
|
private _html = '';
|
|
413
413
|
/**
|
|
414
|
-
*
|
|
414
|
+
* Base path for static assets
|
|
415
415
|
* @description
|
|
416
|
-
* base
|
|
416
|
+
* The base property is used to control the loading path of static assets and is the core of Esmx framework's dynamic base path configuration:
|
|
417
417
|
*
|
|
418
|
-
* 1.
|
|
419
|
-
* -
|
|
420
|
-
* -
|
|
421
|
-
* -
|
|
418
|
+
* 1. **Build-time Processing**
|
|
419
|
+
* - Static asset paths are marked with special placeholders: `[[[___ESMX_DYNAMIC_BASE___]]]/your-app-name/`
|
|
420
|
+
* - Placeholders are injected into all static asset reference paths
|
|
421
|
+
* - Supports various static assets like CSS, JavaScript, images, etc.
|
|
422
422
|
*
|
|
423
|
-
* 2.
|
|
424
|
-
* -
|
|
425
|
-
* - RenderContext
|
|
423
|
+
* 2. **Runtime Replacement**
|
|
424
|
+
* - Set the actual base path through the `base` parameter of `esmx.render()`
|
|
425
|
+
* - RenderContext automatically replaces placeholders in HTML with actual paths
|
|
426
426
|
*
|
|
427
|
-
* 3.
|
|
428
|
-
* -
|
|
429
|
-
* -
|
|
430
|
-
* -
|
|
427
|
+
* 3. **Technical Advantages**
|
|
428
|
+
* - Deployment flexibility: The same set of build artifacts can be deployed to any path
|
|
429
|
+
* - Performance optimization: Maintain the best caching strategy for static assets
|
|
430
|
+
* - Development-friendly: Simplify multi-environment configuration management
|
|
431
431
|
*
|
|
432
432
|
* @example
|
|
433
433
|
* ```ts
|
|
434
|
-
* // 1.
|
|
434
|
+
* // 1. Basic usage
|
|
435
435
|
* const rc = await esmx.render({
|
|
436
|
-
* base: '/esmx', //
|
|
436
|
+
* base: '/esmx', // Set base path
|
|
437
437
|
* params: { url: req.url }
|
|
438
438
|
* });
|
|
439
439
|
*
|
|
440
|
-
* // 2.
|
|
440
|
+
* // 2. Multi-language site example
|
|
441
441
|
* const rc = await esmx.render({
|
|
442
|
-
* base: '/cn', //
|
|
442
|
+
* base: '/cn', // Chinese site
|
|
443
443
|
* params: { lang: 'zh-CN' }
|
|
444
444
|
* });
|
|
445
445
|
*
|
|
446
|
-
* // 3.
|
|
446
|
+
* // 3. Micro-frontend application example
|
|
447
447
|
* const rc = await esmx.render({
|
|
448
|
-
* base: '/app1', //
|
|
448
|
+
* base: '/app1', // Sub-application 1
|
|
449
449
|
* params: { appId: 1 }
|
|
450
450
|
* });
|
|
451
451
|
* ```
|
|
452
452
|
*/
|
|
453
453
|
public readonly base: string;
|
|
454
454
|
/**
|
|
455
|
-
*
|
|
455
|
+
* Server-side rendering entry function name
|
|
456
456
|
* @description
|
|
457
|
-
* entryName
|
|
457
|
+
* The entryName property is used to specify the entry function used during server-side rendering:
|
|
458
458
|
*
|
|
459
|
-
* 1.
|
|
460
|
-
* -
|
|
461
|
-
* -
|
|
462
|
-
* -
|
|
459
|
+
* 1. **Basic Usage**
|
|
460
|
+
* - Default value is 'default'
|
|
461
|
+
* - Used to select the rendering function to use from entry.server.ts
|
|
462
|
+
* - Supports scenarios where a module exports multiple rendering functions
|
|
463
463
|
*
|
|
464
|
-
* 2.
|
|
465
|
-
* -
|
|
466
|
-
* - A/B
|
|
467
|
-
* -
|
|
464
|
+
* 2. **Use Cases**
|
|
465
|
+
* - Multi-template rendering: Different pages use different rendering templates
|
|
466
|
+
* - A/B testing: The same page uses different rendering logic
|
|
467
|
+
* - Special rendering: Some pages need custom rendering processes
|
|
468
468
|
*
|
|
469
469
|
* @example
|
|
470
470
|
* ```ts
|
|
471
|
-
* // 1.
|
|
471
|
+
* // 1. Default entry function
|
|
472
472
|
* // entry.server.ts
|
|
473
473
|
* export default async (rc: RenderContext) => {
|
|
474
|
-
* //
|
|
474
|
+
* // Default rendering logic
|
|
475
475
|
* };
|
|
476
476
|
*
|
|
477
|
-
* // 2.
|
|
477
|
+
* // 2. Multiple entry functions
|
|
478
478
|
* // entry.server.ts
|
|
479
479
|
* export const mobile = async (rc: RenderContext) => {
|
|
480
|
-
* //
|
|
480
|
+
* // Mobile rendering logic
|
|
481
481
|
* };
|
|
482
482
|
*
|
|
483
483
|
* export const desktop = async (rc: RenderContext) => {
|
|
484
|
-
* //
|
|
484
|
+
* // Desktop rendering logic
|
|
485
485
|
* };
|
|
486
486
|
*
|
|
487
|
-
* // 3.
|
|
487
|
+
* // 3. Select entry function based on device type
|
|
488
488
|
* const rc = await esmx.render({
|
|
489
489
|
* entryName: isMobile ? 'mobile' : 'desktop',
|
|
490
490
|
* params: { url: req.url }
|
|
@@ -494,29 +494,29 @@ export class RenderContext {
|
|
|
494
494
|
public readonly entryName: string;
|
|
495
495
|
|
|
496
496
|
/**
|
|
497
|
-
*
|
|
497
|
+
* Rendering parameters
|
|
498
498
|
* @description
|
|
499
|
-
* params
|
|
499
|
+
* The params property is used to pass and access parameters during the server-side rendering process:
|
|
500
500
|
*
|
|
501
|
-
* 1.
|
|
502
|
-
* -
|
|
503
|
-
* -
|
|
504
|
-
* -
|
|
501
|
+
* 1. **Parameter Types**
|
|
502
|
+
* - Supports key-value pairs of any type
|
|
503
|
+
* - Defined through Record<string, any> type
|
|
504
|
+
* - Remains unchanged throughout the entire rendering lifecycle
|
|
505
505
|
*
|
|
506
|
-
* 2.
|
|
507
|
-
* -
|
|
508
|
-
* -
|
|
509
|
-
* -
|
|
510
|
-
* -
|
|
506
|
+
* 2. **Common Use Cases**
|
|
507
|
+
* - Pass request information (URL, query parameters, etc.)
|
|
508
|
+
* - Set page configuration (language, theme, etc.)
|
|
509
|
+
* - Inject environment variables (API address, version number, etc.)
|
|
510
|
+
* - Share server-side state (user information, permissions, etc.)
|
|
511
511
|
*
|
|
512
|
-
* 3.
|
|
513
|
-
* -
|
|
514
|
-
* -
|
|
515
|
-
* -
|
|
512
|
+
* 3. **Access Methods**
|
|
513
|
+
* - Accessed through rc.params in server-side rendering functions
|
|
514
|
+
* - Can destructure to get specific parameters
|
|
515
|
+
* - Supports setting default values
|
|
516
516
|
*
|
|
517
517
|
* @example
|
|
518
518
|
* ```ts
|
|
519
|
-
* // 1.
|
|
519
|
+
* // 1. Basic usage - Pass URL and language settings
|
|
520
520
|
* const rc = await esmx.render({
|
|
521
521
|
* params: {
|
|
522
522
|
* url: req.url,
|
|
@@ -524,7 +524,7 @@ export class RenderContext {
|
|
|
524
524
|
* }
|
|
525
525
|
* });
|
|
526
526
|
*
|
|
527
|
-
* // 2.
|
|
527
|
+
* // 2. Page configuration - Set theme and layout
|
|
528
528
|
* const rc = await esmx.render({
|
|
529
529
|
* params: {
|
|
530
530
|
* theme: 'dark',
|
|
@@ -532,7 +532,7 @@ export class RenderContext {
|
|
|
532
532
|
* }
|
|
533
533
|
* });
|
|
534
534
|
*
|
|
535
|
-
* // 3.
|
|
535
|
+
* // 3. Environment configuration - Inject API address
|
|
536
536
|
* const rc = await esmx.render({
|
|
537
537
|
* params: {
|
|
538
538
|
* apiBaseUrl: process.env.API_BASE_URL,
|
|
@@ -540,17 +540,17 @@ export class RenderContext {
|
|
|
540
540
|
* }
|
|
541
541
|
* });
|
|
542
542
|
*
|
|
543
|
-
* // 4.
|
|
543
|
+
* // 4. Use in rendering function
|
|
544
544
|
* export default async (rc: RenderContext) => {
|
|
545
|
-
* //
|
|
545
|
+
* // Destructure to get parameters
|
|
546
546
|
* const { url, lang = 'en' } = rc.params;
|
|
547
547
|
*
|
|
548
|
-
* //
|
|
548
|
+
* // Execute different logic based on parameters
|
|
549
549
|
* if (lang === 'zh-CN') {
|
|
550
|
-
* //
|
|
550
|
+
* // Chinese version processing...
|
|
551
551
|
* }
|
|
552
552
|
*
|
|
553
|
-
* //
|
|
553
|
+
* // Pass parameters to component
|
|
554
554
|
* const html = await renderToString(createApp({
|
|
555
555
|
* props: {
|
|
556
556
|
* currentUrl: url,
|
|
@@ -558,7 +558,7 @@ export class RenderContext {
|
|
|
558
558
|
* }
|
|
559
559
|
* }));
|
|
560
560
|
*
|
|
561
|
-
* //
|
|
561
|
+
* // Set HTML
|
|
562
562
|
* rc.html = `
|
|
563
563
|
* <!DOCTYPE html>
|
|
564
564
|
* <html lang="${lang}">
|
|
@@ -570,50 +570,50 @@ export class RenderContext {
|
|
|
570
570
|
*/
|
|
571
571
|
public readonly params: Record<string, any>;
|
|
572
572
|
/**
|
|
573
|
-
*
|
|
573
|
+
* Module dependency collection set
|
|
574
574
|
* @description
|
|
575
|
-
* importMetaSet
|
|
575
|
+
* importMetaSet is the core of Esmx framework's intelligent dependency collection mechanism, used to track and record module dependencies during the server-side rendering process:
|
|
576
576
|
*
|
|
577
|
-
* 1.
|
|
578
|
-
* -
|
|
579
|
-
* -
|
|
580
|
-
* -
|
|
577
|
+
* 1. **On-demand Collection**
|
|
578
|
+
* - Automatically tracks and records module dependencies during the actual component rendering process
|
|
579
|
+
* - Only collects resources actually used during the current page rendering
|
|
580
|
+
* - Precisely records the module dependency relationships of each component
|
|
581
581
|
*
|
|
582
|
-
* 2.
|
|
583
|
-
* -
|
|
584
|
-
* -
|
|
585
|
-
* -
|
|
582
|
+
* 2. **Performance Optimization**
|
|
583
|
+
* - Avoids loading unused modules, significantly reducing first-screen loading time
|
|
584
|
+
* - Precisely controls resource loading order, optimizing page rendering performance
|
|
585
|
+
* - Automatically generates optimal import maps
|
|
586
586
|
*
|
|
587
|
-
* 3.
|
|
588
|
-
* -
|
|
589
|
-
* -
|
|
590
|
-
* -
|
|
587
|
+
* 3. **Usage**
|
|
588
|
+
* - Passed to renderToString in the rendering function
|
|
589
|
+
* - Framework automatically collects dependencies, no manual handling required
|
|
590
|
+
* - Supports dependency collection for async components and dynamic imports
|
|
591
591
|
*
|
|
592
592
|
* @example
|
|
593
593
|
* ```ts
|
|
594
|
-
* // 1.
|
|
594
|
+
* // 1. Basic usage
|
|
595
595
|
* const renderToString = (app: any, context: { importMetaSet: Set<ImportMeta> }) => {
|
|
596
|
-
* //
|
|
597
|
-
* //
|
|
598
|
-
* //
|
|
596
|
+
* // Automatically collect module dependencies during the rendering process
|
|
597
|
+
* // Framework will automatically call context.importMetaSet.add(import.meta) during component rendering
|
|
598
|
+
* // Developers do not need to manually handle dependency collection
|
|
599
599
|
* return '<div id="app">Hello World</div>';
|
|
600
600
|
* };
|
|
601
601
|
*
|
|
602
|
-
* //
|
|
602
|
+
* // Usage example
|
|
603
603
|
* const app = createApp();
|
|
604
604
|
* const html = await renderToString(app, {
|
|
605
605
|
* importMetaSet: rc.importMetaSet
|
|
606
606
|
* });
|
|
607
607
|
*
|
|
608
|
-
* // 2.
|
|
608
|
+
* // 2. Commit dependencies
|
|
609
609
|
* await rc.commit();
|
|
610
610
|
*
|
|
611
|
-
* // 3.
|
|
611
|
+
* // 3. Generate HTML
|
|
612
612
|
* rc.html = `
|
|
613
613
|
* <!DOCTYPE html>
|
|
614
614
|
* <html>
|
|
615
615
|
* <head>
|
|
616
|
-
* <!--
|
|
616
|
+
* <!-- Automatically inject resources based on collected dependencies -->
|
|
617
617
|
* ${rc.preload()}
|
|
618
618
|
* ${rc.css()}
|
|
619
619
|
* </head>
|
|
@@ -629,35 +629,35 @@ export class RenderContext {
|
|
|
629
629
|
*/
|
|
630
630
|
public importMetaSet = new Set<ImportMeta>();
|
|
631
631
|
/**
|
|
632
|
-
*
|
|
632
|
+
* Resource file list
|
|
633
633
|
* @description
|
|
634
|
-
* files
|
|
634
|
+
* The files property stores all static resource file paths collected during the server-side rendering process:
|
|
635
635
|
*
|
|
636
|
-
* 1.
|
|
637
|
-
* - js: JavaScript
|
|
638
|
-
* - css:
|
|
639
|
-
* - modulepreload:
|
|
640
|
-
* - importmap:
|
|
641
|
-
* - resources:
|
|
636
|
+
* 1. **Resource Types**
|
|
637
|
+
* - js: List of JavaScript files, containing all scripts and modules
|
|
638
|
+
* - css: List of stylesheet files
|
|
639
|
+
* - modulepreload: List of ESM modules that need to be preloaded
|
|
640
|
+
* - importmap: List of import map files
|
|
641
|
+
* - resources: List of other resource files (images, fonts, etc.)
|
|
642
642
|
*
|
|
643
|
-
* 2.
|
|
644
|
-
* -
|
|
645
|
-
* -
|
|
646
|
-
* -
|
|
643
|
+
* 2. **Use Cases**
|
|
644
|
+
* - Automatically collect and categorize resources in the commit() method
|
|
645
|
+
* - Inject resources into HTML through methods like preload(), css(), etc.
|
|
646
|
+
* - Supports base path configuration, implementing dynamic loading of resources
|
|
647
647
|
*
|
|
648
648
|
* @example
|
|
649
649
|
* ```ts
|
|
650
|
-
* // 1.
|
|
650
|
+
* // 1. Resource collection
|
|
651
651
|
* await rc.commit();
|
|
652
652
|
*
|
|
653
|
-
* // 2.
|
|
653
|
+
* // 2. Resource injection
|
|
654
654
|
* rc.html = `
|
|
655
655
|
* <!DOCTYPE html>
|
|
656
656
|
* <html>
|
|
657
657
|
* <head>
|
|
658
|
-
* <!--
|
|
658
|
+
* <!-- Preload resources -->
|
|
659
659
|
* ${rc.preload()}
|
|
660
|
-
* <!--
|
|
660
|
+
* <!-- Inject stylesheets -->
|
|
661
661
|
* ${rc.css()}
|
|
662
662
|
* </head>
|
|
663
663
|
* <body>
|
|
@@ -681,47 +681,47 @@ export class RenderContext {
|
|
|
681
681
|
code: ''
|
|
682
682
|
};
|
|
683
683
|
/**
|
|
684
|
-
*
|
|
684
|
+
* Define the generation mode for importmap
|
|
685
685
|
*
|
|
686
686
|
* @description
|
|
687
|
-
* ImportmapMode
|
|
688
|
-
* - `inline`:
|
|
689
|
-
* -
|
|
690
|
-
* -
|
|
691
|
-
* -
|
|
692
|
-
* - `js`:
|
|
693
|
-
* -
|
|
694
|
-
* -
|
|
695
|
-
* -
|
|
696
|
-
*
|
|
697
|
-
*
|
|
698
|
-
* 1.
|
|
699
|
-
* -
|
|
700
|
-
* -
|
|
701
|
-
* -
|
|
702
|
-
* 2.
|
|
703
|
-
* -
|
|
704
|
-
* -
|
|
705
|
-
* -
|
|
706
|
-
* 3.
|
|
707
|
-
* -
|
|
708
|
-
* -
|
|
709
|
-
* -
|
|
687
|
+
* ImportmapMode is used to control the generation method of importmap, supporting two modes:
|
|
688
|
+
* - `inline`: Inline importmap content directly into HTML (default value), suitable for the following scenarios:
|
|
689
|
+
* - Need to reduce the number of HTTP requests
|
|
690
|
+
* - Importmap content is small
|
|
691
|
+
* - High requirements for first-screen loading performance
|
|
692
|
+
* - `js`: Generate importmap content as an independent JS file, suitable for the following scenarios:
|
|
693
|
+
* - Importmap content is large
|
|
694
|
+
* - Need to utilize browser caching mechanisms
|
|
695
|
+
* - Multiple pages share the same importmap
|
|
696
|
+
*
|
|
697
|
+
* Reasons for choosing 'inline' as the default value:
|
|
698
|
+
* 1. Simple and direct
|
|
699
|
+
* - Reduce additional HTTP requests
|
|
700
|
+
* - No additional resource management required
|
|
701
|
+
* - Suitable for most application scenarios
|
|
702
|
+
* 2. First-screen performance
|
|
703
|
+
* - Avoid additional network requests
|
|
704
|
+
* - Ensure import maps are immediately available
|
|
705
|
+
* - Reduce page loading time
|
|
706
|
+
* 3. Easy to debug
|
|
707
|
+
* - Import maps are directly visible
|
|
708
|
+
* - Facilitate problem diagnosis
|
|
709
|
+
* - Simplify development process
|
|
710
710
|
*
|
|
711
711
|
* @example
|
|
712
712
|
* ```ts
|
|
713
|
-
* //
|
|
713
|
+
* // Use inline mode (default)
|
|
714
714
|
* const rc = await esmx.render({
|
|
715
715
|
* params: { url: req.url }
|
|
716
716
|
* });
|
|
717
717
|
*
|
|
718
|
-
* //
|
|
718
|
+
* // Explicitly specify inline mode
|
|
719
719
|
* const rc = await esmx.render({
|
|
720
720
|
* importmapMode: 'inline',
|
|
721
721
|
* params: { url: req.url }
|
|
722
722
|
* });
|
|
723
723
|
*
|
|
724
|
-
* //
|
|
724
|
+
* // Use JS file mode
|
|
725
725
|
* const rc = await esmx.render({
|
|
726
726
|
* importmapMode: 'js',
|
|
727
727
|
* params: { url: req.url }
|
|
@@ -730,25 +730,25 @@ export class RenderContext {
|
|
|
730
730
|
*/
|
|
731
731
|
public importmapMode: ImportmapMode;
|
|
732
732
|
/**
|
|
733
|
-
* HTML
|
|
733
|
+
* HTML content
|
|
734
734
|
* @description
|
|
735
|
-
* html
|
|
735
|
+
* The html property is used to set and get the final generated HTML content:
|
|
736
736
|
*
|
|
737
|
-
* 1.
|
|
738
|
-
* -
|
|
739
|
-
* -
|
|
740
|
-
* -
|
|
737
|
+
* 1. **Base Path Replacement**
|
|
738
|
+
* - Automatically handles base path placeholders when setting HTML
|
|
739
|
+
* - Replaces `[[[___ESMX_DYNAMIC_BASE___]]]/your-app-name/` with the actual base path
|
|
740
|
+
* - Ensures all static asset reference paths are correct
|
|
741
741
|
*
|
|
742
|
-
* 2.
|
|
743
|
-
* -
|
|
744
|
-
* -
|
|
745
|
-
* -
|
|
742
|
+
* 2. **Use Cases**
|
|
743
|
+
* - Set HTML content generated by server-side rendering
|
|
744
|
+
* - Support dynamic base path configuration
|
|
745
|
+
* - Automatically handle static asset reference paths
|
|
746
746
|
*
|
|
747
747
|
* @example
|
|
748
748
|
* ```ts
|
|
749
|
-
* // 1.
|
|
749
|
+
* // 1. Basic usage
|
|
750
750
|
* export default async (rc: RenderContext) => {
|
|
751
|
-
* //
|
|
751
|
+
* // Set HTML content
|
|
752
752
|
* rc.html = `
|
|
753
753
|
* <!DOCTYPE html>
|
|
754
754
|
* <html>
|
|
@@ -766,15 +766,15 @@ export class RenderContext {
|
|
|
766
766
|
* `;
|
|
767
767
|
* };
|
|
768
768
|
*
|
|
769
|
-
* // 2.
|
|
769
|
+
* // 2. Dynamic base path
|
|
770
770
|
* const rc = await esmx.render({
|
|
771
|
-
* base: '/app', //
|
|
771
|
+
* base: '/app', // Set base path
|
|
772
772
|
* params: { url: req.url }
|
|
773
773
|
* });
|
|
774
774
|
*
|
|
775
|
-
* // HTML
|
|
776
|
-
* // [[[
|
|
777
|
-
* //
|
|
775
|
+
* // Placeholders in HTML will be automatically replaced:
|
|
776
|
+
* // [[[___ESMX_DYNAMIC_BASE___]]]/your-app-name/css/style.css
|
|
777
|
+
* // Replaced with:
|
|
778
778
|
* // /app/your-app-name/css/style.css
|
|
779
779
|
* ```
|
|
780
780
|
*/
|
|
@@ -795,23 +795,23 @@ export class RenderContext {
|
|
|
795
795
|
this.importmapMode = options.importmapMode ?? 'inline';
|
|
796
796
|
}
|
|
797
797
|
/**
|
|
798
|
-
*
|
|
798
|
+
* Serialize JavaScript object to string
|
|
799
799
|
* @description
|
|
800
|
-
* serialize
|
|
800
|
+
* The serialize method is used to serialize state data during the server-side rendering process for passing to the client:
|
|
801
801
|
*
|
|
802
|
-
* 1.
|
|
803
|
-
* -
|
|
804
|
-
* -
|
|
805
|
-
* -
|
|
802
|
+
* 1. **Main Uses**
|
|
803
|
+
* - Serialize server-side state data
|
|
804
|
+
* - Ensure data can be safely embedded in HTML
|
|
805
|
+
* - Support complex data structures (such as Date, RegExp, etc.)
|
|
806
806
|
*
|
|
807
|
-
* 2.
|
|
808
|
-
* -
|
|
809
|
-
* -
|
|
810
|
-
* -
|
|
807
|
+
* 2. **Security Handling**
|
|
808
|
+
* - Automatically escape special characters
|
|
809
|
+
* - Prevent XSS attacks
|
|
810
|
+
* - Maintain data type integrity
|
|
811
811
|
*
|
|
812
812
|
* @example
|
|
813
813
|
* ```ts
|
|
814
|
-
* // 1.
|
|
814
|
+
* // 1. Basic usage - Serialize state data
|
|
815
815
|
* export default async (rc: RenderContext) => {
|
|
816
816
|
* const state = {
|
|
817
817
|
* user: { id: 1, name: 'Alice' },
|
|
@@ -824,7 +824,7 @@ export class RenderContext {
|
|
|
824
824
|
* <html>
|
|
825
825
|
* <head>
|
|
826
826
|
* <script>
|
|
827
|
-
* //
|
|
827
|
+
* // Inject serialized state into global variable
|
|
828
828
|
* window.__INITIAL_STATE__ = ${rc.serialize(state)};
|
|
829
829
|
* </script>
|
|
830
830
|
* </head>
|
|
@@ -833,17 +833,17 @@ export class RenderContext {
|
|
|
833
833
|
* `;
|
|
834
834
|
* };
|
|
835
835
|
*
|
|
836
|
-
* // 2.
|
|
836
|
+
* // 2. Custom serialization options
|
|
837
837
|
* const state = { sensitive: 'data' };
|
|
838
838
|
* const serialized = rc.serialize(state, {
|
|
839
|
-
* isJSON: true, //
|
|
840
|
-
* unsafe: false //
|
|
839
|
+
* isJSON: true, // Use JSON compatible mode
|
|
840
|
+
* unsafe: false // Disable unsafe serialization
|
|
841
841
|
* });
|
|
842
842
|
* ```
|
|
843
843
|
*
|
|
844
|
-
* @param {any} input -
|
|
845
|
-
* @param {serialize.SerializeJSOptions} [options] -
|
|
846
|
-
* @returns {string}
|
|
844
|
+
* @param {any} input - Input data to be serialized
|
|
845
|
+
* @param {serialize.SerializeJSOptions} [options] - Serialization options
|
|
846
|
+
* @returns {string} Serialized string
|
|
847
847
|
*/
|
|
848
848
|
public serialize(
|
|
849
849
|
input: any,
|
|
@@ -852,27 +852,27 @@ export class RenderContext {
|
|
|
852
852
|
return serialize(input, options);
|
|
853
853
|
}
|
|
854
854
|
/**
|
|
855
|
-
*
|
|
855
|
+
* Serialize state data and inject it into HTML
|
|
856
856
|
* @description
|
|
857
|
-
* state
|
|
857
|
+
* The state method is used to serialize state data and inject it into HTML during server-side rendering, so that the client can restore these states when activating:
|
|
858
858
|
*
|
|
859
|
-
* 1.
|
|
860
|
-
* -
|
|
861
|
-
* -
|
|
862
|
-
* -
|
|
859
|
+
* 1. **Serialization Mechanism**
|
|
860
|
+
* - Use safe serialization methods to process data
|
|
861
|
+
* - Support complex data structures (objects, arrays, etc.)
|
|
862
|
+
* - Automatically handle special characters and XSS protection
|
|
863
863
|
*
|
|
864
|
-
* 2.
|
|
865
|
-
* -
|
|
866
|
-
* -
|
|
867
|
-
* -
|
|
864
|
+
* 2. **Use Cases**
|
|
865
|
+
* - Synchronize server-side state to client
|
|
866
|
+
* - Initialize client application state
|
|
867
|
+
* - Implement seamless server-side rendering to client activation
|
|
868
868
|
*
|
|
869
|
-
* @param varName
|
|
870
|
-
* @param data
|
|
871
|
-
* @returns
|
|
869
|
+
* @param varName Global variable name, used to access injected data on the client
|
|
870
|
+
* @param data Data object that needs to be serialized
|
|
871
|
+
* @returns Script tag string containing serialized data
|
|
872
872
|
*
|
|
873
873
|
* @example
|
|
874
874
|
* ```ts
|
|
875
|
-
* // 1.
|
|
875
|
+
* // 1. Basic usage - Inject user information
|
|
876
876
|
* export default async (rc: RenderContext) => {
|
|
877
877
|
* const userInfo = {
|
|
878
878
|
* id: 1,
|
|
@@ -893,12 +893,12 @@ export class RenderContext {
|
|
|
893
893
|
* `;
|
|
894
894
|
* };
|
|
895
895
|
*
|
|
896
|
-
* // 2.
|
|
897
|
-
* //
|
|
896
|
+
* // 2. Client-side usage
|
|
897
|
+
* // Can directly access injected data on the client
|
|
898
898
|
* const userInfo = window.__USER__;
|
|
899
|
-
* console.log(userInfo.name); //
|
|
899
|
+
* console.log(userInfo.name); // Output: 'John'
|
|
900
900
|
*
|
|
901
|
-
* // 3.
|
|
901
|
+
* // 3. Complex data structures
|
|
902
902
|
* export default async (rc: RenderContext) => {
|
|
903
903
|
* const appState = {
|
|
904
904
|
* user: {
|
|
@@ -932,42 +932,42 @@ export class RenderContext {
|
|
|
932
932
|
return `<script>window[${serialize(varName)}] = ${serialize(data, { isJSON: true })};</script>`;
|
|
933
933
|
}
|
|
934
934
|
/**
|
|
935
|
-
*
|
|
935
|
+
* Commit dependency collection and update resource list
|
|
936
936
|
* @description
|
|
937
|
-
* commit
|
|
938
|
-
*
|
|
939
|
-
* 1.
|
|
940
|
-
* -
|
|
941
|
-
* -
|
|
942
|
-
* -
|
|
943
|
-
* -
|
|
944
|
-
*
|
|
945
|
-
* 2.
|
|
946
|
-
* - js: JavaScript
|
|
947
|
-
* - css:
|
|
948
|
-
* - modulepreload:
|
|
949
|
-
* - importmap:
|
|
950
|
-
* - resources:
|
|
951
|
-
*
|
|
952
|
-
* 3.
|
|
953
|
-
* -
|
|
954
|
-
* -
|
|
955
|
-
* -
|
|
937
|
+
* The commit method is the core of RenderContext's dependency collection mechanism, responsible for handling all collected module dependencies and updating the file resource list:
|
|
938
|
+
*
|
|
939
|
+
* 1. **Dependency Processing Flow**
|
|
940
|
+
* - Collect all used modules from importMetaSet
|
|
941
|
+
* - Parse specific resources for each module based on manifest files
|
|
942
|
+
* - Handle different types of dependencies such as JS, CSS, resource files, etc.
|
|
943
|
+
* - Automatically handle module preloading and import maps
|
|
944
|
+
*
|
|
945
|
+
* 2. **Resource Classification**
|
|
946
|
+
* - js: JavaScript files, containing all scripts and modules
|
|
947
|
+
* - css: Stylesheet files
|
|
948
|
+
* - modulepreload: ESM modules that need to be preloaded
|
|
949
|
+
* - importmap: Import map files
|
|
950
|
+
* - resources: Other resource files (images, fonts, etc.)
|
|
951
|
+
*
|
|
952
|
+
* 3. **Path Processing**
|
|
953
|
+
* - Automatically add base path prefix
|
|
954
|
+
* - Ensure the correctness of resource paths
|
|
955
|
+
* - Support resource isolation for multi-application scenarios
|
|
956
956
|
*
|
|
957
957
|
* @example
|
|
958
958
|
* ```ts
|
|
959
|
-
* // 1.
|
|
959
|
+
* // 1. Basic usage
|
|
960
960
|
* export default async (rc: RenderContext) => {
|
|
961
|
-
* //
|
|
961
|
+
* // Render page and collect dependencies
|
|
962
962
|
* const app = createApp();
|
|
963
963
|
* const html = await renderToString(app, {
|
|
964
964
|
* importMetaSet: rc.importMetaSet
|
|
965
965
|
* });
|
|
966
966
|
*
|
|
967
|
-
* //
|
|
967
|
+
* // Commit dependency collection
|
|
968
968
|
* await rc.commit();
|
|
969
969
|
*
|
|
970
|
-
* //
|
|
970
|
+
* // Generate HTML
|
|
971
971
|
* rc.html = `
|
|
972
972
|
* <!DOCTYPE html>
|
|
973
973
|
* <html>
|
|
@@ -985,18 +985,18 @@ export class RenderContext {
|
|
|
985
985
|
* `;
|
|
986
986
|
* };
|
|
987
987
|
*
|
|
988
|
-
* // 2.
|
|
988
|
+
* // 2. Multi-application scenario
|
|
989
989
|
* const rc = await esmx.render({
|
|
990
|
-
* base: '/app1', //
|
|
990
|
+
* base: '/app1', // Set base path
|
|
991
991
|
* params: { appId: 1 }
|
|
992
992
|
* });
|
|
993
993
|
*
|
|
994
|
-
* //
|
|
994
|
+
* // Render and commit dependencies
|
|
995
995
|
* const html = await renderApp(rc);
|
|
996
996
|
* await rc.commit();
|
|
997
997
|
*
|
|
998
|
-
* //
|
|
999
|
-
* //
|
|
998
|
+
* // Resource paths will automatically add base path prefix
|
|
999
|
+
* // For example: /app1/your-app-name/js/main.js
|
|
1000
1000
|
* ```
|
|
1001
1001
|
*/
|
|
1002
1002
|
public async commit() {
|
|
@@ -1052,37 +1052,37 @@ export class RenderContext {
|
|
|
1052
1052
|
this._importMap = await esmx.getImportMapClientInfo(this.importmapMode);
|
|
1053
1053
|
}
|
|
1054
1054
|
/**
|
|
1055
|
-
*
|
|
1055
|
+
* Generate resource preload tags
|
|
1056
1056
|
* @description
|
|
1057
|
-
* preload()
|
|
1057
|
+
* The preload() method is used to generate resource preload tags, optimizing page performance by loading critical resources in advance:
|
|
1058
1058
|
*
|
|
1059
|
-
* 1.
|
|
1060
|
-
* - CSS
|
|
1061
|
-
* - JS
|
|
1059
|
+
* 1. **Resource Types**
|
|
1060
|
+
* - CSS files: Use `as="style"` to preload stylesheets
|
|
1061
|
+
* - JS files: Use `as="script"` to preload import map scripts
|
|
1062
1062
|
*
|
|
1063
|
-
* 2.
|
|
1064
|
-
* -
|
|
1065
|
-
* -
|
|
1066
|
-
* -
|
|
1067
|
-
* -
|
|
1063
|
+
* 2. **Performance Optimization**
|
|
1064
|
+
* - Discover and load critical resources in advance
|
|
1065
|
+
* - Load in parallel with HTML parsing
|
|
1066
|
+
* - Optimize resource loading order
|
|
1067
|
+
* - Reduce page rendering blocking
|
|
1068
1068
|
*
|
|
1069
|
-
* 3.
|
|
1070
|
-
* -
|
|
1071
|
-
* -
|
|
1072
|
-
* -
|
|
1069
|
+
* 3. **Best Practices**
|
|
1070
|
+
* - Use as early as possible in the head
|
|
1071
|
+
* - Only preload resources necessary for the current page
|
|
1072
|
+
* - Use in conjunction with other resource loading methods
|
|
1073
1073
|
*
|
|
1074
|
-
* @returns
|
|
1074
|
+
* @returns Returns HTML string containing all preload tags
|
|
1075
1075
|
*
|
|
1076
1076
|
* @example
|
|
1077
1077
|
* ```ts
|
|
1078
|
-
* //
|
|
1078
|
+
* // Use in HTML head
|
|
1079
1079
|
* rc.html = `
|
|
1080
1080
|
* <!DOCTYPE html>
|
|
1081
1081
|
* <html>
|
|
1082
1082
|
* <head>
|
|
1083
|
-
* <!--
|
|
1083
|
+
* <!-- Preload critical resources -->
|
|
1084
1084
|
* ${rc.preload()}
|
|
1085
|
-
* <!--
|
|
1085
|
+
* <!-- Inject stylesheets -->
|
|
1086
1086
|
* ${rc.css()}
|
|
1087
1087
|
* </head>
|
|
1088
1088
|
* <body>
|
|
@@ -1108,34 +1108,34 @@ export class RenderContext {
|
|
|
1108
1108
|
return list.join('');
|
|
1109
1109
|
}
|
|
1110
1110
|
/**
|
|
1111
|
-
*
|
|
1111
|
+
* Inject first-screen stylesheets
|
|
1112
1112
|
* @description
|
|
1113
|
-
* css()
|
|
1113
|
+
* The css() method is used to inject stylesheet resources required by the page:
|
|
1114
1114
|
*
|
|
1115
|
-
* 1.
|
|
1116
|
-
* -
|
|
1117
|
-
* -
|
|
1118
|
-
* -
|
|
1115
|
+
* 1. **Injection Position**
|
|
1116
|
+
* - Must be injected in the head tag
|
|
1117
|
+
* - Avoid page flickering (FOUC) and reflow
|
|
1118
|
+
* - Ensure styles are in place when content is rendered
|
|
1119
1119
|
*
|
|
1120
|
-
* 2.
|
|
1121
|
-
* -
|
|
1122
|
-
* -
|
|
1123
|
-
* -
|
|
1120
|
+
* 2. **Performance Optimization**
|
|
1121
|
+
* - Support critical CSS extraction
|
|
1122
|
+
* - Automatically handle style dependency relationships
|
|
1123
|
+
* - Utilize browser parallel loading capabilities
|
|
1124
1124
|
*
|
|
1125
|
-
* 3.
|
|
1126
|
-
* -
|
|
1127
|
-
* -
|
|
1128
|
-
* -
|
|
1125
|
+
* 3. **Use Cases**
|
|
1126
|
+
* - Inject styles necessary for the first screen
|
|
1127
|
+
* - Handle component-level styles
|
|
1128
|
+
* - Support theme switching and dynamic styles
|
|
1129
1129
|
*
|
|
1130
1130
|
* @example
|
|
1131
1131
|
* ```ts
|
|
1132
|
-
* // 1.
|
|
1132
|
+
* // 1. Basic usage
|
|
1133
1133
|
* rc.html = `
|
|
1134
1134
|
* <!DOCTYPE html>
|
|
1135
1135
|
* <html>
|
|
1136
1136
|
* <head>
|
|
1137
|
-
* ${rc.preload()} <!--
|
|
1138
|
-
* ${rc.css()} <!--
|
|
1137
|
+
* ${rc.preload()} <!-- Preload resources -->
|
|
1138
|
+
* ${rc.css()} <!-- Inject stylesheets -->
|
|
1139
1139
|
* </head>
|
|
1140
1140
|
* <body>
|
|
1141
1141
|
* <div id="app">Hello World</div>
|
|
@@ -1143,13 +1143,13 @@ export class RenderContext {
|
|
|
1143
1143
|
* </html>
|
|
1144
1144
|
* `;
|
|
1145
1145
|
*
|
|
1146
|
-
* // 2.
|
|
1146
|
+
* // 2. Use in conjunction with other resources
|
|
1147
1147
|
* rc.html = `
|
|
1148
1148
|
* <!DOCTYPE html>
|
|
1149
1149
|
* <html>
|
|
1150
1150
|
* <head>
|
|
1151
|
-
* ${rc.preload()} <!--
|
|
1152
|
-
* ${rc.css()} <!--
|
|
1151
|
+
* ${rc.preload()} <!-- Preload resources -->
|
|
1152
|
+
* ${rc.css()} <!-- Inject stylesheets -->
|
|
1153
1153
|
* </head>
|
|
1154
1154
|
* <body>
|
|
1155
1155
|
* ${html}
|
|
@@ -1167,35 +1167,35 @@ export class RenderContext {
|
|
|
1167
1167
|
.join('');
|
|
1168
1168
|
}
|
|
1169
1169
|
/**
|
|
1170
|
-
*
|
|
1170
|
+
* Inject module import map
|
|
1171
1171
|
* @description
|
|
1172
|
-
* importmap()
|
|
1173
|
-
*
|
|
1174
|
-
* 1.
|
|
1175
|
-
* -
|
|
1176
|
-
* -
|
|
1177
|
-
* -
|
|
1178
|
-
*
|
|
1179
|
-
* 2.
|
|
1180
|
-
* -
|
|
1181
|
-
* -
|
|
1182
|
-
* -
|
|
1183
|
-
* -
|
|
1184
|
-
* - JS
|
|
1185
|
-
* -
|
|
1186
|
-
* -
|
|
1187
|
-
* -
|
|
1188
|
-
*
|
|
1189
|
-
* 3.
|
|
1190
|
-
* -
|
|
1191
|
-
* -
|
|
1192
|
-
* -
|
|
1172
|
+
* The importmap() method is used to inject path resolution rules for ESM modules:
|
|
1173
|
+
*
|
|
1174
|
+
* 1. **Injection Position**
|
|
1175
|
+
* - Must be injected in the body
|
|
1176
|
+
* - Must be executed before moduleEntry
|
|
1177
|
+
* - Avoid blocking the first page render
|
|
1178
|
+
*
|
|
1179
|
+
* 2. **Import Map Modes**
|
|
1180
|
+
* - Inline mode (inline):
|
|
1181
|
+
* - Inline map content directly into HTML
|
|
1182
|
+
* - Suitable for scenarios with smaller map content
|
|
1183
|
+
* - Reduce the number of HTTP requests
|
|
1184
|
+
* - JS file mode (js):
|
|
1185
|
+
* - Generate independent JS files
|
|
1186
|
+
* - Suitable for scenarios with larger map content
|
|
1187
|
+
* - Can utilize browser caching mechanisms
|
|
1188
|
+
*
|
|
1189
|
+
* 3. **Technical Reasons**
|
|
1190
|
+
* - Define path resolution rules for ESM modules
|
|
1191
|
+
* - Client entry modules and their dependencies need to use these maps
|
|
1192
|
+
* - Ensure the map is correctly set before executing module code
|
|
1193
1193
|
*
|
|
1194
1194
|
* @example
|
|
1195
1195
|
* ```ts
|
|
1196
|
-
* // 1.
|
|
1196
|
+
* // 1. Basic usage - Inline mode
|
|
1197
1197
|
* const rc = await esmx.render({
|
|
1198
|
-
* importmapMode: 'inline' //
|
|
1198
|
+
* importmapMode: 'inline' // Default mode
|
|
1199
1199
|
* });
|
|
1200
1200
|
*
|
|
1201
1201
|
* rc.html = `
|
|
@@ -1207,16 +1207,16 @@ export class RenderContext {
|
|
|
1207
1207
|
* </head>
|
|
1208
1208
|
* <body>
|
|
1209
1209
|
* ${html}
|
|
1210
|
-
* ${rc.importmap()} <!--
|
|
1211
|
-
* ${rc.moduleEntry()} <!--
|
|
1210
|
+
* ${rc.importmap()} <!-- Inject import map -->
|
|
1211
|
+
* ${rc.moduleEntry()} <!-- Execute after import map -->
|
|
1212
1212
|
* ${rc.modulePreload()}
|
|
1213
1213
|
* </body>
|
|
1214
1214
|
* </html>
|
|
1215
1215
|
* `;
|
|
1216
1216
|
*
|
|
1217
|
-
* // 2. JS
|
|
1217
|
+
* // 2. JS file mode - Suitable for large applications
|
|
1218
1218
|
* const rc = await esmx.render({
|
|
1219
|
-
* importmapMode: 'js' //
|
|
1219
|
+
* importmapMode: 'js' // Use JS file mode
|
|
1220
1220
|
* });
|
|
1221
1221
|
* ```
|
|
1222
1222
|
*/
|
|
@@ -1224,27 +1224,27 @@ export class RenderContext {
|
|
|
1224
1224
|
return this._importMap.code;
|
|
1225
1225
|
}
|
|
1226
1226
|
/**
|
|
1227
|
-
*
|
|
1227
|
+
* Inject client entry module
|
|
1228
1228
|
* @description
|
|
1229
|
-
* moduleEntry()
|
|
1230
|
-
* 1.
|
|
1231
|
-
* -
|
|
1232
|
-
* -
|
|
1233
|
-
* -
|
|
1234
|
-
*
|
|
1235
|
-
* 2.
|
|
1236
|
-
* -
|
|
1237
|
-
* -
|
|
1238
|
-
* -
|
|
1239
|
-
*
|
|
1240
|
-
* 3.
|
|
1241
|
-
* -
|
|
1242
|
-
* -
|
|
1243
|
-
* -
|
|
1229
|
+
* The moduleEntry() method is used to inject the client's entry module:
|
|
1230
|
+
* 1. **Injection Position**
|
|
1231
|
+
* - Must be executed after importmap
|
|
1232
|
+
* - Ensure the import map is correctly set before executing module code
|
|
1233
|
+
* - Control the start timing of client activation (Hydration)
|
|
1234
|
+
*
|
|
1235
|
+
* 2. **Technical Reasons**
|
|
1236
|
+
* - Serve as the entry point for client code
|
|
1237
|
+
* - Need to wait for infrastructure (such as import maps) to be ready
|
|
1238
|
+
* - Ensure correct module path resolution
|
|
1239
|
+
*
|
|
1240
|
+
* 3. **Use Cases**
|
|
1241
|
+
* - Start the client application
|
|
1242
|
+
* - Execute client activation
|
|
1243
|
+
* - Initialize client state
|
|
1244
1244
|
*
|
|
1245
1245
|
* @example
|
|
1246
1246
|
* ```ts
|
|
1247
|
-
* // 1.
|
|
1247
|
+
* // 1. Basic usage
|
|
1248
1248
|
* rc.html = `
|
|
1249
1249
|
* <!DOCTYPE html>
|
|
1250
1250
|
* <html>
|
|
@@ -1254,16 +1254,16 @@ export class RenderContext {
|
|
|
1254
1254
|
* </head>
|
|
1255
1255
|
* <body>
|
|
1256
1256
|
* ${html}
|
|
1257
|
-
* ${rc.importmap()} <!--
|
|
1258
|
-
* ${rc.moduleEntry()} <!--
|
|
1257
|
+
* ${rc.importmap()} <!-- Inject import map first -->
|
|
1258
|
+
* ${rc.moduleEntry()} <!-- Then inject entry module -->
|
|
1259
1259
|
* ${rc.modulePreload()}
|
|
1260
1260
|
* </body>
|
|
1261
1261
|
* </html>
|
|
1262
1262
|
* `;
|
|
1263
1263
|
*
|
|
1264
|
-
* // 2.
|
|
1264
|
+
* // 2. Multiple entry configuration
|
|
1265
1265
|
* const rc = await esmx.render({
|
|
1266
|
-
* entryName: 'mobile', //
|
|
1266
|
+
* entryName: 'mobile', // Specify entry name
|
|
1267
1267
|
* params: { device: 'mobile' }
|
|
1268
1268
|
* });
|
|
1269
1269
|
* ```
|
|
@@ -1273,28 +1273,28 @@ export class RenderContext {
|
|
|
1273
1273
|
}
|
|
1274
1274
|
|
|
1275
1275
|
/**
|
|
1276
|
-
*
|
|
1276
|
+
* Preload module dependencies
|
|
1277
1277
|
* @description
|
|
1278
|
-
* modulePreload()
|
|
1278
|
+
* The modulePreload() method is used to preload modules that may be needed later:
|
|
1279
1279
|
*
|
|
1280
|
-
* 1.
|
|
1281
|
-
* -
|
|
1282
|
-
* -
|
|
1283
|
-
* -
|
|
1280
|
+
* 1. **Injection Position**
|
|
1281
|
+
* - Must be after importmap and moduleEntry
|
|
1282
|
+
* - Ensure the correct module path mapping is used
|
|
1283
|
+
* - Avoid competing with first-screen rendering for resources
|
|
1284
1284
|
*
|
|
1285
|
-
* 2.
|
|
1286
|
-
* -
|
|
1287
|
-
* -
|
|
1288
|
-
* -
|
|
1285
|
+
* 2. **Performance Optimization**
|
|
1286
|
+
* - Preload modules that may be needed later
|
|
1287
|
+
* - Improve runtime performance
|
|
1288
|
+
* - Optimize on-demand loading experience
|
|
1289
1289
|
*
|
|
1290
|
-
* 3.
|
|
1291
|
-
* -
|
|
1292
|
-
* -
|
|
1293
|
-
* -
|
|
1290
|
+
* 3. **Technical Reasons**
|
|
1291
|
+
* - Need correct path resolution rules
|
|
1292
|
+
* - Avoid duplicate loading
|
|
1293
|
+
* - Control loading priority
|
|
1294
1294
|
*
|
|
1295
1295
|
* @example
|
|
1296
1296
|
* ```ts
|
|
1297
|
-
* // 1.
|
|
1297
|
+
* // 1. Basic usage
|
|
1298
1298
|
* rc.html = `
|
|
1299
1299
|
* <!DOCTYPE html>
|
|
1300
1300
|
* <html>
|
|
@@ -1306,16 +1306,16 @@ export class RenderContext {
|
|
|
1306
1306
|
* ${html}
|
|
1307
1307
|
* ${rc.importmap()}
|
|
1308
1308
|
* ${rc.moduleEntry()}
|
|
1309
|
-
* ${rc.modulePreload()} <!--
|
|
1309
|
+
* ${rc.modulePreload()} <!-- Preload module dependencies -->
|
|
1310
1310
|
* </body>
|
|
1311
1311
|
* </html>
|
|
1312
1312
|
* `;
|
|
1313
1313
|
*
|
|
1314
|
-
* // 2.
|
|
1314
|
+
* // 2. Use with async components
|
|
1315
1315
|
* const AsyncComponent = defineAsyncComponent(() =>
|
|
1316
1316
|
* import('./components/AsyncComponent.vue')
|
|
1317
1317
|
* );
|
|
1318
|
-
* // modulePreload
|
|
1318
|
+
* // modulePreload will automatically collect and preload dependencies of async components
|
|
1319
1319
|
* ```
|
|
1320
1320
|
*/
|
|
1321
1321
|
public modulePreload() {
|