/
renderRoutes.tsx
174 lines (164 loc) · 4.95 KB
/
renderRoutes.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import React, { useEffect, useState, createElement } from 'react';
import { Plugin, Redirect, ApplyPluginsType } from '@umijs/runtime';
import { IRoute, IComponent } from '..';
import Switch from './Switch';
import Route from './Route';
interface IOpts {
routes: IRoute[];
plugin: Plugin;
extraProps?: object;
pageInitialProps?: object;
getInitialPropsCtx?: object;
isServer?: boolean;
ssrProps?: object;
rootRoutes?: IRoute[];
}
interface IGetRouteElementOpts {
route: IRoute;
index: number;
opts: IOpts;
}
function wrapInitialPropsFetch(route: IRoute, opts: IOpts): IComponent {
const { component, ...restRouteParams } = route;
let Component: any = route!.component;
function ComponentWithInitialPropsFetch(props: any) {
const [initialProps, setInitialProps] = useState(
() => (window as any).g_initialProps,
);
useEffect(() => {
/**
* 1. 首次渲染时,此时 window.g_initialProps 变量存在,不需要再走一次 getInitialProps,这样一次 SSR 就走了 2 次 getInitialProps
* 2. 但是路由切换时,window.getInitialProps 会被赋为 null,这时候就走 getInitialProps 逻辑
* 3. 如果任何时候都走 2 次,配置 forceInitial: true,这个场景用于静态站点的首屏加载希望走最新数据
* 4. 开启动态加载后,会在执行 getInitialProps 前预加载下
*/
const handleGetInitialProps = async () => {
// preload when enalbe dynamicImport
if (Component.preload) {
const preloadComponent = await Component.preload();
// for test case, really use .default
Component = preloadComponent.default || preloadComponent;
}
const defaultCtx = {
isServer: false,
match: props?.match,
history: props?.history,
route,
...(opts.getInitialPropsCtx || {}),
...restRouteParams,
};
if (Component?.getInitialProps) {
const ctx = await opts.plugin.applyPlugins({
key: 'ssr.modifyGetInitialPropsCtx',
type: ApplyPluginsType.modify,
initialValue: defaultCtx,
async: true,
});
const initialProps = await Component!.getInitialProps!(
ctx || defaultCtx,
);
setInitialProps(initialProps);
}
};
// null 时,一定会触发 getInitialProps 执行
if (!(window as any).g_initialProps) {
handleGetInitialProps();
}
}, [window.location.pathname, window.location.search]);
return <Component {...props} {...initialProps} />;
}
// flag for having wrappered
ComponentWithInitialPropsFetch.wrapInitialPropsLoaded = true;
ComponentWithInitialPropsFetch.displayName = 'ComponentWithInitialPropsFetch';
return ComponentWithInitialPropsFetch;
}
function render({
route,
opts,
props,
}: {
route: IRoute;
opts: IOpts;
props: object;
}) {
const routes = renderRoutes({
...opts,
routes: route.routes || [],
rootRoutes: opts.rootRoutes,
});
let { component: Component, wrappers } = route;
if (Component) {
const defaultPageInitialProps = opts.isServer
? {}
: (window as any).g_initialProps;
const newProps = {
...props,
...opts.extraProps,
...(opts.pageInitialProps || defaultPageInitialProps),
route,
routes: opts.rootRoutes,
};
// @ts-ignore
let ret = <Component {...newProps}>{routes}</Component>;
// route.wrappers
if (wrappers) {
let len = wrappers.length - 1;
while (len >= 0) {
ret = createElement(wrappers[len], newProps, ret);
len -= 1;
}
}
return ret;
} else {
return routes;
}
}
function getRouteElement({ route, index, opts }: IGetRouteElementOpts) {
const routeProps = {
key: route.key || index,
exact: route.exact,
strict: route.strict,
sensitive: route.sensitive,
path: route.path,
};
if (route.redirect) {
return <Redirect {...routeProps} from={route.path} to={route.redirect} />;
} else {
// avoid mount and unmount with url hash change
if (
// only when SSR config enable
opts.ssrProps &&
!opts.isServer &&
// make sure loaded once
!(route.component as any)?.wrapInitialPropsLoaded &&
(route.component?.getInitialProps || route.component?.preload)
) {
// client Render for enable ssr, but not sure SSR success
route.component = wrapInitialPropsFetch(route, opts);
}
return (
<Route
{...routeProps}
render={(props: object) => {
return render({ route, opts, props });
}}
/>
);
}
}
export default function renderRoutes(opts: IOpts) {
return opts.routes ? (
<Switch>
{opts.routes.map((route, index) =>
getRouteElement({
route,
index,
opts: {
...opts,
rootRoutes: opts.rootRoutes || opts.routes,
},
}),
)}
</Switch>
) : null;
}