Привет, я создаю приложение React с SSR. Серверная и клиентская части написаны машинописно и передаются отдельно. Вот структура приложения для лучшего понимания:
Вот упрощенные конфигурации веб-пакетов для пакетов сервера и клиента:
// webpack.client.js
module.exports = {
mode: "development",
resolve: {
modules: ["src", "static", "node_modules"],
extensions: [".ts", ".tsx", ".js", ".jsx"],
},
entry: [
"./src/client/index.tsx"
],
output: {
filename: "bundle.js",
path: PUBLIC_PATH,
},
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: "ts-loader",
}
],
},
]
},
plugins: [
new webpack.DefinePlugin({ IS_SERVER: false })
]
};
Конфигурация сервера выглядит в значительной степени за исключением target
и externals
//webpack.server.js
const config = {
mode: "development",
resolve: {
modules: ["src", "static", "node_modules"],
extensions: [".ts", ".tsx", ".js", ".jsx"],
},
externals: [webpackNodeExternals()],
target: 'node',
entry: [
"./src/server/index.ts"
],
output: {
filename: "bundle.js",
path: SERVER_BUILD_PATH
},
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: "ts-loader",
}
],
},
]
},
plugins: [
new webpack.DefinePlugin({ IS_SERVER: true })
]
};
В коде сервера я получил функцию renderer
, которая переводит приложение React в строку.
// renderer.tsx
import React from "react";
import { renderToString } from "react-dom/server";
import { App } from "client/App";
const html = (app) => `
<html>
<head>
</head>
<body>
<div id="root">${app}</div>
<script src="/public/bundle.js"></script>
</body>
</html>
`;
export async function renderer(req) {
const app = renderToString(<App />);
return html(app);
}
Который затем возвращается клиенту через экспресс-сервер.
//index.ts
app.get("*", async (req, res) => {
const content = await renderer(req);
res.send(content);
});
Как видите, обе части должны транспортировать приложение React. Вопрос в том, как я могу повторно использовать перенесенный клиентский код в комплекте серверов, чтобы конфигурации сервера нужно было только переносить index.ts
и renderer.tsx
?