Webpack-Basic
文章目录

Webpack-Basic

webpack 与 grunt、gulp 的不同?

Webpack: 基于路口构建, 递归解析路口所需要加载的所有文件然后通过 loader 或 plugin 进行处理
其他两者: 基于流构建, 找到一个或一类文件, 进行一系列链式操作并更新数据

Loader 和 plugin 的不同

  • Loader
    • 这个是加载器, 这个是实际处理文件内容的
    • 这个我们通过在 module.rules 里面配置, 可以作为模块的解析规则而存在, 并且我会通过 options 来判断对特定的文件, 使用特定的加载器
  • Plugin
    • 这个是整个工具的插件, 这个可以对整个工具进行扩展, 可以对生命周期进行扩展, 可以在合适的时候
    • 就我们在 plugins 这个字段下面单独配置

常用的 Loader

  • ES6
    • babel-loader
  • 文件加载(一般是图片的需求)
    • file-loader: 用于将图片转为连接
    • url-loader:对小图片直接 Base64 编码,对大图片通过 file-loader 进行处理
    • image-webpack-loader: 对各种图片进行压缩
  • CSS
    • style-loader:负责将 css 自动添加到 html 文件中
    • css-loader:负责处理对 css 文件的依赖
  • source-map-loader:加载额外的 Source Map 文件,以方便断点调试

常用的 Plugin

  • commons-chunk-plugin:提取公共代码
  • uglifyjs-webpack-plugin:通过 UglifyES 压缩 ES6 代码
  • define-plugin:定义环境变量
  • webpack-parallel-uglify-plugin: 多核压缩,提高压缩速度

分别介绍 bundle,chunk,module 是什么

  • bundle:是由 webpack 打包出来的文件
  • chunk:代码块,一个 chunk 由多个模块组合而成,用于代码的合并和分割
  • module:是开发中的单个模块,在 webpack 的世界,一切皆模块,一个模块对应一个文件,webpack 会从配置的 entry 中递归开始找出所有依赖的模块

Webpack 的详细流程

  1. 初始化参数
    • 从配置文件和 Shell 语句中读取与合并参数, 得出最终的参数;
  2. 开始编译
    • 用上一步得到的参数初始化 Compiler 对象, 加载所有配置的插件, 执行对象的 run 方法开始执行编译;
  3. 确定入口
    • 根据配置中的 entry 找出所有的入口文件;
  4. 编译模块
    • 从入口文件出发, 调用所有配置的 Loader 对模块进行翻译, 再找出该模块依赖的模块, 再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
  5. 完成模块编译
    • 在经过第 4 步使用 Loader 翻译完所有模块后, 得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
  6. 输出资源
    • 根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;7. 输出完成
    • 在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

如何利用 webpack 来优化前端性能?

  • 提取 runtime
    • 设置 webpack-optimization-runtimechunk
  • 压缩代码
    • UglifyJS 插件
    • 一些插件会提供 minify 的属性, 比如 htmlWebpackPlugin 这个插件, 然后可以在 minify 里面定义删除注释或者清空空行
  • 提取公共代码, 常见的操作是将三方库提取出来
    • 三方库分为动态依赖与静态依赖的(就是本地的和远程的区别)
    • webpack4 会默认将动态依赖的三方库分离出来
    • 额外设置 webpack-cacheGroup 会将静态三方库分离出来
    • 如此分离的目的是为了方便缓存
  • 多线程加速
    • webpack-parallel-uglify-plugin 插件可以实现多线程加速, 相比 Uglify 仅仅使用单线程的情况会快很多
  • 预编译
  • Scope Hoisting
    • Webpack 4 已经不需要配置了 mode = production 的时候默认开启
    • 变量作用与提升以解除一些闭包带来的性能问题
  • Tree Shaking
    • 类似于抖动一棵树, 会将打包过程中所有没有引用到的部分给剔除掉。
    • Webpack 对于 ES6 代码默认开启这个功能

如何提高 webpack 的构建速度?

  1. 多入口情况下, 使用 CommonsChunkPlugin 来提取公共代码
  2. 通过 externals 配置来提取常用库
  3. 利用 DllPluginDllReferencePlugin 预编译资源模块 通过 DllPlugin 来对那些我们引用但是绝对不会修改的 npm 包来进行预编译, 再通过 DllReferencePlugin 将预编译的模块加载进来.
  4. 使用 Happypack 实现多线程加速编译
  5. 使用 webpack-uglify-parallel 来提升 uglifyPlugin 的压缩速度. 原理上 webpack-uglify-parallel 采用了多核并行压缩来提升压缩速度
  6. 使用 Tree-shakingScope Hoisting 来剔除多余代码

Webpack 多入口 * 多出口

1
2
3
4
5
6
7
8
9
10
11
entry:{
    entry:'./src/entry.js',
    //这里我们又引入了一个入口文件
    entry2:'./src/entry2.js'
},
 //出口文件的配置项
output:{
    //输出的路径,用了 Node 语法
    path:path.resolve(__dirname,'dist'),
    //输出的文件名称, 正因为有两个入口,所以就会输出两个文件。filename:'[name].js'
},

单入口打包成多文件性能提升案例

我们用的 V4

分割的目的:

  1. 减少代码重复
  2. 支持缓存

Webpack4 默认的分割配置

  1. 异步加载的 module
  2. 被引用次数大于等于 2 的 module

默认分割的弊端

以上在默认配置下生成的 bundle 有以下缺点:

  1. webpack 本身的 runtime 代码没有分离出来, 这部分代码分离出来后可以

    • 这一点设置 optimization 下方的 runtimeChunk 即可解决
    1
    2
    3
    4
        optimization: {
            runtimeChunk: {
                "name": "manifest"
            },
  2. 默认输出的 main.fa1c3667353182bc989d.js 既包含了第三方库,又包含了我们自己的代码

    • Webpack 默认会将异步加载的三方库进行分割, 手动设置 splitChunk 即可解决:
    1
    2
    3
        splitChunks: {
            chunks:'all'
        }
    • 最终会生成一个 vendor~main.*.js 文件
  3. B-module 和 C-module 同时依赖了 D-module,但是 D-module 在 B-module 和 C-module 中重复存在

    • 设置 cacheGroups
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        cacheGroups: {
            common: {
                minChunks: 2,
                name:'commons',
                chunks: 'async',
                priority: 10,
                reuseExistingChunk: true,
                enforce: true
            }
        }
  4. webpack 默认通过数字编号给 module 起名,如果 module 发生增减,会导致包含第三方库在内的 bundle 都会发生改变

    • 加入 HashedModuleIdsPlugin 插件即可

根据上述修改后会输出以下目录:

1
2
3
4
5
6
7
8
9
10
11
distribution/
├── async-b.e367719620c9ca8ce61f.js // 异步加载
├── async-c.ed6ccbf81ee739e1680d.js
├── bundle-analyzer-report.html
├── commons.69d2ac742d5253325cb1.js
├── index.html
├── main.d144c2daa62efff6dcc9.js
├── manifest.20a9103ae59dd6003f45.js
├── vendors~async-lodash.d810246d214be5df29ec.js
├── vendors~async-underscore.7ca24e0a09ca4a78e339.js
└── vendors~main.5016e1c2cb426d8b4eef.js