Webpack知识体系| 青训营笔记

73 阅读6分钟

这是我参与「第四届青训营 」笔记创作活动的第8天

Webpack知识体系

什么是Webpack?

Webpack是基于模块化打包的⼯具: ⾃动化处理模块,webpack把⼀切当成模块,当 webpack 处理应⽤程序时,它会递归地构建⼀个依赖关系图 (dependency graph),其中包含应⽤程序需要的每个模块,然后将所有这些模块打包成⼀个或多个 bundle。

核心流程

  1. 入口处理

  2. 依赖解析

  3. 资源解析

  4. 资源合并打包

module.exports = {
  // 入口
  entry: "",
  // 输出
  output: {},
  // 加载器
  module: {
    rules: [],
  },
  // 插件
  plugins: [],
  // 模式
  mode: "",
};

image.png

Loader相关

Loader直译为"加载器"。Webpack将⼀切⽂件视为模块,但是webpack原⽣是只能解析js⽂件,如果想将其他⽂件也打包的话,就会⽤到 loader 。 所以Loader的作⽤是让webpack拥有了加载和解析⾮JavaScript⽂件的能⼒。

Loader在 module.rules 中配置,也就是说他作为模块的解析规则⽽存在。 类型为数组,每⼀项都是⼀个 Object ,⾥⾯描述了对于什么类型的⽂件( test ),使⽤什么加载( loader )和使⽤的参数( options ),运行在打包文件之前

有哪些常⻅的Loader

  • file-loader:把⽂件输出到⼀个⽂件夹中,在代码中通过相对 URL 去引⽤输出的⽂件
  • url-loader:和 file-loader 类似,但是能在⽂件很⼩的情况下以 base64 的⽅式把⽂件内容注⼊到代码中去
  • source-map-loader:加载额外的 Source Map ⽂件,以⽅便断点调试
  • image-loader:加载并且压缩图⽚⽂件
  • babel-loader:把 ES6 转换成 ES5
  • css-loader:允许在js中import一个css文件,会将css文件当成一个模块引入到js文件中
  • style-loader:能够在需要载入的html中创建一个<style></style>标签,标签里的内容就是CSS内容。
  • eslint-loader:通过 ESLint 检查 JavaScript 代码

注意: 在Webpack中,loader的执行顺序是从右向左执行的。因为webpack选择了compose这样的函数式编程方式,这种方式的表达式执行是从右向左的。

Plugin

Plugin直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运⾏的⽣命周期中会⼴播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

Plugin在 plugins 中单独配置。 类型为数组,每⼀项是⼀个 plugin 的实例,参数都通过构造函数传⼊。 plugins 在整个编译周期都起作用.

有哪些常⻅的Plugin

  • define-plugin:定义环境变量
  • html-webpack-plugin:简化html⽂件创建
  • uglifyjs-webpack-plugin:通过 UglifyES 压缩 ES6 代码
  • webpack-parallel-uglify-plugin: 多核压缩,提⾼压缩速度
  • webpack-bundle-analyzer: 可视化webpack输出⽂件的体积
  • mini-css-extract-plugin: CSS提取到单独的⽂件中,⽀持按需加载

热更新

模块热替换(HMR - Hot Module Replacement)功能会在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面。主要是通过以下几种方式,来显著加快开发速度:

  • 保留在完全重新加载页面时丢失的应用程序状态。
  • 只更新变更内容,以节省宝贵的开发时间。
  • 调整样式更加快速 - 几乎相当于在浏览器调试器中更改样式。

基本配置

module.exports = {
  // 其他省略
  devServer: {
    host: "localhost", // 启动服务器域名
    port: "3000", // 启动服务器端口号
    open: true, // 是否自动打开浏览器
    hot: true, // 开启HMR功能(只能用于开发环境,生产环境不需要了)
  },
};

原理:

  • 当修改了一个或多个文件;
  • 文件系统接收更改并通知 webpack
  • webpack 重新编译构建一个或多个模块,并通知 HMR 服务器进行更新;
  • HMR Server 使用 webSocket 通知 HMR runtime 需要更新,HMR 运行时通过 HTTP 请求更新 jsonp
  • HMR 运行时替换更新中的模块,如果确定这些模块无法更新,则触发整个页面刷新 image.png

TreeShaking

作用: 它表示在打包的时候会去除一些无用的代码

在 Webpack 中,启动 Tree Shaking 功能必须同时满足三个条件:

  • 使用 ESM 规范编写模块代码

  • 配置 optimization.usedExportstrue,启动标记功能

  • 启动代码优化功能,可以通过如下方式实现:

    • 配置 mode = production
    • 配置 optimization.minimize = true
    • 提供 optimization.minimizer 数组

特点:

  • 生产模式下它是默认开启的,但是由于经过babel编译全部模块被封装成IIFE,它存在副作用无法被tree-shaking
  • 可以在package.json中配置sideEffects指定哪些文件是有副作用的。它有两种值,一个是布尔类型,如果是false则表示所有文件都没有副作用;如果是一个数组的话,数组里的文件路径表示改文件有副作用
  • rollupwebpack中对tree-shaking的层度不同,例如对babel转译后的class,如果babel的转译是宽松模式下的话(也就是loosetrue),webpack依旧会认为它有副作用不会tree-shaking掉,而rollup会。这是因为rollup有程序流分析的功能,可以更好的判断代码是否真正会产生副作用

原理

  • ES6 Module 引入进行静态分析,故而编译的时候正确判断到底加载了那些模块
  • 静态分析程序流,判断那些模块和变量未被使用或者引用,进而删除对应代码

依赖于import/export

通过导入所有的包后再进行条件获取

ES6 的 import 语法完美可以使用 tree shaking,因为可以在代码不运行的情况下就能分析出不需要的代码(编译时就进行加载了)

Webpack的构建流程 ?

Webpack 的运⾏流程是⼀个串⾏的过程,从启动到结束会依次执⾏以下流程:

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

在以上过程中,Webpack 会在特定的时间点⼴播出特定的事件,插件在监听到感兴趣的事件后会执⾏特定的逻辑,并且插件可以调⽤ Webpack 提供的 API 改变 Webpack 的运⾏结果。