详解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都依赖他,并且我们设置的连接符是"~"那么,最终生成的就是 vendor~a~b.js

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

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

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

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

优先级关系

maxInitialRequest / maxAsyncRequests <maxSize <minSize。

cacheGroups 定制分割包的规则

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

minChunks

最少引入的次数

实践

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

 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的配置去掉,将会生成一个topic~home的包,我们配置了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次下载文件,太多可能会适得其反。大家可以根据自己的项目做不同的拆分方法,总而言之,就是为了让项目更完美的在线上运行,给用户更好的体验~

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • webpack4之SplitChunksPlugin使用指南

    写在前面 前面写了一篇有关webpack4的不完全升级指南以及在webpack3.x迁移的时候遇到的问题,有兴许可以看一下. 0. 参数介绍 先对参数有一个大概的认识,虽然撸了很多遍官方的更新文档,但是还是去参看了一下新的wbepack源码,下面是各种参数及含义: chunks: 表示显示块的范围,有三个可选值:initial(初始块).async(按需加载块).all(全部块),默认为all; minSize: 表示在压缩前的最小模块大小,默认为0: minChunks: 表示被引用次数,默认

  • 详解webpack4之splitchunksPlugin代码包分拆

    本文讲解的是最近在做的一个多页面项目,结合webpack4的splitChunks进行代码包分拆的过程 项目框架 项目有home和topic两个入口文件,主要包括: react.mobx.antd作为项目的基本框架, echarts和d3(画图)是项目中部分页面用到比较大的组件库 src下的工作的组件和代码 其他的非公共代码. 两个入口文件都用react-loadable做了异步加载 import Loadable from 'react-loadable'; ... const Loadabl

  • 详解Webpack4多页应用打包方案

    前言 学习了 webpack 之后,将自己的博客的前端进行重构,由于自己的博客是多页应用,所以研究了下多页应用的打包方案.在这里把最后配置的成果分享下,不足之处,请指正.(文字不多,全是代码,不是配置教程,所以没有特别详细的写,只是一个参考) 项目地址: https://github.com/Ray-daydayup/MPA-webpack 文件目录结构 项目目录结构 首先先看下我的项目的目录结构 myblog-webpack ├── dist // 打包输出文件夹 ├── src // 源代码

  • IOS 详解socket编程[oc]粘包、半包处理

    IOS 详解socket编程[oc]粘包.半包处理 在做socket编程时,如果是做tcp连接,那就不可避免的会遇到粘包与半包的问题,粘包就是多组数据被一并接收了,粘在了一起,无法做划分:半包就是有数据接收不完整,无法处理.要解决粘包.半包的问题,一般在设计数据(消息)格式时会约定好一个字段专门用于描述数据包的长度,这样就使数据有了边界,依靠这个边界,就能把每组数据划分出来,数据不完整时也能获知数据的缺失. (当然也可以把数据设计成定长数据,但这样不够灵活:或者用\n,\r这类字符作为数据划分依

  • 详解Go语言中关于包导入必学的 8 个知识点

    1. 单行导入与多行导入 在 Go 语言中,一个包可包含多个 .go 文件(这些文件必须得在同一级文件夹中),只要这些 .go 文件的头部都使用 package 关键字声明了同一个包. 导入包主要可分为两种方式: 单行导入 import "fmt" import "sync" 多行导入 import( "fmt" "sync" ) 如你所见,Go 语言中 导入的包,必须得用双引号包含,在这里吐槽一下. 2. 使用别名 在一些场

  • 一文详解Golang中net/http包的实现原理

    目录 前言 http包执行流程 http包源码分析 端口监听 请求解析 路由分配 响应处理 前言 Go语言自带的net/http包提供了HTTP客户端和服务端的实现,实现一个简单的http服务非常容易,其自带了一些列结构和方法来帮助开发者简化HTTP服务开发的相关流程,因此我们不需要依赖任何第三方组件就能构建并启动一个高并发的HTTP服务器,net/http包在编写web应用中有很重要的作用,这篇文章会学习如何用 net/http 自己编写实现一个 HTTP Server 并探究其实现原理,具体

  • 详解如何将本地JAR包添加到本地Maven仓库中

    详解如何将本地JAR包添加到本地Maven仓库中 有些时候,当我们需要在自己的maven工程中添加一个确定的jar而发现Maven中央仓库不存在时,但是我们自己手上有这些需要的jar包,那么我们可以自给自足,把这个jar包添加到我们的自己的maven仓库中,这样方便在maven工程中引用. 比如sqljdbc.jar在Maven中央仓库中找不到,但是我们的maven工程中确实需要这样的一个jar包,那么我们首先需要下载对应版本的sqljdbc.jar包到本地,然后按照以下的命令添加到本地仓库.(

  • python 详解turtle画爱心代码

    导语: 哈喽,在经历了过年相亲这一环节,成了是好事,不成也是多认识一个人,见见"世面",也可以"开拓"一下眼界,说不定遇到什么奇葩,以后跟朋友也有了茶余饭后的话题. 希望我们在这快餐时代里,都能遇到小火慢炖的粥~ 正文: 一直觉得turtle是个非常可爱的库,突发奇想,然后想试试传说中的土味表白:用python画一颗小爱心-- Google programming!启动! 确实有很多很多现成的代码,比如[1]: 画出来也很好看: 但左看右看,觉得背后的逻辑,比如fo

  • 详解IOS如何防止抓包

    目录 抓包原理 防止抓包 一.发起请求之前判断是否存在代理,存在代理就直接返回,请求失败. 二.我们可以在请求配置中清空代理,让请求不走代理 SSL Pinning(AFN+SSL Pinning)推荐 扩展 抓包原理 其实原理很是简单:一般抓包都是通过代理服务来冒充你的服务器,客户端真正交互的是这个假冒的代理服务,这个假冒的服务再和我们真正的服务交互,这个代理就是一个中间者 ,我们所有的数据都会通过这个中间者,所以我们的数据就会被抓取.HTTPS 也同样会被这个中间者伪造的证书来获取我们加密的

  • 详解R语言图像处理EBImage包

    目录 什么是EBImage 1. 图像读取与保存 2.色彩管理 3.图像处理 4.空间变换 5.形态运算 6.图像分割 本文摘自<Keras深度学习:入门.实战及进阶>第四章部分章节. 什么是EBImage EBImage是R的一个扩展包,提供了用于读取.写入.处理和分析图像的通用功能,非常容易上手.EBImage包在Bioconductor中,通过以下命令进行安装. install.packages("BiocManager") BiocManager::install(

  • Websocket协议详解及简单实例代码

    Websocket协议详解 关于websocket的协议是用来干嘛的,请参考其他文章. WebSocket关键词 HTML5协议,实时,全双工通信,长连接 WebSocket比传统Http的好处 客户端与服务端只建立一个TCP连接,可以使用更少的连接 WebSocket的服务端可以将数据推送到客户端,如实时将证券信息反馈到客户端(这个很关键),实时天气数据,比http请求响应模式更灵活 更轻量的协议头,减少数据传送量 数据帧格式 下图为手工打造的数据帧格式 /** * fin |masked |

随机推荐