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 相关文章推荐
filters.revealTrans.Transition使用方法小结
Aug 19 Javascript
打印json对象的内容及JSON.stringify函数应用
Mar 29 Javascript
js中复制行和删除行的操作实例
Jun 25 Javascript
推荐9款炫酷的基于jquery的页面特效
Dec 07 Javascript
招聘网站基于jQuery实现自动刷新简历
May 10 Javascript
JS打字效果的动态菜单代码分享
Aug 21 Javascript
深入浅析JavaScript中with语句的理解
May 12 Javascript
JavaScript判断数组重复内容的两种方法(推荐)
Jun 06 Javascript
JSP基于Bootstrap分页显示实例解析
Jun 12 Javascript
Java框架SSH结合Easyui控件实现省市县三级联动示例解析
Jun 12 Javascript
深入浅出理解JavaScript高级定时器原理与用法
Aug 02 Javascript
微信小程序实现折线图的示例代码
Jun 07 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
Flash空降上海 化身大魔王接受挑战
2020/03/02 星际争霸
ThinkPHP快速入门实例教程之数据分页
2014/07/01 PHP
PHP定义字符串的四种方式详解
2018/02/06 PHP
PPK 谈 JavaScript 的 this 关键字 [翻译]
2009/09/29 Javascript
JS类的封装及实现代码
2009/12/02 Javascript
在IE 浏览器中使用 jquery的fadeIn() 效果 英文字符字体加粗
2011/06/02 Javascript
js正文内容高亮效果的实现方法
2013/06/30 Javascript
js兼容的placeholder属性详解
2013/08/18 Javascript
JS获得选取checkbox整行数据的方法
2015/01/28 Javascript
jquery实现鼠标拖拽滑动效果来选择数字的方法
2015/05/04 Javascript
jQuery同步提交示例代码
2015/12/12 Javascript
js剪切板应用clipboardData实例解析
2016/05/29 Javascript
js前端面试题及答案整理(一)
2016/08/26 Javascript
jQuery实现 RadioButton做必选校验功能
2017/06/15 jQuery
AngularJS实现的获取焦点及失去焦点时的表单验证功能示例
2017/10/25 Javascript
vue.js图片转Base64上传图片并预览的实现方法
2018/08/02 Javascript
layer.prompt输入层的例子
2019/09/24 Javascript
Javascript模拟实现new原理解析
2020/03/03 Javascript
Python中的exec、eval使用实例
2014/09/23 Python
Python实现将一个大文件按段落分隔为多个小文件的简单操作方法
2017/04/17 Python
获取django框架orm query执行的sql语句实现方法分析
2019/06/20 Python
Golang GBK转UTF-8的例子
2019/08/26 Python
Python笔试面试题小结
2019/09/07 Python
python框架flask表单实现详解
2019/11/04 Python
Django用户身份验证完成示例代码
2020/04/03 Python
香港No.1得奖零食网:香港零食大王
2016/07/22 全球购物
ASOS比利时:英国线上零售商及自有品牌
2018/07/29 全球购物
纽约香氛品牌:NEST Fragrance
2018/10/15 全球购物
Aeropostale官网:美国著名校园品牌及青少年服饰品牌
2019/03/21 全球购物
德国大型和小型家用电器网上商店:Energeto
2019/05/15 全球购物
介绍一下MYSQL常用的优化技巧
2012/10/25 面试题
分公司经理岗位职责
2013/11/11 职场文书
CSS 圆形进度栏
2021/04/06 HTML / CSS
CSS3 天气图标动画效果
2021/04/06 HTML / CSS
如何用JavaScipt测网速
2021/05/09 Javascript
使用javascript解析二维码的三种方式
2021/11/11 Javascript