@@ -2,7 +2,13 @@ import React, { ReactElement } from 'react';
2
2
import * as ReactDomServer from 'react-dom/server' ;
3
3
import { matchRoutes } from 'react-router-dom' ;
4
4
import { Writable } from 'stream' ;
5
- import type { IRoutesById , IServerLoaderArgs , UmiRequest } from './types' ;
5
+ import type {
6
+ IRoutesById ,
7
+ IServerLoaderArgs ,
8
+ MetadataLoader ,
9
+ ServerLoader ,
10
+ UmiRequest ,
11
+ } from './types' ;
6
12
7
13
interface RouteLoaders {
8
14
[ key : string ] : ( ) => Promise < any > ;
@@ -11,11 +17,6 @@ interface RouteLoaders {
11
17
export type ServerInsertedHTMLHook = ( callbacks : ( ) => React . ReactNode ) => void ;
12
18
13
19
interface CreateRequestServerlessOptions {
14
- /**
15
- * only return body html
16
- * @example <div id="root">{app}</div> ...
17
- */
18
- withoutHTML ?: boolean ;
19
20
/**
20
21
* folder path for `build-manifest.json`
21
22
*/
@@ -37,6 +38,16 @@ interface CreateRequestHandlerOptions extends CreateRequestServerlessOptions {
37
38
ServerInsertedHTMLContext : React . Context < ServerInsertedHTMLHook | null > ;
38
39
}
39
40
41
+ interface IExecLoaderOpts {
42
+ routeKey : string ;
43
+ routesWithServerLoader : RouteLoaders ;
44
+ serverLoaderArgs ?: IServerLoaderArgs ;
45
+ }
46
+
47
+ interface IExecMetaLoaderOpts extends IExecLoaderOpts {
48
+ serverLoaderData ?: any ;
49
+ }
50
+
40
51
const createJSXProvider = (
41
52
Provider : any ,
42
53
serverInsertedHTMLCallbacks : Set < ( ) => React . ReactNode > ,
@@ -93,18 +104,33 @@ function createJSXGenerator(opts: CreateRequestHandlerOptions) {
93
104
return ;
94
105
}
95
106
96
- const loaderData : { [ key : string ] : any } = { } ;
107
+ const loaderData : Record < string , any > = { } ;
108
+ const metadata : Record < string , any > = { } ;
97
109
await Promise . all (
98
110
matches
99
111
. filter ( ( id : string ) => routes [ id ] . hasServerLoader )
100
112
. map (
101
113
( id : string ) =>
102
114
new Promise < void > ( async ( resolve ) => {
103
- loaderData [ id ] = await executeLoader (
104
- id ,
115
+ loaderData [ id ] = await executeLoader ( {
116
+ routeKey : id ,
105
117
routesWithServerLoader,
106
118
serverLoaderArgs,
107
- ) ;
119
+ } ) ;
120
+ // 如果有metadataLoader,执行metadataLoader
121
+ // metadataLoader在serverLoader返回之后执行这样metadataLoader可以使用serverLoader的返回值
122
+ // 如果有多层嵌套路由和合并多层返回的metadata但最里层的优先级最高
123
+ if ( routes [ id ] . hasMetadataLoader ) {
124
+ Object . assign (
125
+ metadata ,
126
+ await executeMetadataLoader ( {
127
+ routesWithServerLoader,
128
+ routeKey : id ,
129
+ serverLoaderArgs,
130
+ serverLoaderData : loaderData [ id ] ,
131
+ } ) ,
132
+ ) ;
133
+ }
108
134
resolve ( ) ;
109
135
} ) ,
110
136
) ,
@@ -121,7 +147,7 @@ function createJSXGenerator(opts: CreateRequestHandlerOptions) {
121
147
location : url ,
122
148
manifest,
123
149
loaderData,
124
- withoutHTML : opts . withoutHTML ,
150
+ metadata ,
125
151
} ;
126
152
127
153
const element = ( await opts . getClientRootComponent (
@@ -219,14 +245,11 @@ export default function createRequestHandler(
219
245
return async function ( req : any , res : any , next : any ) {
220
246
// 切换路由场景下,会通过此 API 执行 server loader
221
247
if ( req . url . startsWith ( '/__serverLoader' ) && req . query . route ) {
222
- const loaderArgs : IServerLoaderArgs = {
223
- request : req ,
224
- } ;
225
- const data = await executeLoader (
226
- req . query . route ,
227
- opts . routesWithServerLoader ,
228
- loaderArgs ,
229
- ) ;
248
+ const data = await executeLoader ( {
249
+ routeKey : req . query . route ,
250
+ routesWithServerLoader : opts . routesWithServerLoader ,
251
+ serverLoaderArgs : { request : req } ,
252
+ } ) ;
230
253
res . status ( 200 ) . json ( data ) ;
231
254
return ;
232
255
}
@@ -293,10 +316,11 @@ export function createUmiServerLoader(opts: CreateRequestHandlerOptions) {
293
316
return async function ( req : UmiRequest ) {
294
317
const query = Object . fromEntries ( new URL ( req . url ) . searchParams ) ;
295
318
// 切换路由场景下,会通过此 API 执行 server loader
296
- const loaderArgs : IServerLoaderArgs = {
297
- request : req ,
298
- } ;
299
- return executeLoader ( query . route , opts . routesWithServerLoader , loaderArgs ) ;
319
+ return await executeLoader ( {
320
+ routeKey : query . route ,
321
+ routesWithServerLoader : opts . routesWithServerLoader ,
322
+ serverLoaderArgs : { request : req } ,
323
+ } ) ;
300
324
} ;
301
325
}
302
326
@@ -335,15 +359,29 @@ function createClientRoute(route: any) {
335
359
} ;
336
360
}
337
361
338
- async function executeLoader (
339
- routeKey : string ,
340
- routesWithServerLoader : RouteLoaders ,
341
- serverLoaderArgs ?: IServerLoaderArgs ,
342
- ) {
362
+ async function executeLoader ( params : IExecLoaderOpts ) {
363
+ const { routeKey, routesWithServerLoader, serverLoaderArgs } = params ;
343
364
const mod = await routesWithServerLoader [ routeKey ] ( ) ;
344
365
if ( ! mod . serverLoader || typeof mod . serverLoader !== 'function' ) {
345
366
return ;
346
367
}
347
368
// TODO: 处理错误场景
348
- return mod . serverLoader ( serverLoaderArgs ) ;
369
+ return ( mod . serverLoader satisfies ServerLoader ) ( serverLoaderArgs ) ;
370
+ }
371
+
372
+ async function executeMetadataLoader ( params : IExecMetaLoaderOpts ) {
373
+ const {
374
+ routesWithServerLoader,
375
+ routeKey,
376
+ serverLoaderArgs,
377
+ serverLoaderData,
378
+ } = params ;
379
+ const mod = await routesWithServerLoader [ routeKey ] ( ) ;
380
+ if ( ! mod . serverLoader || typeof mod . serverLoader !== 'function' ) {
381
+ return ;
382
+ }
383
+ return ( mod . metadataLoader satisfies MetadataLoader ) (
384
+ serverLoaderData ,
385
+ serverLoaderArgs ,
386
+ ) ;
349
387
}
0 commit comments