首页 > 技术文章 > Webpack常见问题总结

goloving 2020-05-10 22:21 原文

一、有哪些常见的Loader

raw-loader:加载文件原始内容(utf-8)

file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)

url-loader:与 file-loader 类似,区别是用户可以设置一个阈值,大于阈值时返回其 publicPath,小于阈值时返回文件 base64 形式编码 (处理图片和字体)

source-map-loader:加载额外的 Source Map 文件,以方便断点调试

svg-inline-loader:将压缩后的 SVG 内容注入代码中

image-loader:加载并且压缩图片文件

json-loader 加载 JSON 文件(默认包含)

babel-loader:把 ES6 转换成 ES5

ts-loader: 将 TypeScript 转换成 JavaScript

style-loader:将 CSS 代码注入 JavaScript 中,通过 DOM 操作去加载

css-loader:加载 CSS,支持模块化、压缩、文件导入等特性

eslint-loader:通过 ESLint 检查 JavaScript 代码

更多 Loader 请参考官网:https://webpack.docschina.org/loaders

二、常见的Plugin

define-plugin:定义环境变量 (Webpack4 之后指定 mode 会自动配置)

ignore-plugin:忽略部分文件

html-webpack-plugin:简化 HTML 文件创建 (依赖于 html-loader)

web-webpack-plugin:可方便地为单页应用输出 HTML,比 html-webpack-plugin 好用

webpack-parallel-uglify-plugin: 多进程执行代码压缩,提升构建速度

mini-css-extract-plugin: 分离样式文件,CSS 提取为独立文件,支持按需加载 (替代extract-text-webpack-plugin)

serviceworker-webpack-plugin:为网页应用增加离线缓存功能

speed-measure-webpack-plugin: 可以看到每个 Loader 和 Plugin 执行耗时 (整个打包耗时、每个 Plugin 和 Loader 耗时)

webpack-bundle-analyzer: 可视化 Webpack 输出文件的体积 (业务组件、依赖第三方模块)

更多 Plugin 请参考官网:https://webpack.docschina.org/plugins

三、Loader和Plugin的区别

  Loader 本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。因为 Webpack 只认识 JavaScript,所以 Loader 就成了翻译官,对其他类型的资源进行转译的预处理工作。

  Loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。

  Plugin 就是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果

  Plugin 在 plugins 中单独配置,类型为数组,每一项是一个 Plugin 的实例,参数都通过构造函数传入

四、Webpack构建流程

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

  • 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数

  • 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译

  • 确定入口:根据配置中的 entry 找出所有的入口文件

  • 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理

  • 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系

  • 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会

  • 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统

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

  简单说就是三步:

  1、初始化:启动构建,读取合并配置参数,加载plugin,实例化Compiler

  2、编译:从entry入口出发,针对每个module串行调用对应的loader函数去翻译文件的内容,再找出该module依赖的module,递归的进行编译处理

  3、输出:将编译后的module组合成chunk,将chunk转换成文件,输出到文件系统中

四、使用webpack开发时,有哪些可以提高效率的插件

webpack-dashboard:可以更友好的展示相关打包信息。

webpack-merge:提取公共配置,减少重复配置代码

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

size-plugin:监控资源体积变化,尽早发现问题

HotModuleReplacementPlugin:模块热替换

五、source map是什么?生产环境怎么用?

  source map 是将编译、打包、压缩后的代码映射回源代码的过程。打包压缩后的代码不具备良好的可读性,想要调试源码就需要 soucre map。

  map文件只要不打开开发者工具,浏览器是不会加载的。

  线上环境一般有三种处理方案:

  • hidden-source-map:借助第三方错误监控平台 Sentry 使用

  • nosources-source-map:只会显示具体行数以及查看源代码的错误栈。安全性比 sourcemap 高

  • sourcemap:通过 nginx 设置将 .map 文件只对白名单开放(公司内网)

六、模块打包原理

  Webpack 实际上为每个模块创造了一个可以导出和导入的环境,本质上并没有修改代码的执行逻辑,代码执行顺序与模块加载顺序也完全一致。

七、文件监听原理

  在发现源码发生变化时,自动重新构建出新的输出文件。Webpack开启监听模式,有两种方式:

  • 启动 webpack 命令时,带上 --watch 参数
  • 在配置 webpack.config.js 中设置 watch:true

  原理:轮询判断文件的最后编辑时间是否变化,如果某个文件发生了变化,并不会立刻告诉监听者,而是先缓存起来,等 aggregateTimeout 后再执行。

module.export = {
    // 默认false,也就是不开启
    watch: true,
    // 只有开启监听模式时,watchOptions才有意义
    watchOptions: {
        // 默认为空,不监听的文件或者文件夹,支持正则匹配
        ignored: /node_modules/,
        // 监听到变化发生后会等300ms再去执行,默认300ms
        aggregateTimeout:300,
        // 判断文件是否发生变化是通过不停询问系统指定文件有没有变化实现的,默认每秒问1000次
        poll:1000
    }
}

八、Webpack 的热更新原理(重点)

  Webpack 的热更新又称热替换(Hot Module Replacement),缩写为 HMR。这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。

  HMR的核心就是客户端从服务端拉取更新后的文件,准确的说是 chunk diff (chunk 需要更新的部分),实际上 WDS 与浏览器之间维护了一个 Websocket,当本地资源发生变化时,WDS 会向浏览器推送更新,并带上构建时的 hash,让客户端与上一次资源进行对比。客户端对比出差异后会向 WDS 发起 Ajax 请求来获取更改内容(文件列表、hash),这样客户端就可以再借助这些信息继续向 WDS 发起 jsonp 请求获取该chunk的增量更新

  后续的部分(拿到增量更新之后如何处理?哪些状态该保留?哪些又需要更新?)由 HotModulePlugin 来完成,提供了相关 API 以供开发者针对自身场景进行处理,像react-hot-loader 和 vue-loader 都是借助这些 API 实现 HMR。

  细节请参考Webpack HMR 原理解析:https://zhuanlan.zhihu.com/p/30669007

九、如何对bundle体积进行监控和分析

  VSCode 中有一个插件 Import Cost 可以帮助我们对引入模块的大小进行实时监测,还可以使用 webpack-bundle-analyzer 生成 bundle 的模块组成图,显示所占体积。bundlesize 工具包可以进行自动化资源体积监控。

十、文件指纹是什么、怎么用

  文件指纹是打包后输出的文件名的后缀。

  • Hash:和整个项目的构建相关,只要项目文件有修改,整个项目构建的 hash 值就会更改

  • Chunkhash:和 Webpack 打包的 chunk 有关,不同的 entry 会生出不同的 chunkhash

  • Contenthash:根据文件内容来定义 hash,文件内容不变,则 contenthash 不变

十一:如何保证各个loader按照预想方式工作

  可以使用 enforce 强制执行 loader 的作用顺序,pre 代表在所有正常 loader 之前执行,post 是所有 loader 之后执行。(inline 官方不推荐使用)

推荐阅读