2020年春节已过,本来打算回郑州,却因为新型冠状病毒感染肺炎的疫情公司推迟了上班的时间,我也推迟了去郑州的时间,在家多陪娃几天。以前都是在书房学习写博客,今天比较特殊,抱着电脑,在楼顶晒着太阳,陪着家人,写着博客。

前面的几篇文章主要告诉大家如何安装、配置webpack、webpack实现样式分离等,今天这篇文章主要跟大家分享如果webpack如何实现代码分片。

现在工程项目中,实现高性能应用的其中重要的一点就是让用户每次只加载必要的资源,优先级别不太高的资源采用延迟加载等技术渐进地进行加载获取。

Webpack 作为打包工具所特有的一项技术就是代码分片技术,通过这项技术我们可以把代码按照特定的形式进行拆分,使用按需加载资源,不必要全部加载下来。
代码分片可以有效降低首屏加载资源的大小,但是我们同时又面临着其他问题,比如如何对项目模块进行分片,分片后的资源如何进行管理等等。今天我们需要对这些问题进行分析解决。

### 利用入口划分代码

在Webpack中,配置参数中每个入口都将生成一个对应的资源文件,通过入口的配置我们可以进行一些简单有效的代码拆分。

对于项目中常常会引入一些第三方库和工具,这些一般不会改动的,可以把它们单独放在一个入口中,由该入口的资源不会经常更新,因此可以有效利用客户端缓存这些资源,让用户不必在每次请求页面时候都重新加载。

//webpack.config.js
entry: {
    index: './index.js',
    lib: ['lib-1', 'lib-2']
}

//index.html
<script src="dist/lib.js"></script>
<script src="dist/index.js"></script>

这种拆分方法主要适合于那些将接口绑定在全局对象上的库,因为业务代码中的模块无法直接引用库中的模块,二者属于不同的依赖树。

对于多页面应用来说,我们可以利用入口划分的方式拆分代码。比如,为每一个页面创建一个入口,并放入只涉及该页面的代码,同时再创建一个入口来包含所有公共模块,并使每个页面都进行加载。但是这样仍会带来公共模块与业务模块处于不同依赖树的问题。另外,很多时候不是所有的页面都需要这些公共模块。这就需要我们利用webpack专用的插件来解决这种问题了。

CommonsChunkPlugin

CommonsChunkPlugin是webpack4之前内部自带的插件,webpack4之后用的是SplitChunks。CommonsChunkPlugin主要是用来提取第三方库和公共模块,避免首屏加载的bundle文件或者按需加载的bundle文件体积过大,从而导致加载时间过长,是一把优化项目的利器。

优点:

  • 开发过程中减少了重复模块打包,可以提升开发速度;
  • 减小整体资源体积;
  • 合理分片后的代码可以更有效地利用客户端缓存。
    首页通过一个简单的例子也说明,假设我们当前项目中有a.js 和b.js 两个入口文件,并且都引入了react,下面是未使用CommonsChunkPlugin的配置
//webpack.config.js
module.exports = {
    entry: {
        a: './a.js',
        b: './b.js'
    },
    output: {
        filename: '[name].js'
    }
}

//a.js
import React from 'React'
... //省略

// b.js
import React from 'React'
... //省略

如果打包,从打包的资源体积可以看出,react被分别打包到a.js 和b.js中。

更改webpack.config.js,添加CommonsChunkPlugin配置

const webpack = require('webpack');
module.exports = {
    entry: {
        a: './a.js',
        b: './b.js'
    },
    output: {
        filename: '[name].js'
    },
    plugins: [
    new webpack.optimize.CommonsChunkPlugin({
    name: 'commons',
    filename: 'commons.js'
})
]
}

在配置文件的头部引入Webpack,接着使用其内部CommonsChunkPlugin函数创建了一个插件实例,并传入配置对象,配置参数可以理解为

  • name:用于指定公共chunk的名字
  • filename: 提取后的资源文件名
    打包后可以看到,产出的资源中多了一个commons.js,而a.js 和b.js文件的体积也减少了,这是由于把react及依赖的模块提到commons.js的原因。
    不过我们需要注意的是,我们需要在页面引入其他j s之前,先引入公用的commons.js文件。

在提取公共模块方面,CommonsChunkPlugin可以满足很多场景的需求,但是它也有一些欠缺的地方。
1)一个CommonsChunkPlugin只能提取一个vendor,假如我们想提取多个vendor则需要配置多个插件,这会增加很多重复的配置代码。

2)前面我们提到的manifest实际上会使浏览器多加载一个资源,这对于页面渲染速度是不友好的。

3)由于内部设计上的一些缺陷,CommonsChunkPlugin在提取公共模块的时候会破坏掉原有Chunk中模块的依赖关系,导致难以进行更多的优化。比如在异步Chunk的场景下CommonsChunkPlugin并不会按照我们的预期正常工作。

optimization.SplitChunks

optimization.SplitChunks(简称SplitChunks)是Webpack 4为了改进CommonsChunk-Plugin而重新设计和实现的代码分片特性。它不仅比CommonsChunkPlugin功能更加强大,还更简单易用。

配置文件web pack.config.js为:

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
  context: path.join(__dirname, './src'),
  entry: {
    index: './index.js'
  },
  output: {
    // path: path.join(__dirname, 'dist'),
    filename: 'index.js',
    publicPath: '/dist/'
  },
  mode: 'development',
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', {
          loader: 'css-loader',
          options: {
            modules: {
              localIdentName: '[path][name]__[local]--[hash:base64:5]',
            }
          }
        }]
      },
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true,
            presets: [
              [
                'env', {
                  modules: false
                }
              ]
            ]
          }
        }
      }
    ],
  }
}

需要引入的两个index.js文件和index2.js文件

// index
import index2 from  './index2.js';
import React from 'react'
document.write('index.js', React.version);

//index2
import React from 'react'
document.write('index2.js', React.version);

使用optimization.splitChunks替代了CommonsChunkPlugin,并指定了chunks的值为all,这个配置项的含义是,SplitChunks将会对所有的chunks生效(默认情况下,SplitChunks只对异步chunks生效,并且不需要配置)
打包结果如下图:

原本我们打包的结果应该是index.js,但是由于SplitChunks的存在,又生成了一个vendors~index.index.js,并且把react提取到了里面。
运行的效果如下图:

在使用CommonsChunkPlugin的时候,我们大多数时候是通过配置项将特定入口中的特定模块提取出来,也就是更贴近命令式的方式。而SplitChunks的不同之处在于我们只需要设置一些提取条件,如提取的模式、提取模块的体积等,当某些模块达到这些条件后就会自动被提取出来。SplitChunks的使用更像是声明式的。

SplitChunks默认情形下的提取条件:

  • 提取后的chunk可被共享或者来自node_modules目录。这一条很容易理解,被多次引用或处于node_modules中的模块更倾向于是通用模块,比较适合被提取出来。
  • 提取后的Javascript chunk体积大于30kB(压缩和gzip之前),CSS chunk体积大于50kB。这个也比较容易理解,如果提取后的资源体积太小,那么带来的优化效果也比较一般。
  • 在按需加载过程中,并行请求的资源最大值小于等于5。按需加载指的是,通过动态插入script标签的方式加载脚本。我们一般不希望同时加载过多的资源,因为每一个请求都要花费建立链接和释放链接的成本,因此提取的规则只在并行请求不多的时候生效。
  • 在首次加载时,并行请求的资源数最大值小于等于3。和上一条类似,只不过在页面首次加载时往往对性能的要求更高,因此这里的默认阈值也更低。

SplitChunks提取方式

SplitChunks 默认的提取方式是异步提取,当我们在chunks上配置参数为all的时候,不是异步资源也可以提取。

SplitChunks 配置

optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      minRemainingSize: 0,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 6,
      maxInitialRequests: 4,
      automaticNameDelimiter: '~',
      name: true,
      automaticNameMaxLength: 30,
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }

(1)匹配模式通过chunks我们可以配置SplitChunks的工作模式。它有3个可选值,分别为async(默认)、initial和all。async即只提取异步chunk,initial则只对入口chunk生效(如果配置了initial则上面异步的例子将失效),all则是两种模式同时开启。

(2)匹配条件minSize、minChunks、maxAsyncRequests、maxInitialRequests都属于匹配条件,

(3)命名配置项name默认为true,它意味着SplitChunks可以根据cacheGroups和作用范围自动为新生成的chunk命名,并以automaticNameDelimiter分隔。如vendors~a~b~c.js意思是cacheGroups为vendors,并且该chunk是由a、b、c三个入口chunk所产生的。

(4)cacheGroups可以理解成分离chunks时的规则。默认情况下有两种规则——defaultVendors和default。defaultVendors用于提取所有node_modules中符合条件的模块,default则作用于被多次引用的模块。我们可以对这些规则进行增加或者修改,如果想要禁用某种规则,也可以直接将其置为false。当一个模块同时符合多个cacheGroups时,则根据其中的priority配置项确定优先级。

总结

有关webpack实现代码分片的几种方法:合理地规划入口,使用Commons-ChunkPlugin或SplitChunks就暂时分享到这里,这仅代表个人的观点,如想了解更多请扫描二维码


最后友情提醒大家要戴口罩,勤洗手,尽量减少外出,做好预防 措施,远离新型冠状病毒。

Webpack实战(八):教你搞懂webpack如果实现代码分片(code splitting)的更多相关文章

  1. (20/24) webpack实战技巧:watch实现热打包和添加代码备注

    在前面的学习中,我们一直使用webpack-dev-server充当(本地)服务器和完成打包任务,但是当出项目团队联合开发,共同使用一个服务器时,这时候我们需要实时进行打包以确保团队间能进行联调或者进 ...

  2. 一篇文章教你搞懂日志采集利器 Filebeat

    关注「开源Linux」,选择"设为星标" 回复「学习」,有我为您特别筛选的学习资料~ 本文使用的Filebeat是7.7.0的版本,文章将从如下几个方面说明: Filebeat是什 ...

  3. 五个小例子教你搞懂 JavaScript 作用域问题

    众所周知,JavaScript 的作用域和其他传统语言(类C)差别比较大,掌握并熟练运用JavaScript 的作用域知识,不仅有利于我们阅读理解别人的代码,也有助于我们编写自己的可靠代码. 下面笔者 ...

  4. 硬核!八张图搞懂 Flink 端到端精准一次处理语义 Exactly-once(深入原理,建议收藏)

    Flink 在 Flink 中需要端到端精准一次处理的位置有三个: Source 端:数据从上一阶段进入到 Flink 时,需要保证消息精准一次消费. Flink 内部端:这个我们已经了解,利用 Ch ...

  5. 教你搞懂Jenkins安装部署!

    前言:请各大网友尊重本人原创知识分享,谨记本人博客:南国以南i Jenkins介绍 Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用 ...

  6. webpack Code Splitting浅析

    Code Splitting是webpack的一个重要特性,他允许你将代码打包生成多个bundle.对多页应用来说,它是必须的,因为必须要配置多个入口生成多个bundle:对于单页应用来说,如果只打包 ...

  7. 30分钟手把手教你学webpack实战

    30分钟手把手教你学webpack实战 阅读目录 一:什么是webpack? 他有什么优点? 二:如何安装和配置 三:理解webpack加载器 四:理解less-loader加载器的使用 五:理解ba ...

  8. 每天记录一点:NetCore获得配置文件 appsettings.json vue-router页面传值及接收值 详解webpack + vue + node 打造单页面(入门篇) 30分钟手把手教你学webpack实战 vue.js+webpack模块管理及组件开发

    每天记录一点:NetCore获得配置文件 appsettings.json   用NetCore做项目如果用EF  ORM在网上有很多的配置连接字符串,读取以及使用方法 由于很多朋友用的其他ORM如S ...

  9. Webpack实战(五):轻松读懂Webpack如何分离样式文件

    在上一篇文章中我给大家分享了预处理器(loader),里面讲到了style-loader 和css-loader,有关样式引入的问题,但是上面的样式文件只是引入到style标签里面,并不是我想要的样式 ...

随机推荐

  1. eclipse git导入的项目 让修改后的文件带有黑色星标记样式

    操作方式:Window——>Preferences——>Team——>Git——>Label Decorations——>Icon Decorations 将 Dirty ...

  2. PSP第一次总结

    项目计划总结 周活动总结表 姓名:王金萱                                                                                 ...

  3. typescript step by step interface class

  4. 双指针,BFS和图论(二)

    (一)BFS 1.地牢大师 你现在被困在一个三维地牢中,需要找到最快脱离的出路! 地牢由若干个单位立方体组成,其中部分不含岩石障碍可以直接通过,部分包含岩石障碍无法通过. 向北,向南,向东,向西,向上 ...

  5. Java容器解析系列(14) IdentityHashMap详解

    IdentityHashMap,使用什么的跟HashMap相同,主要不同点在于: 数据结构:使用一个数组table来存储 key:value,table[2k] 为key, table[2k + 1] ...

  6. python的数据类型之列表list

    列表是最常用的Python数据类型,它可以作为一个方括号内的逗号分隔值出现.例如: stus = ["zhangsan","lisi","wangwu ...

  7. kivy file import

    from kivy.app import Appfrom kivy.uix.boxlayout import BoxLayoutfrom kivy.properties import ObjectPr ...

  8. chrome浏览器无法开启同步功能 request cancel

    解决办法 添加代理规则*.googleapis.com

  9. 暑假第五周总结(学习HBASE相关知识)

    本周主要对HBASE的相关知识进行了学习,主要是通过视频的讲解了解到了HBASE的存储机制,HBASE的机制与普通的关系型数据库完全不同,HBASE以列进行存储,其主要执行的就是增删查操作,其更改主要 ...

  10. 快乐编程大本营【java语言训练班】 6课:用java的对象和类编程

    快乐编程大本营[java语言训练班] 6课:用java的对象和类编程 第1节. 什么是对象和类 第2节. 对象的属性和方法 第3节. 类的继承 第4节. 使用举例:创建类,定义方法,定义属性 第5节. ...