浅聊webpack的工作原理

参考文献: https://webpack.docschina.org/concepts/

简述一下

WebPack 是一个模块打包工具,可以使用 WebPack 管理模块。在 webpack 看来,项目里所有资源皆模块,分析模块间的依赖关系,最终编绎输出模块为 HTML、JavaScript、CSS 以及各种静态文件(图片、字体等),让开发过程更加高效。

是什么?

webpack 是一个用于现代JavaScript应用程序的静态模块打包工具

静态模块

这里的静态模块指的是开发阶段,可以被 webpack 直接引用的资源(可以直接被获取打包进bundle.js的资源)

webpack处理应用程序时,它会在内部构建一个依赖图,此依赖图对应映射到项目所需的每个模块(不再局限js文件),并生成一个或多个 bundle

webpack的能力

编译代码能力,提高效率,解决浏览器兼容问题

模块整合能力,提高性能,可维护性,解决浏览器频繁请求文件的问题

万物皆可模块能力,项目维护性增强,支持不同种类的前端模块类型,统一的模块化方案,所有资源文件的加载都可以通过代码控制

原理

1、核心概念

(1)entry:一个可执行模块或者库的入口。

(2)chunk:多个文件组成一个代码块。可以将可执行的模块和他所依赖的模块组合成一个chunk,这是打包。

(3)loader:文件转换器。例如把es6转为es5,scss转为css等

(4)plugin:扩展webpack功能的插件。在webpack构建的生命周期节点上加入扩展hook,添加功能。

2、webpack构建流程(原理)

1.Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:1.初始化参数: 从配置文件和 Shell 语句中读取与合并参数,得出最终的参数

2.开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译确定入口: 根据配置中的 entry 找出所有的入口文件3 .

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

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

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

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

webpack 中常见的 Loader?

以下就是常见的 loader:

  • image-loader:加载并且压缩图片文件
  • css-loader:帮助 webpack 打包处理 css 文件,使用 css-loader 必须要配合使用 style-loader
  • style-loader:用于将 css 编译完成的样式,挂载到页面的 style 标签上。但是要注意 loader 执行顺序,style-loader 要放在第一位,loader 都是从后往前执行
  • babel-loader:把 ES6 转换成 ES5
  • sass-loader: css 预处理器,能更好的编写 css
  • postcss-loader: 用于补充 css 样式在各种浏览器的前缀,很方便,不需要手动写了
  • eslint-loader: 用于检查代码是否符合规范,是否存在语法错误
  • url-loader: 处理图片类型资源,可以根据图片的大小进行不同的操作,如果图片大小大于指定大小,则将图片进行资源打包,否则将图片转换为 base64 格式字符串,再把这个字符串打包到 JS 文件里

webpack 中常见的 Plugin?

Plugin(Plug-in)是一种计算机应用程序,它和主应用程序互相交互,以提供特定的功能

是一种遵循一定规范的应用程序接口编写出来的程序,只能运行在程序规定的系统下,因为其需要调用原纯净系统提供的函数库或者数据

webpack中的plugin也是如此,plugin赋予其各种灵活的功能,例如打包优化、资源管理、环境变量注入等,它们会运行在 webpack 的不同阶段(钩子 / 生命周期),贯穿了webpack整个编译周期

配置方式

这里讲述文件的配置方式,一般情况,通过配置文件导出对象中plugins属性传入new实例对象。如下所示:

复制代码
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 访问内置的插件
module.exports = {
  ...
  plugins: [
    new webpack.ProgressPlugin(),
    new HtmlWebpackPlugin({ template: './src/index.html' }),
  ],
};

二、特性

其本质是一个具有apply方法javascript对象

apply 方法会被 webpack compiler调用,并且在整个编译生命周期都可以访问 compiler对象

复制代码
const pluginName = 'ConsoleLogOnBuildWebpackPlugin'

class ConsoleLogOnBuildWebpackPlugin {
  apply(compiler) {
    compiler.hooks.run.tap(pluginName, (compilation) => {
      console.log('webpack 构建过程开始!')
    })
  }
}

module.exports = ConsoleLogOnBuildWebpackPlugin

compiler hooktap方法的第一个参数,应是驼峰式命名的插件名称

关于整个编译生命周期钩子,有如下:

  • entry-option :初始化 option
  • run
  • compile: 真正开始的编译,在创建 compilation 对象之前
  • compilation :生成好了 compilation 对象
  • make 从 entry 开始递归分析依赖,准备对每个模块进行 build
  • after-compile: 编译 build 过程结束
  • emit :在将内存中 assets 内容写到磁盘文件夹之前
  • after-emit :在将内存中 assets 内容写到磁盘文件夹之后
  • done: 完成所有的编译过程
  • failed: 编译失败的时候

webpack 热更新的实现原理?

Hot Module Replacement,简称 HMR,在不需要刷新整个页面的同时更新模块,能够提升开发的效率和体验。热更新时只会局部刷新页面上发生了变化的模块,同时可以保留当前页面的状态,比如复选框的选中状态等。

热更新的实现

webpack-dev-server 启动的时候会做三件事情

  1. 启动 webpack,生成 compiler 实例,compiler 实例的功能很多,比如用来启动 webpack 的编译工作,监听文件变化等。
  2. 使用 Express 启动一个本地服务,使得浏览器可以访问本地服务
  3. 启动 websocket 服务,用于浏览器和本地 node 服务进行通讯。

监听文件变化

webpack 监听文件变化主要是通过 webpack-dev-middleware 这个库来完成,它负责本地文件的编译、输出和监听 webpack-dev-middleware 中执行了 compiler.watch 方法,它主要做了两件事情

  1. 对本地文件编译打包
  2. 编译结束之后,开启监听,文件发生变化时重新编译,并持续进行监听

监听 webpack 编译结束

setupHooks 方法用来注册监听事件,当监听到 webpack 编译结束时,通过 websocket 给浏览器发通知,浏览器拿到 hash 只之后就可以做检查更新逻辑。

首先要知道server端和client端都做了处理工作:

1.第一步,在 webpack 的watch 模式下,文件系统中某一个文件发生修改,webpack 监听到文件变化,根据配置文

件对模块重新编译打包,并将打包后的代码通过简单的 JavaScript 对象保存在内存中。

2.第二步是 webpack-dev-server和 webpack 之间的接口交互,而在这一步,主要是 dev-server 的中间件webpack- dev-middleware 和 webpack 之间的交五,webpack-dev-middleware 调用 webpack 暴露的API对代码变化进行监 控,并且告诉 webpack,将代码打包到内存中。

3.第三步是 webpack-dev-server 对文件变化的一个监控,这一步不同于第一步,并不是监控代码变化重新3打包。当我们在配置文件中配置了devServer.watchContentBase 为 true 的时候,Server 会监听这些配置文件夹中静态文件的变化,变化后会通知浏览器诺对应用进行 live reload。注意,这儿是浏览器刷新,和HMR 是两个概念。

4.第四步也是 webpack-dev-server 代码的工作,该步骤主要是通过 sockis (webpack-dev-server 的依赖)4在浏览器端和服务端之间建立一个 websocket 长连接,将 webpack 编译打包的各个阶段的状态信息告知浏览器端,同时也包括第=步中 Server 监听静态文件变化的信息。浏览器端根据这些 socket 消息进行不同的操作。当然服务端传递的最主要信息还是新模块的 hash 值,后面的步骤根据这一 hash 值来进行模块热替换

5.webpack-dev-server/client 端并不能够请求更新的代码,也不会执行热更模块操作,而把这些工作又交回5给了webpack,webpack/hot/dev-server 的工作就是根据 webpack-dev-server/client 传给它的信息以及dev-server 的配置决定是刷新浏览器呢还是进行模块热更新。当然如果仅仅是刷新浏览器,也就没有后面那些步骤了。

6.HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上一步传递给他的新模块的 hash 值,它通过JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求,服务端返回一个json,该json 包含了所有要更新的模块的 hash 值,获取到更新列表后,该模块再次通过jsonp 请求,获取到最新的模块代码。这就是上图中 7、8、9 步骤。

7.而第 10 步是决定 HMR 成功与否的关键步骤,在该步骤中,HotModulePlugin 将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。

8.最后一步,当 HMR 失败后,回退到 live reload 操作,也就是进行浏览器刷新来获取最新打包代码.

如何借助 webpack 来优化前端性能?

用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。

压缩代码: 删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的 UglifvJsPlugin 和ParallelUglifvPlugin 来压缩JS文件,利用 cssnano (css-loader?minimize) 来压缩css

利用CDN加速:在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output 参数和冬loader的 publicPath 参数来修改资源路径

Tree Shaking:将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数 --optimizeminimize 来实现

Code Splitting: 将代码按路由维度或者组件分块(chunk),这样做到按需加载,同时可以充分利用浏览器缓存

提取公共第三方库: SplitChunksplugin插件来进行公共模块抽取,利用浏览器缓存可以长期缓存这些无需频繁变动的公共代码

相关推荐
江号软件分享2 分钟前
从DNS到防火墙:NetDisabler多策略断网方法详解
前端
灵犀学长11 分钟前
解锁HTML5页面生命周期API:前端开发的新视角
前端·html·html5
江号软件分享19 分钟前
轻松解决Office版本冲突问题:卸载是关键
前端
致博软件F2BPM26 分钟前
Element Plus和Ant Design Vue深度对比分析与选型指南
前端·javascript·vue.js
慧一居士1 小时前
flex 布局完整功能介绍和示例演示
前端
DoraBigHead1 小时前
小哆啦解题记——两数失踪事件
前端·算法·面试
一斤代码7 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子7 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年7 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子7 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架