Skip to content

Webpack

原理

webpack 是用来做什么的,原理是什么

webpack 是一个打包工具,将我们开发中遇到的如脚本开发语言 TS、ES6,样式开发工具 Sass、Less 编译成浏览器能识别的 js 和 css。

原理

  1. 在 webpack 中存在以下核心的概念

Entry: 入口,Webpack 执行构建的第一步就是从入口 Entry 开始,深度遍历生成 module 树的

Module: 模块,在 Webpack 中一切皆模块,一个原始文件就是一个模块,webpack 会从 Entry 开始递归遍历找出所有依赖的模块.

Chunk: 代码块,一个 Chunk 由多个 module 组成,用于代码的合并与切割

Loader: 模块装换器,用于将模块原内容按照需求转换成新的内容

Plugin: 扩展插件, 在 Webpack 构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。

  1. 其流程大体可以分为以下步骤:
  • 初始化参数:从配置文件和 Shell 语句中读取并合并参数,然后与 webpack 内置的默认参数进行合并处理。

  • 编译: 初始化 Compiler 对象,并根据上面的参数对应加载相应的插件,并执行 Compiler.run 开始编译。

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

  • 编译模块: 从入口文件开始,resolve 解析文件的路径,RuleSet 匹配文件对应的 loader,然后通过对应的 loader 解析文件,找出该模块依赖的模块,然后递归本步骤直到找到所有的入口依赖文件。

  • chunk 图生产:根据上面的 module 树,生成 chunk、chunkGroup 和 module 之间的关系图

  • 输出资源: 根据上面的 chunk 图,

bundle、chunk、module 分别是什么?

module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块, 其不仅仅是 js,也包含 css,图片等 chunk:代码块,一个 Chunk 由多个 Module 组合而成,用于代码合并与分割,这些模块是从入口模块通过依赖分析得来的。 bundle:最终打包完成的文件,一般就是和 chunk 一一对应的关系,bundle 就是对 chunk 进行编译压缩打包等处理后的产出

文件监听原理是什么?

在发现源码发生变化时,自动重新构建出新的输出文件。 原理 轮询判断文件的最后编辑时间是否变化,如果某个文件发生了变化,并不会立刻告诉监听者,而是先缓存来,等 aggregateTimeout 后再执行。 方式 启动 webpack 命令时,带上 --watch 参数。 在配置 webpack.config.js 中设置 watch:true。 缺点 每次需要手动刷新浏览器。

优化系列

使用 webpack 时如何优化项目体积

压缩代码,删除多余的注释,简化代码

  • 利用 uglifyjs-webpack-plugin 压缩 JS 文件
  • 利用 optimize-css-assets-webpack-plugin 压缩 css
  • 对于 externals 提取的常用库使用 CDN
  • Tree Shaking 摇树优化
  • CodeSplitting 代码分隔

如何提高 webpack 的构建速度

  • 通过 externals 提取常用库
  • 利用 DLLPligin 和 DllReferencePlugin 预提取核心底层公共库
  • Happypack : 使用 Happypack 将 loader 从单线程转换成多线程加速编译

如何使用 webpack 优化前端性能

  1. 压缩代码,删除多余的注释,简化代码
  • 利用 uglifyjs-webpack-plugin 压缩 JS 文件
  • 利用 optimize-css-assets-webpack-plugin 压缩 css
  1. 利用 CDN 加速
  • 对于 externals 提取的常用库使用 CDN
  • 对于编译的结果使用 publicPath 参数修改资源前缀路径
  1. 删除多余的代码
  • Tree Shaking 摇树优化
  • CodeSplitting 代码分隔 将代码按路由维度的组件提取多次使用的组件块,可以充分利用浏览器的缓存功能
  1. 提取公共代码

webpack 中的 loader 的作用是什么

有没有自己写过一个 webpack 的 loader

webpack 中 plugin 的作用是什么,有没有自己写过

使用 webpack 时如何优化项目体积

什么是 HMR,原理是什么

HMR 是 webpack 提供的一个模块热更新的功能,它允许 JS 在运行时更新各种模块,而不需要刷新整个页面。

原理:

HMR 主要分为以下几个部分:

  1. dev-server 通过 express 搭建路由的请求服务,从而拦截所有的请求
  2. webpack-hot-middleware 这是一个插件,主要用来构建内存文件系统,把 wenpack 的输出从 OutputFileSystem 替换成 InMemoryFileSystem。也提供拦截 express 的文件请求,使得其从内存文件系统的获取结果
  3. webpack HotModuleReplacementPlugin webpack 内置的插件,主要提供在编译阶段对 Chunk 的结果注入 HotModule 的能力,主要包含
  • 修改入口 Chunk 提供 webpackHotUpdate 函数能力
  • 修改入口 Chunk 的__webpack_require__函数
  • 内置一些核心的函数 如 hotDownloadManifest、hotDownloadUpdateChunk 等

流程如下:

  1. 当启动 dev-server 的时候,webpack 进入 watch 编译模式,并在编译期间向 entry 入口文件注入热更新的代码
  2. 浏览器 Client 首次打卡后,Server 和 Client 通过 Socket 建立通讯渠道
  3. 修改文件,Server 发现文件变化,webpack 开始从新编译,完成后触发 Done 事件
  4. Server 通过 socket 发送消息告知 Client;
  5. Client 根据 Server 的消息(hash 值和 state 状态),通过 ajax 请求获取 Server 的 manifest 描述文件;
  6. Client 对比当前 modules tree ,再次发请求到 Server 端获取新的 JS 模块;
  7. Client 获取到新的 JS 模块后,会更新 modules tree 并替换掉现有的模块;
  8. 最后调用 module.hot.accept() 完成热更新;

使用 webpack 打包时,如何更好地利用 long term cache

随着 http2 的发展,webpack 有没有更好的打包方案

webpack 中 tree shaking 的原理是什么

Tree-Shaking 主要是移除掉没有使用的代码,其主要体现在以下方面

js
// a.js
import { func2 } from b

// b.js
const fun1 = 1;
const fun2 = () => {}

export {
   func1,
   func2
}
  1. Wepack 在 Make 阶段收集 module 中所有的 import 和 export 的值,并构建 ModuleGraph(模块依赖关系图).
  2. 在 Seal 阶段会对 module.exportInfo 进行遍历分析是否被使用,并对被使用就会被标记为 setUsedConditionally,那么在生成文件的过程中就会移除所有未被使用的 exports(注意这一步不会涉及到 module 内部的实现代码,只会移除 export)

这一步上述例子会变成

js
// b.js

// 移除了 func1的 export
__webpack_require__.d(__webpack_exports__, "fun2", function () {
  return fun2
})

const fun1 = 1
const fun2 = () => {}
  1. 下面交给自定义的压缩插件 如 Teser 等,插件会通过 DCE 的方式去删除 module 中未被使用的代码部分(这一部分才会设计实现部分)

这部分会发现 func1 没有地方引用了,所以直接删除了这部分

  1. 最后生成最终的文件

重点:

  1. 在 webpack 中其只是通过标记和移除代码中的 未被使用的 export 语句,并不关注 export 的变量实现部分是否需要删除,具体删除实现部分是交给压缩插件,其通过这个变量在代码中不被使用了,那就说明需要删除

loader

如何保证各个 loader 按照预想方式工作?

可以使用 enforce 强制执行 loader 的作用顺序,pre 代表在所有正常 loader 之前执行,post 是所有之后执行。

vue-loader 的实现原理是什么

文件指纹策略

文件指纹策略

指的是 webpack 打包后输出的文件名包含的后缀信息,其分为三种指纹策略: Hash 、chunkhash、contenthash

  • hash: 只要一个文件修改,整个项目构建的 hash 值都会改变
  • chunkhash: 和 webpack 打包的 chunk 有关,不同的 entry 会生成不同的 chunkhash
  • contenthash: 文件内容来决定 hash,文件内容不变,则 contenthash 不变

使用了 md5 指纹之后发现每次打包还是会发生变化?

这是由于 webpack 的处理机制导致的,webpack 每次打包会把每个模块的配置信息 如文件名、文件顺序、文件 md5 等信息作为配置打入 js 中,以便于其进行模块管理,而这 部内容,每次打包都有可能发生变化,导致整个 js 文件名每次都会发生变化。

webpack 提供了一个 manifest 的机制来剥出这个配置文件。我们需要使用 CommonsChunkPlugin 来将其剥离,同时使用 chunk-manifest-webpack-plugin 读取其 内容导出另外一个文件,防止其内容变化导致整个 js 文件指纹发生变化

webpack 模块化

  1. CommonJs 规范
  • CommonJs 使用 module.exports、exports 导出模块,使用 require 引入模块
  1. ES Module 规范
  • ES Module 使用 export 导出模块,使用 import 引入模块
  • import() 引入模块,可以实现懒加载
  • ES Module 静态导入的方式,可以实现 tree shaking

运行时原理

import 动态加载原理

Webpack4 到 5 的区别

  1. Tree Shaking

  2. 缓存

  • webpack4 支持缓存到内存中需要使用 hard-source-webpack-plugin
  • webpack5 默认支持内存缓存 type: memory
  1. 资源模块类型

webpack5 加入了资源模块类型系统,减少了 webpack4 中对于 loader 中不同资源类型使用 file-loader 、url-loader、raw-loader

  • webpack5
    • asset/resource 替换 file-loader(发送单独文件)
    • asset/inline 替换 url-loader (导出 url)
    • asset/source 替换 raw-loader(导出源代码)
    • asset
  1. 模块联邦

webpack5 和 webpack4 的区别有哪些

file-loader 和 url-loader 的区别

file-loader : 主要是解决文件的路径问题 url-loader : 是对 file-loader 的封装,加强了对图片的处理,对于小图片会使用内联 Base64 的方式