blog icon indicating copy to clipboard operation
blog copied to clipboard

解决 roadhog 单页应用提取公共模块的问题

Open Runtu4378 opened this issue 7 years ago • 5 comments

前言 roadhog 在打包单页应用文件的时候如果采取异步加载模块文件的话,每个模块里面应用到的一些公用库(antd、moment等)会在每个生成的异步模块文件里面重复应用到,导致所有文件的总体积增加了很多(因为公用库被重复引用了)本文描述的是如何解决这个问题

相关仓库版本

  • roadhog: 1.1.1

问题描述

在 cpbs PC 端开发过程中,发现在经过 roadhog build --debug --analyze 命令打包之后的文件体积很大,具体内容如下所示:

Compiled successfully in 25.6s.

File sizes after gzip:

  1.56 MB    dist\index.js
  1.27 MB    dist\role.async.js
  1.26 MB    dist\privilegeAudit.async.js
  1.23 MB    dist\merchant.async.js
  1.23 MB    dist\agent.async.js
  1.23 MB    dist\merchantAudit.async.js
  1.23 MB    dist\agentAudit.async.js
  1.23 MB    dist\merchantReview.async.js
  1.23 MB    dist\agentReview.async.js
  1.22 MB    dist\user.async.js
  1.18 MB    dist\privilege.async.js
  1.15 MB    dist\transactionTotal.async.js
  1.14 MB    dist\statisticRemit.async.js
  1.14 MB    dist\statisticContrast.async.js
  1.14 MB    dist\transactionFlow.async.js
  239.48 KB  dist\login.async.js
  17.2 KB    dist\index.css
  3.93 KB    dist\error.async.js
  1.7 KB     dist\antd.js

Analyze result is generated at dist/stats.html.

roadhog 将每个 model 都打包为一个单独的异步调用的 js 文件,这些文件即使在 gzip 压缩之后每个都需要平均 1M 的体积,在查看打包的具体内容 (stats.html)之后,发现体积过大的这些包都对一些共同使用到的组件进行了打包,而非引用模块部分的代码体积只有 300k 左右:

模块打包文件示意图

经过分析,发现体积过大的原因有以下几点:

  • moment 模块被重复引用
  • moment 模块引用了多余的本地化文件(约300k)
  • antd 重复引用到的模块,如 TableButtonForm 等被各个模块重复打包,增加了体积

解决思路

  • 将体积大的公用的模块进行打包(momentantd部分使用频率高的模块)
  • 移除 moment 模块没有使用到的语言包

本文只讲述如何将公共模块抽离出来作为一个单独文件


解决过程

使用 CommonsChunkPlugin 将公用模块抽离到 ventor.js

修改 roadhog 的配置文件:

// .roadhogrc.js
  ...
  "entry": "./src/index.js",
  ...

在根目录添加 webpack.config.js,输入以下内容:

const webpack = require('webpack')

module.exports = function wp(webpackConfig, env) {
  // 对roadhog默认配置进行操作,比如:
  if (env === 'production') {
    // 上线环境使用分包打包方式  
    webpackConfig.entry = {
      index: './src/index.js',
      vendor: [
        'moment',
        'mockjs',
        'lodash',
        'react',
        'react-dom',
        'react-helmet',
      ],
    }
    // webpackConfig.plugins.push(new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/))
    webpackConfig.plugins.push(new webpack.optimize.CommonsChunkPlugin({
      name: ['vendor'],
      minChunks: Infinity,
    }))
  }

  return webpackConfig
}

至此,完成了将除 antd 之外的公用模块的抽离

使用 CommonsChunkPluginantd 的高频模块抽离到 antd.js

修改 webpack.config.js为:

const webpack = require('webpack')

module.exports = function wp(webpackConfig, env) {
  // 对roadhog默认配置进行操作,比如:
  if (env === 'production') {
    // 上线环境使用分包打包方式  
    webpackConfig.entry = {
      index: './src/index.js',
      vendor: [
        'moment',
        'mockjs',
        'lodash',
        'react',
        'react-dom',
        'react-helmet',
      ],
      antd: [
        'antd/lib/button',
        'antd/lib/icon',
        'antd/lib/table',
        'antd/lib/date-picker',
        'antd/lib/form',
        'antd/lib/modal',
        'antd/lib/grid',
        'antd/lib/input',
      ],
    }
    // webpackConfig.plugins.push(new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/))
    webpackConfig.plugins.push(new webpack.optimize.CommonsChunkPlugin({
      name: ['vendor', 'antd'],
      minChunks: Infinity,
    }))
  }

  return webpackConfig
}

修改 index.htmlindex.ejs

/src 目录下的文件进行如下调整:

|-index.ejs
|-index.js
|-index.less
|-router.js

各个文件的配置为:

<!-- index.ejs -->
<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
</head>
<body>
<div id="root"></div>
</body>
</html>
import 'babel-polyfill'
import dva from 'dva'
import createHistory from 'history/createBrowserHistory'
import createLoading from 'dva-loading'
import { message } from 'antd'
import moment from 'moment'
import 'moment/locale/zh-cn'
import './index.less'
import './styles/lib/animate.css'    // 引入全局样式动画库
import './themes/index.less'

moment.locale('zh-cn')
const ERROR_MSG_DURATION = 3 // 3 秒

// 1. Initialize
const app = dva({
  history: createHistory(),
  onError(error) {
    console.error(error)
    message.error(error.message, ERROR_MSG_DURATION)
  },
})

// 2. Plugins
app.use(createLoading({ effects: true }))

// 3. Model

// 4. Router
app.router(require('./router'))

// 5. Start
app.start('#root')

index.lessrouter.js 的内容这里不赘述

经过以上配置之后,index.ejs 能够根据 roadhog 里面的 entry 配置自动引入所有入口文件。

注意 CommonsChunkPlugin 配置里面的 name 的数组顺序会影响到 entry 的引用顺序,如果出现 webpack 未定义的错误,尝试改变这个属性值的顺序,因为 CommonsChunkPlugin 是按照抽离顺序将 webpack 的全局对象放到了 entry 的最后一个入口文件里面。


解决成果

经过上面的处理之后,打包的个文件体积为:

Compiled successfully in 21.1s.

File sizes after gzip:

  1.41 MB    dist\antd.js
  596.22 KB  dist\index.js
  556.53 KB  dist\role.async.js
  549.17 KB  dist\privilegeAudit.async.js
  526.47 KB  dist\merchant.async.js
  524.27 KB  dist\agent.async.js
  520.04 KB  dist\merchantAudit.async.js
  519.58 KB  dist\agentAudit.async.js
  516.8 KB   dist\merchantReview.async.js
  516.34 KB  dist\agentReview.async.js
  508.46 KB  dist\user.async.js
  474.88 KB  dist\privilege.async.js
  443.19 KB  dist\transactionTotal.async.js
  434.33 KB  dist\statisticRemit.async.js
  433.45 KB  dist\statisticContrast.async.js
  429.59 KB  dist\transactionFlow.async.js
  279.46 KB  dist\vendor.js
  112.09 KB  dist\login.async.js
  17.2 KB    dist\index.css
  3.92 KB    dist\error.async.js

Analyze result is generated at dist/stats.html.

可以看到被抽离了公用模块之后的各模块的体积基本减少到了 500k(具体抽离哪些模块还可以再精准研究),代码包的整体面积无疑是减少了很多的。


后续报道

发现在使用多入口方式的时候无法触发热更新,所以在开发环境之下还是使用旧有的打包方式,只在线上环境使用抽离式打包,解决方式就是在 webpack.config.js 里面添加一个环境变量判断,详细见上面代码。

const webpack = require('webpack')

module.exports = function wp(webpackConfig, env) {
  // 对roadhog默认配置进行操作,比如:
  if (env === 'production') {
    // ...do what you want
  }

  return webpackConfig
}

资料来源

roadhog - 支持 vendor 的配置 #370

Runtu4378 avatar Jul 03 '18 00:07 Runtu4378

@Runtu4378 @sorrycc你好,我按这个配置生成了vendor.js和antd.js,但是其它打包出来的文件大小并没有变化,这个是bug吗?

SmithXiong avatar Sep 01 '18 07:09 SmithXiong

@SmithXiong 有可复现的在线 demo 吗,这篇博文写时 roadhog 的版本是 1.1.1

2.x.x 版本的 roadhog 有内置 commons 的配置 你试下弃用 webpack.config.js 这个文件,结合下面的配置来查看有没有如愿提取到公用文件

https://github.com/sorrycc/roadhog#commons https://github.com/sorrycc/roadhog/issues/577

Runtu4378 avatar Sep 01 '18 07:09 Runtu4378

@Runtu4378 谢谢了,commons配置确实可以分离出来,原来打包的文件小了一些。

SmithXiong avatar Sep 01 '18 08:09 SmithXiong

@Runtu4378 谢谢了,commons配置确实可以分离出来,原来打包的文件小了一些。

请问你是怎么配置commons的,我也在研究这个问题,没整出来啊

CR7tiantian avatar Aug 30 '19 09:08 CR7tiantian