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 相关文章推荐
mouse_on_title.js
Aug 25 Javascript
js 弹出框只弹一次(二次修改之后的)
Nov 26 Javascript
express的中间件basicAuth详解
Dec 04 Javascript
node.js中的http.response.removeHeader方法使用说明
Dec 14 Javascript
javascript 应用小技巧方法汇总
Jul 05 Javascript
Vue异步组件使用详解
Apr 08 Javascript
基于JavaScript实现表格滚动分页
Nov 22 Javascript
webpack4.0 入门实践教程
Oct 08 Javascript
基于Vue组件化的日期联动选择器功能的实现代码
Nov 30 Javascript
javascript实现切割轮播效果
Nov 28 Javascript
微信小程序基于movable-view实现滑动删除效果
Jan 08 Javascript
详解ES6数组方法find()、findIndex()的总结
May 12 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下对数组进行排序的函数
2010/08/08 PHP
php中adodbzip类实例
2014/12/08 PHP
ThinkPHP中Widget扩展的两种写法及调用方法详解
2017/05/04 PHP
php使用curl伪造来源ip和refer的方法示例
2018/05/08 PHP
JQuery 实现的页面滚动时浮动窗口控件
2009/07/10 Javascript
无阻塞加载脚本分析[全]
2011/01/20 Javascript
javascript继承之为什么要继承
2012/11/10 Javascript
jQuery表单验证功能实例
2015/08/28 Javascript
JS实现仿FLASH效果的竖排导航代码
2015/09/15 Javascript
浅析JS异步加载进度条
2016/05/05 Javascript
js导出excel文件的简洁方法(推荐)
2016/11/02 Javascript
js实现瀑布流效果(自动生成新的内容)
2017/03/16 Javascript
jQuery插件HighCharts绘制简单2D折线图效果示例【附demo源码】
2017/03/21 jQuery
webpack 2.x配置reactjs基本开发环境详解
2017/08/08 Javascript
浅谈jquery fullpage 插件增加头部和版权的方法
2018/03/20 jQuery
angular inputNumber指令输入框只能输入数字的实现
2019/12/03 Javascript
nodejs如何在package.json中设置多条启动命令
2020/03/16 NodeJs
vue 路由meta 设置导航隐藏与显示功能的示例代码
2020/09/04 Javascript
[41:20]2014 DOTA2华西杯精英邀请赛 5 24 NewBee VS DK
2014/05/26 DOTA
python解析模块(ConfigParser)使用方法
2013/12/10 Python
python获取当前计算机cpu数量的方法
2015/04/18 Python
详解Python中的Cookie模块使用
2015/07/06 Python
python开发之基于thread线程搜索本地文件的方法
2015/11/11 Python
Python两个内置函数 locals 和globals(学习笔记)
2016/08/28 Python
解决Tensorflow使用pip安装后没有model目录的问题
2018/06/13 Python
opencv python 图像去噪的实现方法
2018/08/31 Python
centos7之Python3.74安装教程
2019/08/15 Python
python3的url编码和解码,自定义gbk、utf-8的例子
2019/08/22 Python
python日志通过不同的等级打印不同的颜色(示例代码)
2021/01/13 Python
纯CSS实现的大小渐变、渐远效果
2014/04/15 HTML / CSS
SmartBuyGlasses台湾:名牌眼镜,名牌太阳眼镜及隐形眼镜
2017/01/04 全球购物
购买英国原创艺术:Art Gallery
2018/08/25 全球购物
俄罗斯美容和健康网上商店:Созвездие Красоты
2019/07/23 全球购物
班干部竞选演讲稿
2014/04/24 职场文书
nginx实现发布静态资源的方法
2021/03/31 Servers
Go语言并发编程 sync.Once
2021/10/16 Golang