diff --git a/examples/ssr-demo/.umirc.ts b/examples/ssr-demo/.umirc.ts index 7f04c0edcf9b..3226de45a815 100644 --- a/examples/ssr-demo/.umirc.ts +++ b/examples/ssr-demo/.umirc.ts @@ -7,20 +7,8 @@ export default { clientLoader: {}, ssr: {}, exportStatic: {}, - // mako: {}, - // mako: { - // plugins: [ - // { - // load: () => {}, - // }, - // ], - // }, - // ssr: { - // // builder: 'mako', - // }, - // exportStatic: {}, styles: [`body { color: red; }`], - + // mako: {}, metas: [ { name: 'test', diff --git a/examples/ssr-demo/src/pages/index.tsx b/examples/ssr-demo/src/pages/index.tsx index 2d3d2fb2f2b8..a86932621d0b 100644 --- a/examples/ssr-demo/src/pages/index.tsx +++ b/examples/ssr-demo/src/pages/index.tsx @@ -21,7 +21,6 @@ import styles from './index.less'; // @ts-ignore import umiLogo from './umi.png'; // @ts-ignore -import styles1 from './a.module.css'; export default function HomePage() { const clientLoaderData = useClientLoaderData(); @@ -42,7 +41,7 @@ export default function HomePage() { return (
id: {id}
This is index.tsx
I should be pink
diff --git a/packages/preset-umi/src/features/ssr/ssr.ts b/packages/preset-umi/src/features/ssr/ssr.ts index 22d0da952d32..3409ad7c554e 100644 --- a/packages/preset-umi/src/features/ssr/ssr.ts +++ b/packages/preset-umi/src/features/ssr/ssr.ts @@ -37,6 +37,7 @@ export default (api: IApi) => { pureApp: zod.boolean(), pureHtml: zod.boolean(), }), + useStream: zod.boolean().default(true), }) .deepPartial(); }, diff --git a/packages/preset-umi/src/features/tmpFiles/tmpFiles.ts b/packages/preset-umi/src/features/tmpFiles/tmpFiles.ts index f75e00c4e4bc..aff9e9b3cfc9 100644 --- a/packages/preset-umi/src/features/tmpFiles/tmpFiles.ts +++ b/packages/preset-umi/src/features/tmpFiles/tmpFiles.ts @@ -301,6 +301,7 @@ declare module '*.txt' { }), ).join('\n'); + const ssrConfig = api.config.ssr; const __INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = api.config.ssr ?.__INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED ?? { pureApp: false, @@ -331,7 +332,8 @@ declare module '*.txt' { __INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: JSON.stringify( __INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, ), - hydrate: !!api.config.ssr, + hydrate: !!ssrConfig, + useStream: ssrConfig?.useStream ?? true, reactRouter5Compat: !!api.config.reactRouter5Compat, loadingComponent: api.appData.globalLoading, }, @@ -506,7 +508,7 @@ if (process.env.NODE_ENV === 'development') { }); // umi.server.ts - if (api.config.ssr) { + if (ssrConfig) { const umiPluginPath = winPath(join(umiDir, 'client/client/plugin.js')); const umiServerPath = winPath(require.resolve('@umijs/server/dist/ssr')); @@ -563,6 +565,7 @@ if (process.env.NODE_ENV === 'development') { ), mountElementId, basename: api.config.base, + useStream: ssrConfig?.useStream ?? true, }, }); } diff --git a/packages/preset-umi/templates/server.tpl b/packages/preset-umi/templates/server.tpl index 1c17334cb48a..f3c08393018c 100644 --- a/packages/preset-umi/templates/server.tpl +++ b/packages/preset-umi/templates/server.tpl @@ -62,7 +62,8 @@ const createOpts = { htmlPageOpts: {{{htmlPageOpts}}}, __INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {{{__INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED}}}, mountElementId: '{{{mountElementId}}}', - basename: '{{{basename}}}' + basename: '{{{basename}}}', + useStream: {{{useStream}}} }; const requestHandler = createRequestHandler(createOpts); /** diff --git a/packages/preset-umi/templates/umi.tpl b/packages/preset-umi/templates/umi.tpl index 8df0f745fe53..0ee335e6479d 100644 --- a/packages/preset-umi/templates/umi.tpl +++ b/packages/preset-umi/templates/umi.tpl @@ -50,6 +50,7 @@ async function render() { {{#hydrate}} hydrate: true, {{/hydrate}} + useStream: {{{useStream}}}, {{#reactRouter5Compat}} reactRouter5Compat: true, {{/reactRouter5Compat}} diff --git a/packages/renderer-react/src/browser.tsx b/packages/renderer-react/src/browser.tsx index 2c90cb6a738c..116d60d1b2be 100644 --- a/packages/renderer-react/src/browser.tsx +++ b/packages/renderer-react/src/browser.tsx @@ -145,6 +145,11 @@ export type RenderClientOpts = { */ hydrate?: boolean; + /** + * ssr 是否启用流式渲染, 默认 true, 对 SEO 存在一定的负优化 + */ + useStream?: boolean; + /** * 直接返回组件,是为了方便测试 */ @@ -188,6 +193,7 @@ const getBrowser = ( routeComponents: opts.routeComponents, loadingComponent: opts.loadingComponent, reactRouter5Compat: opts.reactRouter5Compat, + useStream: opts.useStream, }); opts.pluginManager.applyPlugins({ key: 'patchClientRoutes', diff --git a/packages/renderer-react/src/routes.tsx b/packages/renderer-react/src/routes.tsx index 709499468b5b..0cbbdc4ff177 100644 --- a/packages/renderer-react/src/routes.tsx +++ b/packages/renderer-react/src/routes.tsx @@ -17,8 +17,9 @@ export function createClientRoutes(opts: { parentId?: string; loadingComponent?: React.ReactNode; reactRouter5Compat?: boolean; + useStream?: boolean; }) { - const { routesById, parentId, routeComponents } = opts; + const { routesById, parentId, routeComponents, useStream = true } = opts; return Object.keys(routesById) .filter((id) => routesById[id].parentId === parentId) .map((id) => { @@ -34,6 +35,7 @@ export function createClientRoutes(opts: { (rid) => routesById[rid].parentId === id, ).length > 0, }), + useStream, }); const children = createClientRoutes({ routesById, @@ -41,6 +43,7 @@ export function createClientRoutes(opts: { parentId: route.id, loadingComponent: opts.loadingComponent, reactRouter5Compat: opts.reactRouter5Compat, + useStream, }); if (children.length > 0) { route.children = children; @@ -74,8 +77,9 @@ function createClientRoute(opts: { loadingComponent?: React.ReactNode; hasChildren?: boolean; reactRouter5Compat?: boolean; + useStream?: boolean; }): IClientRoute { - const { route } = opts; + const { route, useStream = true } = opts; const { redirect, ...props } = route; const Remote = opts.reactRouter5Compat ? RemoteComponentReactRouter5 @@ -93,6 +97,7 @@ function createClientRoute(opts: { loader={React.memo(opts.routeComponent)} loadingComponent={opts.loadingComponent || DefaultLoading} hasChildren={opts.hasChildren} + useStream={useStream} /> ), @@ -117,28 +122,33 @@ function RemoteComponentReactRouter5(props: any) { // staticContext 没有兼容 好像没看到对应的兼容写法 const Component = props.loader; - - return ( + const ComponentProps = { + location: history.location, + match, + history, + params, + route, + routes: clientRoutes, + }; + const Remote = () => ( +