create-react-app 修改为多入口编译的方法


Posted in Javascript onAugust 01, 2018

需求和出发点

我们会有较多的小的单页应用,主要是一些简单的页面和活动之类。这些页面相互之间没有交集,但是会有一些可以共用的代码,资源、接口、组件啥的。

对此,我们想到了两种解决方案:

  • react-router 路由方案;
  • 同一个项目的多入口编译;

针对我们的业务需求,其实 react-router 方案会有两个小问题:

  • 单个活动的修改,其实需要编译整个项目;
  • 若是不做编译优化,整个项目的包会比较大,但其实没必要,当然这个可以通过 react-router 的按需加载来解决;

权衡之下,我们还是选择了第二个方案——改造项目成为多入口编译。

文件结构设计

改进后,整个项目的结构大体如下:

- project
  - build
  - config
  - public
  - scripts
  - src
    - api
    - component
    - site
      - site1
        - index.html
        - index.js
        - ...
      - site2
        - index.html
        - index.js
        - ...
  - package.json

site 文件夹下的所有文件夹都是一个独立的项目,项目通用的代码、资源被抽离到更外层的文件夹内,如 api、component 等,文件夹内都会有自己的 index.html 和 index.js,这会作为该项目的 html 模板和入口文件。下面,我们看下是如何修改编译过程的。

修改入口和出口

编译需要指定编译的入口和输出的位置,在 create-react-app 本来生成的 code 中,只有单入口和单出口,但是其实 webpack 是支持多入口、多出口的。

入口修改

create-react-app 命令生成的 config 文件夹中,有个 paths.js 文件,这里面 export 了比较常用的路径。在这里,我对 src/site 文件夹内的文件夹进行了遍历,生成为对象。具体代码如下:

// all site paths
function allSitePath(source) {
 const { lstatSync, readdirSync } = fs
 const { join } = path
 const result = {}
 const isDirectory = source => lstatSync(source).isDirectory()
 readdirSync(source).map(name => {
  let path = join(resolveApp(source), name)
  if (isDirectory(path)) result[name] = path
 })
 return result
}

module.exports = {
 ...
 allSites: allSitePath('src/site'),
}

在 webpack.config.dev.js / webpack.config.prod.js 中找到 module.exports 的 entry 属性,将其修改为:

// 动态生成 entry
const entry = {}
Object.keys(paths.allSites).forEach(item => {
 entry[item] = [
  require.resolve('./polyfills'),
  require.resolve('react-dev-utils/webpackHotDevClient'),
  require.resolve('react-error-overlay'),
  paths.allSites[item]
 ]
})

module.exports = {
 ...
 entry: entry,
 ...
}

出口修改

出口的修改分为两部分,一部分是 module.exports 的 output,添加 name 以使静态资源区分不同项目:

module.exports = {
 ...
 output: {
  path: paths.appBuild,
  pathinfo: true,
  filename: 'static/js/[name].bundle.js',
  chunkFilename: 'static/js/[name].chunk.js',
  publicPath: publicPath,
  devtoolModuleFilenameTemplate: info =>
   path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
 },
 ...
}

另一部分是 plugin 的修改,webpack 中,每个 HTML 文件的输出,其实是一个 HtmlWebpackPlugin,我们需要添加多个 HtmlWebpackPlugin,以求生成多个 HTML:

// 动态生成 plugins
const plugins = []
Object.keys(paths.allSites).forEach(item => {
 plugins.push(new HtmlWebpackPlugin({
  inject: true,
  chunks: [item],
  template: `${paths.allSites[item]}/index.html`,
  filename: `${item}/index.html`,
 }))
})

module.exports = {
 ...
 plugins: [
  ...
 ].concat(plugins),  
 ...
}

修改 webpack Dev Server 配置

上述配置做完后,理论就可以打包出多入口的版本;但使用npm start启动后,发现无论输入/index.html还是/admin.html,好像都是和原来/index.html显示一样的内容。甚至输入显然不存在的/xxxx.html,也显示为/index.html的内容。

这里,我们还需要修改 /config/webpackDevServer.config.js,做一些额外配置。

const rewrites = []
Object.keys(paths.allSites).forEach(item => {
 rewrites.push({
  from: new RegExp(`^\\/${item}/`, 'i'),
  to: `/${item}/index.html`,
 })
})

...

module.exports = function(proxy, allowedHost) {
 return {
  ...
  historyApiFallback: {
   // Paths with dots should still use the history fallback.
   // See https://github.com/facebookincubator/create-react-app/issues/387.
   disableDotRule: true,
   // 指明哪些路径映射到哪个html
   rewrites: rewrites,
  },
  ...
 };
};

OK,到这里,整个改造就完成了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript 模拟用户单击事件
Dec 31 Javascript
浅析JQuery获取和设置Select选项的常用方法总结
Jul 04 Javascript
Jquery操作cookie记住用户名
Mar 29 Javascript
浅谈MVC+EF easyui dataGrid 动态加载分页表格
Nov 10 Javascript
Vue 2.0 服务端渲染入门介绍
Mar 29 Javascript
详解如何在Angular中快速定位DOM元素
May 17 Javascript
AngularJS实现单一页面内设置跳转路由的方法
Jun 28 Javascript
Vue.js中组件中的slot实例详解
Jul 17 Javascript
基于vue.js快速搭建图书管理平台
Oct 29 Javascript
vue 中的keep-alive实例代码
Jul 20 Javascript
node.js的Express服务器基本使用教程
Jan 09 Javascript
深度解读vue-resize的具体用法
Jul 08 Javascript
Vue项目全局配置页面缓存之按需读取缓存的实现详解
Aug 01 #Javascript
JavaScript执行环境及作用域链实例分析
Aug 01 #Javascript
Vue.js 利用v-for中的index值实现隔行变色
Aug 01 #Javascript
echarts设置图例颜色和地图底色的方法实例
Aug 01 #Javascript
看看“疫苗查询”小程序有温度的代码
Jul 31 #Javascript
Vue父子组件双向绑定传值的实现方法
Jul 31 #Javascript
react中实现搜索结果中关键词高亮显示
Jul 31 #Javascript
You might like
PHP 采集心得技巧
2009/05/15 PHP
Pain 全世界最小最简单的PHP模板引擎 (普通版)
2011/10/23 PHP
PHP输出缓存ob系列函数详解
2014/03/11 PHP
PHP编写daemon process 实例详解
2016/11/13 PHP
php实现商城购物车的思路和源码分析
2020/07/23 PHP
同一页面多个商品倒计时JS 基于面向对象的javascript
2012/02/16 Javascript
javascript 进阶篇1 正则表达式,cookie管理,userData
2012/03/14 Javascript
JS实现从网页顶部掉下弹出层效果的方法
2015/08/06 Javascript
基于jQuery 实现bootstrapValidator下的全局验证
2015/12/07 Javascript
AngularJS入门教程之 XMLHttpRequest实例讲解
2016/07/27 Javascript
js绘制购物车抛物线动画
2020/11/18 Javascript
全选复选框JavaScript编写小结(附代码)
2017/08/16 Javascript
Angular实现的简单查询天气预报功能示例
2017/12/27 Javascript
移动端吸顶fixbar的解决方案详解
2019/07/17 Javascript
JS+HTML实现自定义上传图片按钮并显示图片功能的方法分析
2020/02/12 Javascript
[55:11]完美世界DOTA2联赛PWL S2 SZ vs LBZS 第一场 11.26
2020/11/30 DOTA
[47:03]完美世界DOTA2联赛PWL S3 access vs LBZS 第一场 12.20
2020/12/23 DOTA
Python版微信红包分配算法
2015/05/04 Python
python通过伪装头部数据抵抗反爬虫的实例
2018/05/07 Python
Python数据结构之栈、队列及二叉树定义与用法浅析
2018/12/27 Python
解决PyCharm不运行脚本,而是运行单元测试的问题
2019/01/17 Python
python ChainMap 合并字典的实现步骤
2019/06/11 Python
python实现得到当前登录用户信息的方法
2019/06/21 Python
Python:合并两个numpy矩阵的实现
2019/12/02 Python
python 串口读取+存储+输出处理实例
2019/12/26 Python
PyCharm Anaconda配置PyQt5开发环境及创建项目的教程详解
2020/03/24 Python
Python设计密码强度校验程序
2020/07/30 Python
在HTML5 Canvas中放入图片和保存为图片的方法
2014/05/03 HTML / CSS
俄罗斯香水在线商店:AromaCode
2019/12/04 全球购物
枚举与#define宏的区别
2014/04/30 面试题
Java程序员常见面试题
2015/07/16 面试题
物业管理大学生个人的自我评价
2013/10/10 职场文书
精神文明单位申报材料
2014/05/02 职场文书
保证书格式
2015/01/16 职场文书
2015七夕情人节宣传语
2015/07/14 职场文书
纯html+css实现Element loading效果
2021/08/02 HTML / CSS