Skip to content

Commit

Permalink
fix: 修复 getClientRootComponent 方法. 增加 ssr basename 案例
Browse files Browse the repository at this point in the history
  • Loading branch information
jaykou25 committed Jul 11, 2024
1 parent 3b33daf commit 2568548
Show file tree
Hide file tree
Showing 19 changed files with 265 additions and 44 deletions.
6 changes: 6 additions & 0 deletions examples/ssg-basename/.umirc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
exportStatic: {},
ssr: {},
base: '/base/',
publicPath: '/base/', // 布署时需要布署在 base 文件夹下.
};
14 changes: 14 additions & 0 deletions examples/ssg-basename/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "@example/ssg-basename",
"private": true,
"description": "该案例用于测试 ssg 预渲染. 当 umi 配置表中设置了 base 后, ssg 输出的预渲染页面中的 a 标签需要有正确的 base 前缀",
"scripts": {
"build": "umi build",
"dev": "umi dev",
"setup": "umi setup",
"start": "npm run dev"
},
"dependencies": {
"umi": "workspace:*"
}
}
14 changes: 14 additions & 0 deletions examples/ssg-basename/src/layouts/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';

import { Outlet } from 'umi';

const Layout = () => {
return (
<div>
<div style={{ marginBottom: '10px' }}>HEADER</div>
<Outlet />
</div>
);
};

export default Layout;
24 changes: 24 additions & 0 deletions examples/ssg-basename/src/pages/about/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { useState } from 'react';
import { Link } from 'umi';

const About = () => {
const [num, setNum] = useState(0);

return (
<div>
<div>
<strong>ABOUT</strong> page
<button
style={{ marginLeft: '5px' }}
onClick={() => setNum((val) => val + 1)}
>
couts: {num}
</button>
</div>
<br />
<Link to="/">to home</Link>
</div>
);
};

export default About;
24 changes: 24 additions & 0 deletions examples/ssg-basename/src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { useState } from 'react';
import { Link } from 'umi';

const Home = () => {
const [num, setNum] = useState(0);
return (
<div>
<div>
<strong>HOME</strong> page
<button
style={{ marginLeft: '5px' }}
onClick={() => setNum((val) => val + 1)}
>
couts: {num}
</button>
</div>

<br />
<Link to="/about">to about</Link>
</div>
);
};

export default Home;
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export default {
ssr: {},
exportStatic: {},
hash: true,
base: '/a/',
base: '/base/',
};
File renamed without changes.
37 changes: 37 additions & 0 deletions examples/ssr-basename/plugins/mylogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { writeFileSync } from 'fs';
import { IApi } from 'umi';

export default (api: IApi) => {
api.describe({
key: 'mylogger',
config: {
schema(joi) {
return joi.string();
},
},
enableBy: api.EnableBy.register,
});

api.registerMethod({
name: 'mylogger',
fn: (pathname, obj) => {
let cache = [];
const str = JSON.stringify(obj, function (key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
// 移除
return;
}
// 收集所有的值
cache.push(value);
}
return value;
});
cache = null;

writeFileSync(pathname, str, () => {
console.log('write log finish');
});
},
});
};
28 changes: 28 additions & 0 deletions examples/ssr-basename/plugins/picker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { IApi } from 'umi';

export default (api: IApi) => {
api.describe({
key: 'routePicker',
config: {
schema({ zod }) {
return zod.array(zod.string());
},
},
enableBy: () => {
return api.name === 'dev';
},
});

console.log('插件 picker 注册');

api.onCheck(async () => {
// console.log('插件', api.appData.routes);
console.log('插件config', api.config);
api.config.routes = [
{
path: '/',
component: 'index',
},
];
});
};
14 changes: 14 additions & 0 deletions examples/ssr-basename/src/layouts/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';

import { Outlet } from 'umi';

const Layout = () => {
return (
<div>
<div style={{ marginBottom: '10px' }}>HEADER</div>
<Outlet />
</div>
);
};

export default Layout;
3 changes: 3 additions & 0 deletions examples/ssr-basename/src/pages/about/form/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function AboutForm() {
return <div>About Form</div>;
}
24 changes: 24 additions & 0 deletions examples/ssr-basename/src/pages/about/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { useState } from 'react';
import { Link } from 'umi';

const About = () => {
const [num, setNum] = useState(0);

return (
<div>
<div>
<strong>ABOUT</strong> page
<button
style={{ marginLeft: '5px' }}
onClick={() => setNum((val) => val + 1)}
>
couts: {num}
</button>
</div>
<br />
<Link to="/">to home</Link>
</div>
);
};

export default About;
24 changes: 24 additions & 0 deletions examples/ssr-basename/src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { useState } from 'react';
import { Link } from 'umi';

const Home = () => {
const [num, setNum] = useState(0);
return (
<div>
<div>
<strong>HOME</strong> page
<button
style={{ marginLeft: '5px' }}
onClick={() => setNum((val) => val + 1)}
>
couts: {num}
</button>
</div>

<br />
<Link to="/about">to about</Link>
</div>
);
};

export default Home;
15 changes: 0 additions & 15 deletions examples/ssr-export-static-basename/src/pages/about/index.tsx

This file was deleted.

15 changes: 0 additions & 15 deletions examples/ssr-export-static-basename/src/pages/index.tsx

This file was deleted.

23 changes: 21 additions & 2 deletions packages/preset-umi/src/features/exportStatic/exportStatic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,32 @@ function getExportHtmlData(routes: Record<string, IRoute>): IExportHtmlItem[] {
async function getPreRenderedHTML(api: IApi, htmlTpl: string, path: string) {
const {
exportStatic: { ignorePreRenderError = false },
base,
} = api.config;
markupRender ??= require(absServerBuildPath(api))._markupGenerator;

try {
const html = await markupRender(path);
const location = `${base.endsWith('/') ? base.slice(0, -1) : base}${path}`;
const markup = await markupRender(location);
const [mainTpl, extraTpl = ''] = markup.split('</html>');
// TODO: improve return type for markup generator
const helmetContent = mainTpl.match(
/<head>[^]*?(<[^>]+data-rh[^]+)<\/head>/,
)?.[1];
const bodyContent = mainTpl.match(/<body[^>]*>([^]+?)<\/body>/)?.[1];

htmlTpl = htmlTpl
// append helmet content
.replace('</head>', `${helmetContent || ''}</head>`)
// replace #root with pre-rendered body content
.replace(
new RegExp(`<div id="${api.config.mountElementId}"[^>]*>.*?</div>`),
bodyContent,
)
// append hidden templates
.replace(/$/, `${extraTpl}`);
logger.info(`Pre-render for ${path}`);
return html;
return htmlTpl;
} catch (err) {
logger.error(`Pre-render ${path} error: ${err}`);
if (!ignorePreRenderError) {
Expand Down
5 changes: 5 additions & 0 deletions packages/renderer-react/src/server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ export async function getClientRootComponent(opts: IRootComponentOptions) {
routes: clientRoutes,
},
});

let rootContainer = (
// 这里的 location 需要包含 basename, 否则会影响 StaticRouter 的匹配.
// 由于 getClientRootComponent 方法会同时用于 ssr 和 ssg, 所以在调用该方法时需要注意传入的 location 是否包含 basename.
// 1. 在用于 ssr 时传入的 location 来源于 request.url, 它是包含 basename 的, 所以没有问题.
// 2. 但是在用于 ssg 时(static export), 需要注意传入的 locaiton 要拼接上 basename.
<StaticRouter basename={basename} location={opts.location}>
<Routes />
</StaticRouter>
Expand Down
17 changes: 14 additions & 3 deletions packages/server/src/ssr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ function createJSXGenerator(opts: CreateRequestHandlerOptions) {
},
});

const matches = matchRoutesForSSR(url, routes);
const matches = matchRoutesForSSR(url, routes, basename);

if (matches.length === 0) {
return;
}
Expand Down Expand Up @@ -606,9 +607,19 @@ export function createAppRootElement(opts: CreateRequestHandlerOptions) {
};
}

function matchRoutesForSSR(reqUrl: string, routesById: IRoutesById) {
function matchRoutesForSSR(
reqUrl: string,
routesById: IRoutesById,
basename?: string,
) {
// react-router-dom 在 v6.4.0 版本上增加了对 basename 结尾为斜杠的支持
// 目前 @umijs/server 依赖的 react-router-dom 版本为 v6.3.0
// 如果传入的 basename 结尾带斜杠, 比如 '/base/', 则会匹配不到.
// 日后如果依赖的版本升级, 此段代码可以删除.
const _basename = basename?.endsWith('/') ? basename.slice(0, -1) : basename;

return (
matchRoutes(createClientRoutes({ routesById }), reqUrl)?.map(
matchRoutes(createClientRoutes({ routesById }), reqUrl, _basename)?.map(
(route: any) => route.route.id,
) || []
);
Expand Down
19 changes: 12 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 2568548

Please sign in to comment.