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

webpack4之splitchunksPlugin #674

Open
libin1991 opened this issue Dec 3, 2018 · 0 comments
Open

webpack4之splitchunksPlugin #674

libin1991 opened this issue Dec 3, 2018 · 0 comments
Labels

Comments

@libin1991
Copy link
Owner

转自webpack4之splitchunksPlugin拆拆拆--项目实践

本文讲解的是最近在做的一个多页面项目,结合webpack4的splitChunks进行代码包分拆的过程

项目框架


项目有home和topic两个入口文件,主要包括:

  • react、mobx、antd作为项目的基本框架,
  • echarts(画图)和d3(画图)是项目中部分页面用到比较大的组件库
  • src项目公共组件代码等
  • 其他的非公共代码。

两个入口文件都用react-loadable做了异步加载

import Loadable from 'react-loadable';
...
const LoadableLogin = Loadable({
  loader: () => import('../../common/components/login'),
  loading: Loading,
});
...

webpack部分配置相关如下:

module.exports = {
  ...
  mode: 'production',
  entry: { // 多入口
    home: './src/home',
    topic: './src/topic',
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath,
  },
  plugins: [
    new HtmlWebpackPlugin({ // home页面
      filename: 'home.html',
      template: './template.html',
    }),
    new HtmlWebpackPlugin({ // topic页面
      filename: 'topic.html',
      template: './template.html',
      inject: true,
    }),
  ],
  ...
}

splitChunks

chunks:

  • all: 不管文件是动态还是非动态载入,统一将文件分离。当页面首次载入会引入所有的包
  • async: 将异步加载的文件分离,首次一般不引入,到需要异步引入的组件才会引入。
  • initial:将异步和非异步的文件分离,如果一个文件被异步引入也被非异步引入,那它会被打包两次(注意和all区别),用于分离页面首次需要加载的包。

minSize: 文件最小打包体积,单位byte,默认30000

比如说某个项目下有三个入口文件,a.js和b.js和c.js都是100byte,当我们将minSize设置为301,那么webpack就会将他们打包成一个包,不会将他们拆分成为多个包。

比如说某个项目下有三个入口文件,a.js和b.js和c.js都是100byte,当我们将minSize设置为301,那么webpack就会将他们打包成一个包,不会将他们拆分成为多个包。

automaticNameDelimiter: 连接符

假设我们生成了一个公用文件名字叫vendor,a.js,和b.js都依赖他,并且我们设置的连接符是""那么,最终生成的就是 vendora~b.js

maxInitialRequests 入口点处的最大并行请求数,默认为3

如果我们设置为1,那么每个入口文件就只会打包成为一个文件

maxAsyncRequests 最大异步请求数量,默认5

如果我们设置为1,那么每个入口文件就只会打包成为一个文件

cacheGroups 定制分割包的规则

test可以配置正则和写入function作为打包规则。其他属性均可继承splitChunks,这里必须说一下priority,设置包的打包优先级,非常重要!(后面结合实践)

minChunks

最少引入的次数

优先级关系

maxInitialRequest / maxAsyncRequests <maxSize <minSize。

实践

我们以一个最简单的配置开始,将公共代码打包出来

   splitChunks: {
      chunks: 'all',   // initial、async和all
      minSize: 30000,   // 形成一个新代码块最小的体积
      maxAsyncRequests: 5,   // 按需加载时候最大的并行请求数
      maxInitialRequests: 3,   // 最大初始化请求数
      automaticNameDelimiter: '~',   // 打包分割符
      name: true,
      cacheGroups: {
        vendors: { // 打包两个页面的公共代码
          minChunks: 2, // 引入两次及以上被打包
          name: 'vendors', // 分离包的名字
          chunks: 'all'
        },
      }
    },

两个入口文件的公共代码被打包到vendor文件夹下面,包括echarts d3 amcharts等一些三方包和src下的公共代码。

这当然不是我们想要的结果!存在以下问题:

其实当我们进入网站,一般第一步都是进入一个登陆页面,需要的只是项目的基本框架代码,例如react,react-dom.antd等,我们可以用all(或者initial)将它们单独打包,作为首页必须载入的包

我们打包的公共包,首次加载页面的时候,只想把同步加载的加载进来,所以需要一个同步的Common包

像echarts,d3,以及一些src下面一些异步加载的包,将它们利用async将打包成一个独立异步加载包

我们修改cacheGroups为:

   cacheGroups: {
        vendors: { // 项目基本框架等
          chunks: 'all',
          test: /(react|react-dom|react-dom-router|babel-polyfill|mobx)/,
          priority: 100,
          name: 'vendors',
        },
        'async-commons': {  // 异步加载公共包、组件等
          chunks: 'async',
          minChunks: 2,
          name: 'async-commons',
          priority: 90,
        },
        commons: { // 其他同步加载公共包
          chunks: 'all',
          minChunks: 2,
          name: 'commons',
          priority: 80,
        },
      }


这次webpack帮我们打出来的包主要有:

async-common:是两个入口文件都异步加载的三方包和利用react-loader做的懒加载代码,有echarts,d3等

vendors: 包括react,react-dom,antd等

commons: 引用超过两次的同步代码

这里说两个需要注意的地方:

注意这里我们priority的设置,vendors>async-commons>commons,我们首先将react,react-dom等优先打包出来,然后再打包公共部分,如果将vendors的优先级设置小于两个Common的优先级,那么react,react-dom将会打包到common包,将不会再生成vendors包

如果我们这里将commons的配置去掉,将会生成一个topichome的包,我们配置了async-common提取出异步加载的公共包后,将会默认将同步加载的公共包打包生成以automaticNameDelimiter连接符‘’生成的名字'topic~home'包,内容其实和commons包内容完全一样

ok!按照我们的要求,这样首次页面加载只会引入vendors,commons包,而不会引入async-common包,还是挺棒的!追求更精致的我们,再认真想想,是不是还可以做一些更好的优化?

到目前为止我们打包文件的打包是这样的:

用gzip压缩后,最大的async-common包有391kb。公司说最近因为一些状况,布置到生产后速度慢的时候,有时候只能有20kb/s的下载速度==。。。。于是继续split!
分析一下:

async-common中包含了自己写的src组件和第三方组件

async-common中比较大是echarts,zrender(echarts引入)和d3,结合项目来说,只有部分页面我们需要echarts(d3同),所以我们可以考虑将d3和echarts这两个比较大的包提取出来,等到某个页面需要的时候,再让其异步加载,这样就大大减小了async-common的体积了。

修改

    cacheGroups: {
        vendors: { // 基本框架
          chunks: 'all',
          test: /(react|react-dom|react-dom-router|babel-polyfill|mobx)/,
          priority: 100,
          name: 'vendors',
        },
        d3Venodr: { // 将体积较大的d3单独提取包,指定页面需要的时候再异步加载
          test: /d3/,
          priority: 100, // 设置高于async-commons,避免打包到async-common中
          name: 'd3Venodr',
          chunks: 'async'
        },
        echartsVenodr: { // 异步加载echarts包
          test: /(echarts|zrender)/,
          priority: 100, // 高于async-commons优先级
          name: 'echartsVenodr',
          chunks: 'async'
        },
        'async-commons': { // 其余异步加载包
          chunks: 'async',
          minChunks: 2,
          name: 'async-commons',
          priority: 90,
        },
        commons: { // 其余同步加载包
          chunks: 'all',
          minChunks: 2,
          name: 'commons',
          priority: 80,
        },
      }


当然,每次修改后,需要在htmlWebpackPlugin中配置chunk需要的包

      plugins: [
        new HtmlWebpackPlugin({ // home页面
          filename: 'home.html',
          template: './template.html',
          chunks: ['vendors', 'commons', 'home'],
        }),
        new HtmlWebpackPlugin({ // topic页面
          filename: 'topic.html',
          template: './template.html',
          chunks: ['vendors', 'commons', 'topic'],
        }),
      ],

后期还做了其他的拆分和优化,大概最大的包保持在100k左右,当然也不建议拆的特别小,因为浏览器http1可能一次性支持6次下载文件,太多可能会适得其反。大家可以根据自己的项目做不同的拆分方法,总而言之,就是为了让项目更完美的在线上运行,给用户更好的体验~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant