Skip to content

Commit

Permalink
add pure client render
Browse files Browse the repository at this point in the history
  • Loading branch information
MrWangJustToDo committed Jan 18, 2022
1 parent eddb3c2 commit c566520
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ PROD_PORT=5000
PROD_HOST=localhost
PUBLIC_API_HOST=localhost:9000
CRYPTO_KEY=ad$cr3efW89ypg
SSR=false
SSR=true
UI=antd
MIDDLEWARE=false
ANIMATE_ROUTER=false
Expand Down
1 change: 1 addition & 0 deletions global.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
declare global {
const __CLIENT__: boolean;
const __SERVER__: boolean;
const __CSR__: boolean;
const __SSR__: boolean;
const __DEVELOPMENT__: boolean;
const __MIDDLEWARE__: boolean;
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"license": "MIT",
"scripts": {
"dev": "./script/start-dev",
"dev:csr": "cross-env CSR=true ./script/start-dev",
"build": "./script/start-prod",
"build:csr": "cross-env CSR=true ./script/start-prod",
"start": "cross-env NODE_ENV=production node ./dist/server/app"
},
"dependencies": {
Expand Down
4 changes: 2 additions & 2 deletions script/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ const compilerPromise = (name, compiler, compilerConfig = {}) => {
compiler.hooks.done.tap(name, (stats) => {
if (!stats.hasErrors()) {
development
? console.log(`[${name}]`, chalk.blue(`compiler done, compiler count: ${count++}`))
: console.log(`[${name}]`, chalk.blue("production code compiler done"));
? console.log(`[${name}]`, chalk.blue(`compiler done, compiler count: ${count++}`), `-- time: ${stats.endTime - stats.startTime} ms`)
: console.log(`[${name}]`, chalk.blue("production code compiler done"), `-- time: ${stats.endTime - stats.startTime} ms`);
return resolve();
}
return reject(`Failed to compile ${name}`);
Expand Down
20 changes: 14 additions & 6 deletions src/client/entry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { hydrate, render } from "react-dom";
import { loadableReady } from "@loadable/component";

import { createUniversalStore } from "store";
import { allRoutes } from "router/routes";
import { log } from "utils/log";
import { safeData } from "utils/safeData";
import { preLoad, preLoadLang } from "utils/preLoad";
import { StoreState } from "types/store";

const place = document.querySelector("#__content__");
Expand Down Expand Up @@ -47,14 +49,20 @@ if (__UI__ === "material") {
Root = originalRoot;
}

if (!window.__ENV__.isSSR) {
// for client side render, will get preloadState on the server, should remove?
loadableReady(() => render(<Root store={store} />, place));
if (__CSR__) {
log("pure render by client", "warn");
Promise.all([preLoadLang({ store, lang: window.__ENV__.LANG }), preLoad(allRoutes, location.pathname, store)]).then(() =>
loadableReady(() => render(<Root store={store} />, place))
);
} else {
if (window.__ENV__.isDEVELOPMENT && window.__ENV__.isMIDDLEWARE) {
log("not hydrate render on client", "warn");
if (!window.__ENV__.isSSR) {
loadableReady(() => render(<Root store={store} />, place));
} else {
loadableReady(() => hydrate(<Root store={store} />, place));
if (window.__ENV__.isDEVELOPMENT && window.__ENV__.isMIDDLEWARE) {
log("not hydrate render on client", "warn");
loadableReady(() => render(<Root store={store} />, place));
} else {
loadableReady(() => hydrate(<Root store={store} />, place));
}
}
}
43 changes: 30 additions & 13 deletions src/server/entry.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import dotenv from "dotenv";
import express from "express";

import { log } from "utils/log";
import { init } from "./init";
import { setUp } from "./setup";
import { apiHandler } from "./api";
import { render } from "server/middleware/render";
import { develop } from "server/middleware/develop";
import { renderError } from "server/middleware/renderError";
import { catchMiddlewareHandler, compose, defaultRunRequestMiddleware, wrapperMiddlewareRequest } from "server/middleware/apiHandler";
Expand All @@ -22,15 +22,32 @@ init(app);

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

develop(app).then(() => {
app.use(
wrapperMiddlewareRequest(
{
requestHandler: render,
errorHandler: ({ req, res, code, e }) => renderError({ req, res, e, code }),
},
compose(catchMiddlewareHandler, defaultRunRequestMiddleware)
)
);
app.listen(port, () => log(`App is running: http://localhost:${port}`, "warn"));
});
if (__CSR__) {
const { renderP_CSR } = require("server/middleware/renderPage/renderP_CSR");
develop(app).then(() => {
app.use(
wrapperMiddlewareRequest(
{
requestHandler: renderP_CSR,
errorHandler: ({ req, res, code, e }) => renderError({ req, res, e, code }),
},
compose(catchMiddlewareHandler, defaultRunRequestMiddleware)
)
);
app.listen(port, () => log(`App is running: http://localhost:${port}`, "warn"));
});
} else {
const { render } = require("server/middleware/render");
develop(app).then(() => {
app.use(
wrapperMiddlewareRequest(
{
requestHandler: render,
errorHandler: ({ req, res, code, e }) => renderError({ req, res, e, code }),
},
compose(catchMiddlewareHandler, defaultRunRequestMiddleware)
)
);
app.listen(port, () => log(`App is running: http://localhost:${port}`, "warn"));
});
}
27 changes: 27 additions & 0 deletions src/server/middleware/renderPage/renderP_CSR.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { renderToString } from "react-dom/server";
import { ChunkExtractor } from "@loadable/server";
import { AnyAction, composeRender } from "./compose";
import { ServerError } from "server/utils/error";
import { manifestLoadable } from "utils/manifest";
import { HTML } from "template/Html";
import { initLang } from "./middleware/initLang";
import { globalEnv } from "./middleware/globalEnv";

const targetRender: AnyAction = async ({ res, env, lang }) => {
if (!env || !lang) {
throw new ServerError("server 初始化失败", 500);
}
const webExtractor = new ChunkExtractor({ statsFile: manifestLoadable("client") });
const linkElements = webExtractor.getLinkElements();
const styleElements = webExtractor.getStyleElements();
const scriptElements = webExtractor.getScriptElements();

env.isSSR = false;

res.send(
"<!doctype html>" +
renderToString(<HTML env={JSON.stringify(env)} lang={JSON.stringify(lang)} script={scriptElements} link={linkElements.concat(styleElements)} />)
);
};

export const renderP_CSR = composeRender(globalEnv, initLang)(targetRender);
2 changes: 2 additions & 0 deletions src/store/saga/action/blog.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { call, put } from "redux-saga/effects";
import { apiName } from "config/api";
import { log } from "utils/log";
import { delay } from "utils/delay";
import { getDataFail_Server, getDataLoading_server, getDataSuccess_Server } from "store/reducer/server/share/action";

Expand All @@ -13,6 +14,7 @@ export function* getBlogData({ done }: { done: () => void }) {
yield put(getDataFail_Server({ name: apiName.blog, data: state }));
}
} catch (e) {
log(`getBlogData error: ${(e as Error).message}`, "error");
yield put(getDataFail_Server({ name: apiName.blog, error: (e as Error).toString() }));
} finally {
done();
Expand Down
2 changes: 2 additions & 0 deletions src/store/saga/action/home.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { call, put } from "redux-saga/effects";
import { apiName } from "config/api";
import { log } from "utils/log";
import { delay } from "utils/delay";
import { getDataFail_Server, getDataLoading_server, getDataSuccess_Server } from "store/reducer/server/share/action";

Expand All @@ -13,6 +14,7 @@ export function* getHomeData({ done }: { done: () => void }) {
yield put(getDataFail_Server({ name: apiName.home, data: state }));
}
} catch (e) {
log(`getHomeData error: ${(e as Error).message}`, "error");
yield put(getDataFail_Server({ name: apiName.home, error: (e as Error).toString() }));
} finally {
done();
Expand Down
3 changes: 3 additions & 0 deletions src/store/saga/action/lang.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { call, put, select } from "redux-saga/effects";
import { apiName } from "config/api";
import { actionName } from "config/action";
import { log } from "utils/log";
import { createRequest } from "utils/fetcher";
import { setDataSuccess_client } from "store/reducer/client/share/action";
import { getDataFail_Server, getDataLoading_server, getDataSuccess_Server } from "store/reducer/server/share/action";
Expand All @@ -15,11 +16,13 @@ export function* getLangData({ done, lang }: { done: () => void; lang: string })
if (code === 0) {
yield put(getDataSuccess_Server({ name: apiName.lang, data }));
} else {
log(`getLangData error: ${state}`, "error");
yield put(getDataFail_Server({ name: apiName.lang, data: state }));
}
}
yield put(setDataSuccess_client({ name: actionName.currentLang, data: lang }));
} catch (e) {
log(`getLangData error: ${typeof e === "object" ? (e as Error).message : e}`, "error");
yield put(getDataFail_Server({ name: apiName.lang, error: (e as Error).toString() }));
} finally {
done();
Expand Down
2 changes: 2 additions & 0 deletions webpack/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const pluginsConfig = ({ env, isDev = true, isSSR = true, isMiddleWareDevelop =
env === "client" && new WebpackManifestPlugin({ fileName: isDev ? "manifest-dev.json" : "manifest-prod.json" }),
new webpack.DefinePlugin({
__SSR__: isSSR,
// pure client render
__CSR__: process.env.CSR && JSON.parse(process.env.CSR),
__CLIENT__: env === "client",
__SERVER__: env === "server",
__DEVELOPMENT__: isDev,
Expand Down

0 comments on commit c566520

Please sign in to comment.