使用 create-react-app 配置 react 多页面应用
本文参考了修改create-react-app支持多入口这篇文章,并且采用了相同原理和步骤,但是通过函数生成配置,减少了代码冗余,便于后期添加入口。
新建creat-react-app
安装并执行命令1
2
3npm install -g create-react-app
create-react-app my-app
cd my-app
获取webpack配置供后续修改
这一步骤是不可逆的
1 | npm run eject |
修改资源(src)结构
原来的资源是全部放在一个文件夹下的1
2
3
4
5src
|-- App.css
|-- App.js
|-- index.js
|-- …
加上自己需要的多个入口,可以按照自己喜欢的方式组织。
按照我的组织方式,增加了两个入口文件夹,分别是key1和key2,所以真正的入口路径(entry
)分别是/src/key1/index.js
和/src/key2/index.js
。1
2
3
4
5
6
7
8src
|-- key1
|-- index.js
|-- …
|-- key2
|-- index.js
|-- …
|-- …
修改paths.js
这个js定义了各种资源所在的路径,原来的入口appIndexJs
是一个字符串路径,代码如下:1
2
3
4
5// old
module.exports = {
// ...
appIndexJs: resolveApp('src/index.js')
};
改成数组:(这里只使用各个入口的id,后续的路由加工会在其他文件进行)1
2
3
4
5
6
7
8module.exports = {
// ...
// appIndexJs: resolveApp('src/index.js'),
appArr: [
'key1',
'key2'
],
};
修改entry和output参数
在webpack.config.dev.js
和webpack.config.prod.js
配置中都有这两个参数,均需要修改。
原来的代码:1
2
3
4
5
6
7
8entry: [
require.resolve('./polyfills'),
require.resolve('react-dev-utils/webpackHotDevClient'),
paths.appIndexJs, //数组最后一个值是真正的入口
],
output: {
filename: 'static/js/bundle.js',
},
改为:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19/*生成entry对象的方法*/
const generatedEntry = (()=>{
let entryObj = {};
paths.appArr.map(app=>{
entryObj[app] = [ // 注意根据原有的配置修改相关参数
require.resolve('./polyfills'), // copy 原入口数组
require.resolve('react-dev-utils/webpackHotDevClient'), // copy 原入口数组
`${paths.appSrc}/${app}/index.js`, // 相应key的路径,需要跟你的文件架构保持一致
]
});
return entryObj;
})();
// ...
entry: generatedEntry,
output: {
filename: 'static/js/[name].bundle.js', //生成时,此处的name会分别填充为上一步中的key1, key2
},
修改HtmlWebpackPlugin
这个插件的作用是生成html并注入<script>
和<style>
同上,在webpack.config.dev.js
和webpack.config.prod.js
都需要修改,原理也大同小异。
依然以webpack.config.dev.js
为例。
原代码如下:1
2
3
4
5
6
7plugins: [
// ...
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
}),
]
根据多入口生成多个HtmlWebpackPlugin
实例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21const generatedHTML = (()=>{
let htmlArr = [];
paths.appArr.map(app=>{
htmlArr.push(
new HtmlWebpackPlugin({ // 注意根据原有的配置修改相关参数
inject: true, // 跟原有数组的配置保持一致
template: paths.appHtml, // 这里用的是同一个模板,可以让不同html使用不同模板
chunks: [app], // 必要,指明注入的 script 对应的是哪个 entry
filename: `${app}.html`, // 必要,重命名 html
})
)
});
return htmlArr;
})();
// ...
plugins: [
// ...
...generatedHTML,
]
修改Webpack Dev Server配置
需要修改的原因引用了开头的参考文章(原文中的参考链接已失效,下面的引用更新了链接)——
上述配置做完后,理论就可以打包出多入口的版本;但使用npm start启动后,发现无论输入/index.html还是/admin.html,好像都是和原来/index.html显示一样的内容。甚至输入显然不存在的/xxxx.html,也显示为/index.html的内容。
这种现象,初步判断是HTTP服务器把所有请求重定向到了/index.html。对于单页应用,这种做法是没有问题的(本来就一个页面);但我们新增的/admin.html就可以访问了。
参考官方文档The historyApiFallback option,发现是webpack dev server的问题,还要额外做一些配置,需修改/config/webpackDevServer.config.js。
webpackDevServer.config.js
中的原代码如下:1
2
3historyApiFallback: {
disableDotRule: true,
},
修改后:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17const rewritesPath = (function(){
let pathArr = [];
paths.appArr.map(app=>{
pathArr.push(
{from: new RegExp('^\/' + app), to: `build/${app}.html`}
// 生成示例:{from: /^\/key1/, to: 'build/key1.html'}
)
});
return pathArr;
})();
// ...
historyApiFallback: {
disableDotRule: true,
rewrites: rewritesPath, //在本地server重定向的映射表
},
捉虫
终于改好了,执行一下!
1 | yarn start |
竟然报错(╯‵□′)╯︵┻━┻1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18path.js:7
throw new TypeError('Path must be a string. Received ' + inspect(path));
^
TypeError: Path must be a string. Received undefined
at assertPath (path.js:7:11)
at Object.dirname (path.js:1324:5)
at checkRequiredFiles (/Users/xinxin/v3/node_modules/react-dev-utils/checkRequiredFiles.js:23:24)
at Object.<anonymous> (/Users/xinxin/v3/scripts/start.js:38:6)
at Module._compile (module.js:573:32)
at Object.Module._extensions..js (module.js:582:10)
at Module.load (module.js:490:32)
at tryModuleLoad (module.js:449:12)
at Function.Module._load (module.js:441:3)
at Module.runMain (module.js:607:10)
at run (bootstrap_node.js:382:7)
at startup (bootstrap_node.js:137:9)
at bootstrap_node.js:497:3
一开始还以为是生成的entry有问题,查了半天。
后来发现是由于我注释了paths.appIndexJs
,而start.js
和build.js
都引用到了它用来防止文件漏缺。
(下次注释前一定要先在全局搜索一遍!)
原代码如下:1
2
3
4// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
都改成:1
2
3
4// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, ...paths.appArrIndexJs])) {
process.exit(1);
}
并在paths.js
中做如下修改:1
2
3
4
5
6
7
8
9
10const appArr = [
'key1',
'key2',
//后续在此处添加新的入口即可
];
module.exports = {
// ...
appArr,
appArrIndexJs: Array.from(appArr, app => resolveApp(`src/${app}/index.js`)), // 获取所有入口文件path
};
后续
- 注意也跑一遍
yarn build
以验证webpack.config.prod.js
脚本修改无误。 - 后续增加入口只要修改
paths.js
中的appArr
即可
本文完成与2018年1月,请参考者注意时效性。