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 相关文章推荐
!DOCTYPE声明对JavaScript的影响分析
Apr 12 Javascript
jQuery EasyUI API 中文文档 搜索框
Sep 29 Javascript
jquery星级插件、支持页面中多次使用
Mar 25 Javascript
判断字符串的长度(优化版)中文占两个字符
Oct 30 Javascript
使用命令对象代替switch语句的写法示例
Feb 28 Javascript
什么是JavaScript注入攻击?
Sep 14 Javascript
jQuery插件FusionCharts绘制的2D双面积图效果示例【附demo源码】
Apr 11 jQuery
关于jquery form表单序列化的注意事项详解
Aug 01 jQuery
微信小程序使用modal组件弹出对话框功能示例
Nov 29 Javascript
浅析node应用的timing-attack安全漏洞
Feb 28 Javascript
koa+mongoose实现简单增删改查接口的示例代码
May 13 Javascript
jquery+ajax实现异步上传文件显示进度条
Aug 17 jQuery
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
Google Voice 短信发送接口PHP开源版(2010.5更新)
2010/07/22 PHP
php用header函数实现301跳转代码实例
2013/11/25 PHP
使用PHP Socket 编程模拟Http post和get请求
2014/11/25 PHP
php实现根据IP地址获取其所在省市的方法
2015/04/30 PHP
PHP实现的最大正向匹配算法示例
2017/12/19 PHP
PHP通过文件路径获取文件名的实例代码
2018/10/14 PHP
jquery.pagination.js 无刷新分页实现步骤分享
2012/05/23 Javascript
分享精心挑选的12款优秀jQuery Ajax分页插件和教程
2012/08/09 Javascript
基于jquery自己写tab滑动门(通用版)
2012/10/30 Javascript
Jquery多选下拉列表插件jquery multiselect功能介绍及使用
2013/05/24 Javascript
正负小数点后两位浮点数实现原理及代码
2013/09/06 Javascript
jquery处理json对象
2014/11/03 Javascript
使用百度地图api实现根据地址查询经纬度
2014/12/11 Javascript
jQuery插件jPaginate实现无刷新分页
2015/05/04 Javascript
微信支付如何实现内置浏览器的H5页面支付
2015/09/25 Javascript
JS实现的简洁二级导航菜单雏形效果
2015/10/13 Javascript
详解Vue项目引入CreateJS的方法(亲测可用)
2019/05/30 Javascript
vue elementUI使用tabs与导航栏联动
2019/06/21 Javascript
5个你不知道的JavaScript字符串处理库(小结)
2020/06/01 Javascript
[16:21]教你分分钟做大人:圣堂刺客
2014/12/03 DOTA
二种python发送邮件实例讲解(python发邮件附件可以使用email模块实现)
2013/12/03 Python
Python使用pickle模块存储数据报错解决示例代码
2018/01/26 Python
Flask框架URL管理操作示例【基于@app.route】
2018/07/23 Python
python try except 捕获所有异常的实例
2018/10/18 Python
Python3+Appium实现多台移动设备操作的方法
2019/07/05 Python
使用PyTorch将文件夹下的图片分为训练集和验证集实例
2020/01/08 Python
python GUI库图形界面开发之PyQt5访问系统剪切板QClipboard类详细使用方法与实例
2020/02/27 Python
Python多线程实现支付模拟请求过程解析
2020/04/21 Python
pytorch快速搭建神经网络_Sequential操作
2020/06/17 Python
python selenium 获取接口数据的实现
2020/12/07 Python
能否解释一下XSS cookie盗窃是什么意思
2012/06/02 面试题
求职简历的自我评价
2014/01/31 职场文书
师德模范事迹材料
2014/06/03 职场文书
入党积极分子个人总结
2015/03/02 职场文书
2015法院个人工作总结范文
2015/05/25 职场文书
全家福照片寄语怎么写?
2019/04/02 职场文书