Skip to content

Commit c566520

Browse files
add pure client render
1 parent eddb3c2 commit c566520

File tree

11 files changed

+86
-22
lines changed

11 files changed

+86
-22
lines changed

.env

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ PROD_PORT=5000
55
PROD_HOST=localhost
66
PUBLIC_API_HOST=localhost:9000
77
CRYPTO_KEY=ad$cr3efW89ypg
8-
SSR=false
8+
SSR=true
99
UI=antd
1010
MIDDLEWARE=false
1111
ANIMATE_ROUTER=false

global.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
declare global {
22
const __CLIENT__: boolean;
33
const __SERVER__: boolean;
4+
const __CSR__: boolean;
45
const __SSR__: boolean;
56
const __DEVELOPMENT__: boolean;
67
const __MIDDLEWARE__: boolean;

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
"license": "MIT",
66
"scripts": {
77
"dev": "./script/start-dev",
8+
"dev:csr": "cross-env CSR=true ./script/start-dev",
89
"build": "./script/start-prod",
10+
"build:csr": "cross-env CSR=true ./script/start-prod",
911
"start": "cross-env NODE_ENV=production node ./dist/server/app"
1012
},
1113
"dependencies": {

script/compiler.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ const compilerPromise = (name, compiler, compilerConfig = {}) => {
3030
compiler.hooks.done.tap(name, (stats) => {
3131
if (!stats.hasErrors()) {
3232
development
33-
? console.log(`[${name}]`, chalk.blue(`compiler done, compiler count: ${count++}`))
34-
: console.log(`[${name}]`, chalk.blue("production code compiler done"));
33+
? console.log(`[${name}]`, chalk.blue(`compiler done, compiler count: ${count++}`), `-- time: ${stats.endTime - stats.startTime} ms`)
34+
: console.log(`[${name}]`, chalk.blue("production code compiler done"), `-- time: ${stats.endTime - stats.startTime} ms`);
3535
return resolve();
3636
}
3737
return reject(`Failed to compile ${name}`);

src/client/entry.tsx

+14-6
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import { hydrate, render } from "react-dom";
33
import { loadableReady } from "@loadable/component";
44

55
import { createUniversalStore } from "store";
6+
import { allRoutes } from "router/routes";
67
import { log } from "utils/log";
78
import { safeData } from "utils/safeData";
9+
import { preLoad, preLoadLang } from "utils/preLoad";
810
import { StoreState } from "types/store";
911

1012
const place = document.querySelector("#__content__");
@@ -47,14 +49,20 @@ if (__UI__ === "material") {
4749
Root = originalRoot;
4850
}
4951

50-
if (!window.__ENV__.isSSR) {
51-
// for client side render, will get preloadState on the server, should remove?
52-
loadableReady(() => render(<Root store={store} />, place));
52+
if (__CSR__) {
53+
log("pure render by client", "warn");
54+
Promise.all([preLoadLang({ store, lang: window.__ENV__.LANG }), preLoad(allRoutes, location.pathname, store)]).then(() =>
55+
loadableReady(() => render(<Root store={store} />, place))
56+
);
5357
} else {
54-
if (window.__ENV__.isDEVELOPMENT && window.__ENV__.isMIDDLEWARE) {
55-
log("not hydrate render on client", "warn");
58+
if (!window.__ENV__.isSSR) {
5659
loadableReady(() => render(<Root store={store} />, place));
5760
} else {
58-
loadableReady(() => hydrate(<Root store={store} />, place));
61+
if (window.__ENV__.isDEVELOPMENT && window.__ENV__.isMIDDLEWARE) {
62+
log("not hydrate render on client", "warn");
63+
loadableReady(() => render(<Root store={store} />, place));
64+
} else {
65+
loadableReady(() => hydrate(<Root store={store} />, place));
66+
}
5967
}
6068
}

src/server/entry.ts

+30-13
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
/* eslint-disable @typescript-eslint/no-var-requires */
12
import dotenv from "dotenv";
23
import express from "express";
34

45
import { log } from "utils/log";
56
import { init } from "./init";
67
import { setUp } from "./setup";
78
import { apiHandler } from "./api";
8-
import { render } from "server/middleware/render";
99
import { develop } from "server/middleware/develop";
1010
import { renderError } from "server/middleware/renderError";
1111
import { catchMiddlewareHandler, compose, defaultRunRequestMiddleware, wrapperMiddlewareRequest } from "server/middleware/apiHandler";
@@ -22,15 +22,32 @@ init(app);
2222

2323
app.use("/api", apiHandler);
2424

25-
develop(app).then(() => {
26-
app.use(
27-
wrapperMiddlewareRequest(
28-
{
29-
requestHandler: render,
30-
errorHandler: ({ req, res, code, e }) => renderError({ req, res, e, code }),
31-
},
32-
compose(catchMiddlewareHandler, defaultRunRequestMiddleware)
33-
)
34-
);
35-
app.listen(port, () => log(`App is running: http://localhost:${port}`, "warn"));
36-
});
25+
if (__CSR__) {
26+
const { renderP_CSR } = require("server/middleware/renderPage/renderP_CSR");
27+
develop(app).then(() => {
28+
app.use(
29+
wrapperMiddlewareRequest(
30+
{
31+
requestHandler: renderP_CSR,
32+
errorHandler: ({ req, res, code, e }) => renderError({ req, res, e, code }),
33+
},
34+
compose(catchMiddlewareHandler, defaultRunRequestMiddleware)
35+
)
36+
);
37+
app.listen(port, () => log(`App is running: http://localhost:${port}`, "warn"));
38+
});
39+
} else {
40+
const { render } = require("server/middleware/render");
41+
develop(app).then(() => {
42+
app.use(
43+
wrapperMiddlewareRequest(
44+
{
45+
requestHandler: render,
46+
errorHandler: ({ req, res, code, e }) => renderError({ req, res, e, code }),
47+
},
48+
compose(catchMiddlewareHandler, defaultRunRequestMiddleware)
49+
)
50+
);
51+
app.listen(port, () => log(`App is running: http://localhost:${port}`, "warn"));
52+
});
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { renderToString } from "react-dom/server";
2+
import { ChunkExtractor } from "@loadable/server";
3+
import { AnyAction, composeRender } from "./compose";
4+
import { ServerError } from "server/utils/error";
5+
import { manifestLoadable } from "utils/manifest";
6+
import { HTML } from "template/Html";
7+
import { initLang } from "./middleware/initLang";
8+
import { globalEnv } from "./middleware/globalEnv";
9+
10+
const targetRender: AnyAction = async ({ res, env, lang }) => {
11+
if (!env || !lang) {
12+
throw new ServerError("server 初始化失败", 500);
13+
}
14+
const webExtractor = new ChunkExtractor({ statsFile: manifestLoadable("client") });
15+
const linkElements = webExtractor.getLinkElements();
16+
const styleElements = webExtractor.getStyleElements();
17+
const scriptElements = webExtractor.getScriptElements();
18+
19+
env.isSSR = false;
20+
21+
res.send(
22+
"<!doctype html>" +
23+
renderToString(<HTML env={JSON.stringify(env)} lang={JSON.stringify(lang)} script={scriptElements} link={linkElements.concat(styleElements)} />)
24+
);
25+
};
26+
27+
export const renderP_CSR = composeRender(globalEnv, initLang)(targetRender);

src/store/saga/action/blog.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { call, put } from "redux-saga/effects";
22
import { apiName } from "config/api";
3+
import { log } from "utils/log";
34
import { delay } from "utils/delay";
45
import { getDataFail_Server, getDataLoading_server, getDataSuccess_Server } from "store/reducer/server/share/action";
56

@@ -13,6 +14,7 @@ export function* getBlogData({ done }: { done: () => void }) {
1314
yield put(getDataFail_Server({ name: apiName.blog, data: state }));
1415
}
1516
} catch (e) {
17+
log(`getBlogData error: ${(e as Error).message}`, "error");
1618
yield put(getDataFail_Server({ name: apiName.blog, error: (e as Error).toString() }));
1719
} finally {
1820
done();

src/store/saga/action/home.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { call, put } from "redux-saga/effects";
22
import { apiName } from "config/api";
3+
import { log } from "utils/log";
34
import { delay } from "utils/delay";
45
import { getDataFail_Server, getDataLoading_server, getDataSuccess_Server } from "store/reducer/server/share/action";
56

@@ -13,6 +14,7 @@ export function* getHomeData({ done }: { done: () => void }) {
1314
yield put(getDataFail_Server({ name: apiName.home, data: state }));
1415
}
1516
} catch (e) {
17+
log(`getHomeData error: ${(e as Error).message}`, "error");
1618
yield put(getDataFail_Server({ name: apiName.home, error: (e as Error).toString() }));
1719
} finally {
1820
done();

src/store/saga/action/lang.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { call, put, select } from "redux-saga/effects";
22
import { apiName } from "config/api";
33
import { actionName } from "config/action";
4+
import { log } from "utils/log";
45
import { createRequest } from "utils/fetcher";
56
import { setDataSuccess_client } from "store/reducer/client/share/action";
67
import { getDataFail_Server, getDataLoading_server, getDataSuccess_Server } from "store/reducer/server/share/action";
@@ -15,11 +16,13 @@ export function* getLangData({ done, lang }: { done: () => void; lang: string })
1516
if (code === 0) {
1617
yield put(getDataSuccess_Server({ name: apiName.lang, data }));
1718
} else {
19+
log(`getLangData error: ${state}`, "error");
1820
yield put(getDataFail_Server({ name: apiName.lang, data: state }));
1921
}
2022
}
2123
yield put(setDataSuccess_client({ name: actionName.currentLang, data: lang }));
2224
} catch (e) {
25+
log(`getLangData error: ${typeof e === "object" ? (e as Error).message : e}`, "error");
2326
yield put(getDataFail_Server({ name: apiName.lang, error: (e as Error).toString() }));
2427
} finally {
2528
done();

webpack/plugins.js

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ const pluginsConfig = ({ env, isDev = true, isSSR = true, isMiddleWareDevelop =
2525
env === "client" && new WebpackManifestPlugin({ fileName: isDev ? "manifest-dev.json" : "manifest-prod.json" }),
2626
new webpack.DefinePlugin({
2727
__SSR__: isSSR,
28+
// pure client render
29+
__CSR__: process.env.CSR && JSON.parse(process.env.CSR),
2830
__CLIENT__: env === "client",
2931
__SERVER__: env === "server",
3032
__DEVELOPMENT__: isDev,

0 commit comments

Comments
 (0)