小宋爱睡觉 小宋爱睡觉
首页
  • HTML
  • CSS
  • JavaScript
  • Vue
  • React
  • 计算机网络
  • 浏览器原理
  • 性能优化
  • 设计模式
手写系列
  • 字符串
  • 数组
  • 链表
  • 树
  • 动态规划
  • 排序算法
  • GitHub (opens new window)
  • JueJin (opens new window)
首页
  • HTML
  • CSS
  • JavaScript
  • Vue
  • React
  • 计算机网络
  • 浏览器原理
  • 性能优化
  • 设计模式
手写系列
  • 字符串
  • 数组
  • 链表
  • 树
  • 动态规划
  • 排序算法
  • GitHub (opens new window)
  • JueJin (opens new window)
  • CDN
  • 其他优化手段
  • Webpack优化
    • 优化 Webpack 的构建速度
    • 优化 Webpack 的打包体积
    • Gzip优化
  • 性能优化
Crucials
2021-12-16

Webpack优化

# webpack优化

# 优化 Webpack 的构建速度

  • 使用高版本的 Webpack (使用webpack4+)

  • 多线程/多实例构建:HappyPack(不维护了)、thread-loader

  • 缩小打包作用域:

    • exclude/include (确定 loader 规则范围)

    • resolve.modules 指明第三方模块的绝对路径 (减少不必要的查找)

      resolve.extensions 尽可能减少后缀尝试的可能性

    • noParse 对完全不需要解析的库进行忽略 (不去解析但仍会打包到 bundle 中,注意被忽略掉的文件里不应该包含 import、require、define 等模块化语句)

    • IgnorePlugin (完全排除模块)

    • 合理使用alias

  • 充分利用缓存提升二次构建速度:

    • babel-loader 开启缓存
    • terser-webpack-plugin 开启缓存
    • 使用 cache-loader 或者 hard-source-webpack-plugin 注意:thread-loader 和 cache-loader 两个要一起使用的话,请先放 cache-loader 接着是 thread-loader 最后才是 heavy-loader
  • DLL:

    • 使用 DllPlugin 进行分包,使用 DllReferencePlugin(索引链接) 对 manifest.json 引用,让一些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间。

2)使用webpack4-优化原因

  • V8带来的优化(for of替代forEach、Map和Set替代Object、includes替代indexOf)
  • 默认使用更快的md4 hash算法
  • webpacks AST可以直接从loader传递给AST,减少解析时间
  • 使用字符串方法替代正则表达式

①noParse

  • 不去解析某个库内部的依赖关系
  • 比如jquery 这个库是独立的, 则不去解析这个库内部依赖的其他的东西
  • 在独立库的时候可以使用
module.exports = {
  module: {
    noParse: /jquery/,
    rules:[]
  }
}
1
2
3
4
5
6

②IgnorePlugin

  • 忽略掉某些内容 不去解析依赖库内部引用的某些内容
  • 从moment中引用 ./local 则忽略掉
  • 如果要用local的话 则必须在项目中必须手动引入
import 'moment/locale/zh-cn'
module.exports = {
    plugins: [
        new Webpack.IgnorePlugin(/./local/, /moment/),
    ]
}
1
2
3
4
5
6

③dillPlugin

  • 不会多次打包, 优化打包时间
  • 先把依赖的不变的库打包
  • 生成 manifest.json文件
  • 然后在webpack.config中引入
  • webpack.DllPlugin Webpack.DllReferencePlugin

④happypack -> thread-loader

  • 大项目的时候开启多线程打包
  • 影响前端发布速度的有两个方面,一个是构建,一个就是压缩,把这两个东西优化起来,可以减少很多发布的时间。

⑤thread-loader thread-loader 会将您的 loader 放置在一个 worker 池里面运行,以达到多线程构建。 把这个 loader 放置在其他 loader 之前(如下图 example 的位置), 放置在这个 loader 之后的 loader 就会在一个单独的 worker 池(worker pool)中运行。

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /.js$/,
        include: path.resolve("src"),
        use: [
          "thread-loader",
          // 你的高开销的loader放置在此 (e.g babel-loader)
        ]
      }
    ]
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

每个 worker 都是一个单独的有 600ms 限制的 node.js 进程。同时跨进程的数据交换也会被限制。请在高开销的loader中使用,否则效果不佳

⑥压缩加速——开启多线程压缩

  • 不推荐使用 webpack-paralle-uglify-plugin,项目基本处于没人维护的阶段,issue 没人处理,pr没人合并。 Webpack 4.0以前:uglifyjs-webpack-plugin,parallel参数
module.exports = {
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        parallel: true,
      }),
    ],
  },};
1
2
3
4
5
6
7
8
  • 推荐使用 terser-webpack-plugin
module.exports = {
  optimization: {
    minimizer: [new TerserPlugin(
      parallel: true   // 多线程
    )],
  },
};
1
2
3
4
5
6
7

# 优化 Webpack 的打包体积

  • 压缩代码
  • 提取页面公共资源:
  • Tree shaking
  • Scope hoisting
  • 图片压缩
  • 动态Polyfill

speed-measure-webpack-plugin 简称 SMP,分析出 Webpack 打包过程中 Loader 和 Plugin 的耗时,有助于找到构建过程中的性能瓶颈。 开发阶段

开启多核压缩 插件: terser-webpack-plugin

const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
    optimization: {
        minimizer: [
            new TerserPlugin({
                parallel: true,
                terserOptions: {
                    ecma: 6,
                },
            }),
        ]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# Gzip优化

这里不再赘述Gzip具体配置

具体讲讲gzip的压缩原理

  1. gzip的压缩算法

gzip中间的文件体,使用的是Deflate算法,这是一种无损压缩解压算法。Deflate是zip压缩文件的默认算法,7z,xz等其他的压缩文件中都有用到,实际上deflate只是一种压缩数据流的算法. 任何需要流式压缩的地方都可以用。

Deflate算法进行压缩时,一般先用 Lz77算法压缩,再使用 Huffman编码。

Lz77算法原理

如果文件中有两块内容相同的话,我们可以用两者之间的距离,相同内容的长度这样一对信息,来替换后一块内容。由于两者之间的距离,相同内容的长度这一对信息的大小,小于被替换内容的大小,所以文件得到了压缩。

举个例子:

http://www.`baidu`.com` `https://www.`taobao`.com
1

上面一段文本可以看到,前后有部分内容是相同的,我们可以用前文相同内容的距离和相同字符长度替换后文的内容。

http://www.`baidu`.com` `(21,12)`taobao`(23,4)
1

Deflate采用的Lz77算法是经过改进的版本,首先三个字节以上的重复串才进行偏码,否则不进行编码。

其次匹配查找的时候用了哈希表,一个head数组记录最近匹配的位置和prev链表来记录哈希值冲突的之前的匹配位置。

而Huffman编码,只大概了解是通过字符出现概率,将高频字符用较短字节进行表示从而达到字符串的压缩。(http2头信息压缩就用到了哈夫曼编码)

问题:但是为什么图片不适合用于gzip压缩

gzip是一个压缩程序而并不是一个算法,经过gzip压缩后文件格式为.gz,我们对.gz文件进行分析。

使用node的fs模块去读取一个gz压缩包可以看到如下一段Buffer内容:

const fs = require("fs");

fs.readFile("vds.gz", (err, data) => {
  console.log(data); // <Buffer 1f 8b 08 00 00 00 00 00 00 0a  ... >
});
1
2
3
4
5

通常gz压缩包有文件头,文件体和文件尾三个部分。

头尾专门用来存储一些文件相关信息,比如我们看到上面的Buffer数据,第一二个字节为1f 8b(16进制),通常第一二字节为1f 8b就可以初步判断这是一个gz压缩包,但是具体还是要看是否完全符合gz文件格式,第三个字节取值范围是0到8,目前只用8,表示使用的是Deflate压缩算法。还有一些比如修改时间,压缩执行的文件系统等信息也会在文件头。

而文件尾会标识出一些原始数据大小的相关信息,被压缩的数据则是放在中间的文件体。

而对于已经压缩过的图片使用gzip体积变大是因为中间的压缩体并没有怎么压缩,却添加了头尾的压缩相关信息

派生:brotli压缩

概念

与常见的通用压缩算法不同,Brotli使用一个预定义的120千字节字典。该字典包含超过13000个常用单词、短语和其他子字符串,这些来自一个文本和HTML文档的大型语料库。预定义的算法可以提升较小文件的压缩密度。使用brotli取代deflate来对文本文件压缩通常可以增加20%的压缩密度,而压缩与解压缩速度则大致不变。

目前该压缩方式大部分浏览器(包括移动端)新版本支持良好,详细的支持情况可在caniuse (opens new window)查询到。

支持Brotli压缩算法的浏览器使用的内容编码类型为br,例如以下是Chrome浏览器请求头里Accept-Encoding的值:

Accept-Encoding: gzip, deflate, sdch, br
1

如果服务端支持Brotli算法,则会返回以下的响应头:

Content-Encoding: br
1

Tips:brotli 压缩只能在 https 中生效,因为 在 http 请求中 request header 里的 Accept-Encoding: gzip, deflate 是没有 br 的。

上次更新: 2022/03/11, 10:10:02
其他优化手段

← 其他优化手段

Copyright © 2021-2025 粤ICP备2021165371号
  • 跟随系统
  • 浅色模式
  • 深色模式