浅聊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 分钟前
webpack 核心模块 — loader & plugins
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
速盾cdn9 分钟前
速盾:vue的cdn是干嘛的?
服务器·前端·网络
四喜花露水41 分钟前
Vue 自定义icon组件封装SVG图标
前端·javascript·vue.js
前端Hardy1 小时前
HTML&CSS: 实现可爱的冰墩墩
前端·javascript·css·html·css3
web Rookie1 小时前
JS类型检测大全:从零基础到高级应用
开发语言·前端·javascript
Au_ust1 小时前
css:基础
前端·css
帅帅哥的兜兜1 小时前
css基础:底部固定,导航栏浮动在顶部
前端·css·css3
yi碗汤园2 小时前
【一文了解】C#基础-集合
开发语言·前端·unity·c#
就是个名称2 小时前
购物车-多元素组合动画css
前端·css
编程一生2 小时前
回调数据丢了?
运维·服务器·前端