问题背景
使用create-react-app会默认创建一个单页应用(SPA: Single Page Application)。即如下图所示:
我们希望生成多个html文件,但仍加载同样的js文件。就像这样:
之所以要这样做,是为了满足不同场景下SEO的需要。比如我们可以生成en.html、jp.html、zh.html等各异的html入口文件,并在各自的
<head>
中配置符合相应语言习惯的<metaname="description" />
标签和<title>
,但是它们共享同一套业务逻辑,即引入的为同一个js入口文件index.js
。
安装CRACO
CRACO(Create React App Configuration Override)是一款可以修改CRA应用的构建配置而无需使用 eject 的工具。
这里是官方的安装教程,不再赘述。
我们为了复刻出多个html入口,只需要做两件事:
- 打包生成多个html
- 开发环境DevServer配置:重新配置DevServer的入口html
打包生成多个html
在webpack的plugins列表中每添加一个new HtmlWebpackPlugin()
,就会相应的生成一个html入口文件。
于是,在 craco.config.js
中添加配置:
const path = require('path')
const fs = require('fs')
const { whenProd } = require('@craco/craco')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const appDirectory = fs.realpathSync(process.cwd())
const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath)
// help doc: https://github.com/gsoft-inc/craco
module.exports = {
webpack: {
plugins: {
add: [
// 另一个html入口
[
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
filename: 'another-index.html',
template: resolveApp('public/another-index.html'), // 需要在 public 文件夹中添加一个 another-index.html 文件作为模版
},
whenProd(() => ({
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}))
)
),
'prepend', // html-webpack-plugin 需要在plugins列表中前置,需要 @craco/craco > v6.3.0
],
] /* An array of plugins */,
},
},
}
开发环境DevServer配置
CRACO 内置了 DevServer , DevServer默认将使用index.html作为入口文件。
可以在 craco.config.js
中添加配置来切换入口:
// help doc: https://github.com/gsoft-inc/craco
module.exports = {
devServer: {
// 选择使用html入口( /index.html | /another-index.html )进行调试, 默认为 /index.html
historyApiFallback: {
disableDotRule: true,
index: '/another-index.html',
},
},
}
也可以使用 rewrites 实现路由分发:
// help doc: https://github.com/gsoft-inc/craco
module.exports = {
devServer: {
// 访问 /one/xxx 和 /another/xxx 会使用不同的入口html
historyApiFallback: {
disableDotRule: true,
historyApiFallback: {
disableDotRule: true,
rewrites: [
{ from: /^\/one/, to: '/index.html' },
{ from: /^\/another/, to: '/another-index.html' },
],
},
},
}