Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Runtu4378 opened this issue Jul 3, 2018 · 4 comments
Open

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

Runtu4378 opened this issue Jul 3, 2018 · 4 comments
Labels
fontend 前端相关 Webpack webpack 相关

Comments

@Runtu4378
Copy link
Owner

Runtu4378 commented Jul 3, 2018

前言
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 Runtu4378 added fontend 前端相关 Webpack webpack 相关 labels Jul 3, 2018
@SmithXiong
Copy link

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

@Runtu4378
Copy link
Owner Author

Runtu4378 commented Sep 1, 2018

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

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

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

@SmithXiong
Copy link

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

@CR7tiantian
Copy link

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fontend 前端相关 Webpack webpack 相关
Projects
None yet
Development

No branches or pull requests

3 participants