指定 URL 路径作为手动构建的 REACT SSR 应用程序的变量(即不使用 NextJS )
Specify URL path as a variable for a REACT SSR applications built manually (i.e. NOT using NextJS )
注意:我的 SSR 应用程序没有使用 NextJS。我也没有使用 create-react-app 来创建我的应用程序。
我有一个手工构建的 React 服务器端渲染 (SSR) 应用程序(相对于使用像 create-react-app 这样的工具)。我使用 WebPack 来捆绑服务器端代码和客户端代码。 我学习了优秀的 Udemy 课程 https://www.udemy.com/course/server-side-rendering-with-react-and-redux/ 以了解如何创建 React SSR 应用程序
我的申请
应用结构
webpack.base.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: 'file-loader',
},
{
test: /\.(js|jsx)$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
cwd: __dirname,
},
},
],
},
resolve: {
extensions: ['.js', '.jsx'],
},
};
webpack.client.config.js
const path = require('path');
const { merge } = require('webpack-merge');
const CopyPlugin = require('copy-webpack-plugin');
const baseConfig = require('./webpack.base.config.js');
const config = {
entry: './src/client/client.jsx',
output: {
filename: 'client-bundle.js',
path: path.resolve(__dirname, 'public'),
},
plugins: [
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, 'src', 'styles'),
to: path.resolve(__dirname, 'public'),
},
],
}),
],
};
module.exports = merge(baseConfig, config);
webpack.server.config.js
const path = require('path');
const { merge } = require('webpack-merge');
const webpackNodeExternals = require('webpack-node-externals');
const baseConfig = require('./webpack.base.config.js');
const config = {
target: 'node',
entry: './src/server/server.js',
output: {
filename: 'server-bundle.js',
path: path.resolve(__dirname, 'build'),
},
externals: [webpackNodeExternals()],
};
module.exports = merge(baseConfig, config);
路线
{
...Home,
path: '/',
exact: true,
},
{
...Page1,
path: '/page1',
exact: true,
},
客户端路由
<BrowserRouter>
...
</BrowserRouter>
服务器端路由
<StaticRouter context={context} location={req.path}>
...
</StaticRouter>
服务器端生成的HTML模板
<html>
<head>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<div id="root">${content}</div>
<script src="client-bundle.js"></script>
</body>
</html>
package.json 脚本
"scripts": {
"start": "node build/server-bundle.js",
"build": "npm-run-all --parallel prod:build*",
"prod:build-server-bundle": "webpack --config webpack.server.config.js",
"prod:build-client-bundle": "webpack --config webpack.client.config.js",
"dev": "npm-run-all --parallel dev:*",
"dev:run-server": "nodemon --watch build --exec node build/server-bundle.js",
"dev:build-server-bundle": "webpack --config webpack.server.config.js --watch",
"dev:build-client-bundle": "webpack --config webpack.client.config.js --watch",
"lint": "eslint ./src --ext .js,.jsx"
},
运行我的申请
我运行 本地应用程序使用
npm run dev
我的申请 URL 因此
http://localhost:/
http://localhost:/page1
我的要求
我希望我的应用程序有一个可自定义的 URL 路径,例如“/a/b”,这样我的 URL 将是
http://localhost:/a/b
http://localhost:/a/b/page1
或者如果我的路径是“xyz”,我的 URLs 将是
http://localhost:/xyz
http://localhost:/xyz/page1
如何在我的 React SSR 应用程序中启用自定义基本路径。
我试过的
我在 HTML 和路由器中的应用程序中硬编码了一个路径,即
<html>
<head>
<link rel="stylesheet" type="text/css" href="a/b/styles.css">
</head>
<body>
<div id="root">${content}</div>
<script src="a/b/client-bundle.js"></script>
</body>
</html>
<BrowserRouter basename="a/b/">
...
</BrowserRouter>
<StaticRouter context={context} location={req.path} basename="a/b/">
...
</StaticRouter>
但是这样不行,要走下面任一路
http://localhost
http://localhost/a/b
呈现我的主页时没有应用样式表,也没有客户端包。这是因为以下都找不到,return a 404
http://localhost/a/b/styles.css
http://localhost/a/b/client-bundle.js
此外,如果我使用 link 调用路由器,样式和客户端包的 URL 有两次路径,即
client side navigation to
http://localhost:8080/a/b/contact
means styles and client-bundle request urls are
http://localhost/a/b/a/b/styles.css
http://localhost/a/b/a/b/client-bundle.js
您可以简单地添加一个环境变量 basePath,然后使用它来设置您的路线。
路线
{
...Home,
path: `${process.env.basePath}/`,
exact: true,
},
{
...Page1,
path: `${process.env.basePath}/page1`,
exact: true,
},
现在,如果您的 basePath 是“/a/b”,您的索引组件将在 yourdomain/a/b/
上可用,而 page1 将在 yourdomain/a/b/page1
上可用
Hassaan Tauqir 的 post 我在上面标记为答案帮助我改进了解决方案。感谢哈桑。
package.json
更改生产环境的脚本以指定 BASE_URL。
"prod:build-server-bundle": "cross-env BASE_URL=/a/b webpack --config webpack.server.config.js",
"prod:build-client-bundle": "cross-env BASE_URL=/a/b webpack --config webpack.client.config.js",
注意:您必须使用“cross-env”,否则这不适用于所有操作系统,因此我必须先安装“cross-env”
npm install cross-env
我保留了 DEVELOPMENT 脚本不变,因为我在本地测试时不需要路径
"dev:build-server-bundle": "webpack --config webpack.server.config.js --watch",
"dev:build-client-bundle": "webpack --config webpack.client.config.js --watch",
webpack.base.config.js
正在读BASE_URL
“BASE_URL”可在“webpack.base.config.js”中访问。我添加了代码,以便我可以处理是否使用尾部斜杠指定的“BASE_URL”。
// Set up the BASE_URL parameter, ensuring it does not have a trailing slash
let BASE_URL = '';
if (process.env.BASE_URL) {
BASE_URL = process.env.BASE_URL.toString().trim();
if (BASE_URL.substr(-1) === '/') {
BASE_URL = BASE_URL.substr(0, BASE_URL.length - 1);
}
}
公共路径
在“module.exports”中添加“输出”部分并添加“publicPath”设置。 “publicPath”允许您指定应用程序中所有资产的基本路径,例如,我使用以下代码在我的应用程序中引用了图像。
import myImage from '../images/myImage.png';
....
<img src={myImage } alt="myImage " />
....
“publicPath”必须以斜杠结尾,因此如果我们有一个 BASE_URL 我会附加一个 / 否则我将其留空。
output: {
publicPath: (BASE_URL) ? `${BASE_URL}/` : '',
},
有关“publicPath”的详细信息,请参阅https://webpack.js.org/guides/public-path/
webpack.DefinePlugin
在“module.exports”中添加“webpack.DefinePlugin”设置要传递给应用程序重置的环境变量
plugins: [
new webpack.DefinePlugin({
'process.env.BASE_URL': JSON.stringify(BASE_URL),
}),
],
有关“DefaultPlugin”的详细信息,请参阅https://webpack.js.org/plugins/define-plugin/
服务器端路由
将“basename”添加到服务器端路由器,其值为webpack配置文件中“DefinePlugin”中指定的变量
<StaticRouter context={context} location={req.path} basename={process.env.BASE_URL}>
...
</StaticRouter>
客户端路由
将“basename”添加到客户端路由器,其值为webpack配置文件中“DefinePlugin”中指定的变量
<BrowserRouter basename={process.env.BASE_URL}>
...
</BrowserRouter>
注意:我的 SSR 应用程序没有使用 NextJS。我也没有使用 create-react-app 来创建我的应用程序。
我有一个手工构建的 React 服务器端渲染 (SSR) 应用程序(相对于使用像 create-react-app 这样的工具)。我使用 WebPack 来捆绑服务器端代码和客户端代码。 我学习了优秀的 Udemy 课程 https://www.udemy.com/course/server-side-rendering-with-react-and-redux/ 以了解如何创建 React SSR 应用程序
我的申请
应用结构
webpack.base.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: 'file-loader',
},
{
test: /\.(js|jsx)$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
cwd: __dirname,
},
},
],
},
resolve: {
extensions: ['.js', '.jsx'],
},
};
webpack.client.config.js
const path = require('path');
const { merge } = require('webpack-merge');
const CopyPlugin = require('copy-webpack-plugin');
const baseConfig = require('./webpack.base.config.js');
const config = {
entry: './src/client/client.jsx',
output: {
filename: 'client-bundle.js',
path: path.resolve(__dirname, 'public'),
},
plugins: [
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, 'src', 'styles'),
to: path.resolve(__dirname, 'public'),
},
],
}),
],
};
module.exports = merge(baseConfig, config);
webpack.server.config.js
const path = require('path');
const { merge } = require('webpack-merge');
const webpackNodeExternals = require('webpack-node-externals');
const baseConfig = require('./webpack.base.config.js');
const config = {
target: 'node',
entry: './src/server/server.js',
output: {
filename: 'server-bundle.js',
path: path.resolve(__dirname, 'build'),
},
externals: [webpackNodeExternals()],
};
module.exports = merge(baseConfig, config);
路线
{
...Home,
path: '/',
exact: true,
},
{
...Page1,
path: '/page1',
exact: true,
},
客户端路由
<BrowserRouter>
...
</BrowserRouter>
服务器端路由
<StaticRouter context={context} location={req.path}>
...
</StaticRouter>
服务器端生成的HTML模板
<html>
<head>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<div id="root">${content}</div>
<script src="client-bundle.js"></script>
</body>
</html>
package.json 脚本
"scripts": {
"start": "node build/server-bundle.js",
"build": "npm-run-all --parallel prod:build*",
"prod:build-server-bundle": "webpack --config webpack.server.config.js",
"prod:build-client-bundle": "webpack --config webpack.client.config.js",
"dev": "npm-run-all --parallel dev:*",
"dev:run-server": "nodemon --watch build --exec node build/server-bundle.js",
"dev:build-server-bundle": "webpack --config webpack.server.config.js --watch",
"dev:build-client-bundle": "webpack --config webpack.client.config.js --watch",
"lint": "eslint ./src --ext .js,.jsx"
},
运行我的申请
我运行 本地应用程序使用
npm run dev
我的申请 URL 因此
http://localhost:/
http://localhost:/page1
我的要求
我希望我的应用程序有一个可自定义的 URL 路径,例如“/a/b”,这样我的 URL 将是
http://localhost:/a/b
http://localhost:/a/b/page1
或者如果我的路径是“xyz”,我的 URLs 将是
http://localhost:/xyz
http://localhost:/xyz/page1
如何在我的 React SSR 应用程序中启用自定义基本路径。
我试过的
我在 HTML 和路由器中的应用程序中硬编码了一个路径,即
<html>
<head>
<link rel="stylesheet" type="text/css" href="a/b/styles.css">
</head>
<body>
<div id="root">${content}</div>
<script src="a/b/client-bundle.js"></script>
</body>
</html>
<BrowserRouter basename="a/b/">
...
</BrowserRouter>
<StaticRouter context={context} location={req.path} basename="a/b/">
...
</StaticRouter>
但是这样不行,要走下面任一路
http://localhost http://localhost/a/b
呈现我的主页时没有应用样式表,也没有客户端包。这是因为以下都找不到,return a 404
http://localhost/a/b/styles.css
http://localhost/a/b/client-bundle.js
此外,如果我使用 link 调用路由器,样式和客户端包的 URL 有两次路径,即
client side navigation to
http://localhost:8080/a/b/contact
means styles and client-bundle request urls are
http://localhost/a/b/a/b/styles.css
http://localhost/a/b/a/b/client-bundle.js
您可以简单地添加一个环境变量 basePath,然后使用它来设置您的路线。
路线
{
...Home,
path: `${process.env.basePath}/`,
exact: true,
},
{
...Page1,
path: `${process.env.basePath}/page1`,
exact: true,
},
现在,如果您的 basePath 是“/a/b”,您的索引组件将在 yourdomain/a/b/
上可用,而 page1 将在 yourdomain/a/b/page1
Hassaan Tauqir 的 post 我在上面标记为答案帮助我改进了解决方案。感谢哈桑。
package.json
更改生产环境的脚本以指定 BASE_URL。
"prod:build-server-bundle": "cross-env BASE_URL=/a/b webpack --config webpack.server.config.js",
"prod:build-client-bundle": "cross-env BASE_URL=/a/b webpack --config webpack.client.config.js",
注意:您必须使用“cross-env”,否则这不适用于所有操作系统,因此我必须先安装“cross-env”
npm install cross-env
我保留了 DEVELOPMENT 脚本不变,因为我在本地测试时不需要路径
"dev:build-server-bundle": "webpack --config webpack.server.config.js --watch",
"dev:build-client-bundle": "webpack --config webpack.client.config.js --watch",
webpack.base.config.js
正在读BASE_URL
“BASE_URL”可在“webpack.base.config.js”中访问。我添加了代码,以便我可以处理是否使用尾部斜杠指定的“BASE_URL”。
// Set up the BASE_URL parameter, ensuring it does not have a trailing slash
let BASE_URL = '';
if (process.env.BASE_URL) {
BASE_URL = process.env.BASE_URL.toString().trim();
if (BASE_URL.substr(-1) === '/') {
BASE_URL = BASE_URL.substr(0, BASE_URL.length - 1);
}
}
公共路径
在“module.exports”中添加“输出”部分并添加“publicPath”设置。 “publicPath”允许您指定应用程序中所有资产的基本路径,例如,我使用以下代码在我的应用程序中引用了图像。
import myImage from '../images/myImage.png';
....
<img src={myImage } alt="myImage " />
....
“publicPath”必须以斜杠结尾,因此如果我们有一个 BASE_URL 我会附加一个 / 否则我将其留空。
output: {
publicPath: (BASE_URL) ? `${BASE_URL}/` : '',
},
有关“publicPath”的详细信息,请参阅https://webpack.js.org/guides/public-path/
webpack.DefinePlugin
在“module.exports”中添加“webpack.DefinePlugin”设置要传递给应用程序重置的环境变量
plugins: [
new webpack.DefinePlugin({
'process.env.BASE_URL': JSON.stringify(BASE_URL),
}),
],
有关“DefaultPlugin”的详细信息,请参阅https://webpack.js.org/plugins/define-plugin/
服务器端路由
将“basename”添加到服务器端路由器,其值为webpack配置文件中“DefinePlugin”中指定的变量
<StaticRouter context={context} location={req.path} basename={process.env.BASE_URL}>
...
</StaticRouter>
客户端路由
将“basename”添加到客户端路由器,其值为webpack配置文件中“DefinePlugin”中指定的变量
<BrowserRouter basename={process.env.BASE_URL}>
...
</BrowserRouter>