webpack热更新原理(面试大概率会问)

news2024/11/15 15:39:52

搭建webpack环境

创建一个项目

mkdir dev-erver && cd dev-server
npm init -y // 快速创建一个项目配置
npm i webpack webpack-dev-server webpack-cli --save-dev
mkdir src // 创建资源目录
mkdir dist // 输出目录
touch webpack.dev.js // 因为是在开发环境需要热更新,所以直接创建dev配置文件

目录结构

image.png

webpack版本

这里说明一下,webpack4和webpack5的配置信息或者显示信息可能有点区别

  "devDependencies": {
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "^4.9.3"
  }

编写配置文件

// webpack.dev.js

'use strict';

const path = require('path');

module.exports = {
    entry: './src/index.js', // 入口文件
    output: {
        path: path.resolve(__dirname, 'dist'), // 输出到哪个文件夹
        filename: 'output.js' // 输出的文件名
    },
    mode: 'development', // 开发模式
    devServer: {
        // contentBase: path.resolve(__dirname, 'dist')  // contentBase是用来指定被访问html页面所在目录的;
        //但是我本地报错了,使用下面的语句
    static: path.resolve(__dirname, "dist")

    }
};

新建文件

// src/index.js

'use strict' 

document.write('hello world~')

package.json添加一条命令

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server --config webpack.dev.js --open"
  },

npm run dev 运行

image.png

我们看到文件已经打包完成了,但是在dist目录里并没有看到文件,这是因为WDS是把编译好的文件放在缓存中,没有放在磁盘上,但是我们是可以访问到的,

output.js 对应你在webpack配置文件中的输出文件,配置的是什么就访问什么

http://localhost:8080/output.js

显然我们想看效果而不是打包后的代码,所以我们在dist目录里创建一个html文件引入即可,

<script src="./output.js"></script>

感受webpack的热更新

内容出来了,我们接下来修改index.js文件,来看下是否可以自动刷新

'use strict' 

document.write('hello world~byebye world')

这确实是热更新,但是这种是每一次修改会重新刷新整个页面,大家可以打开控制台查看。webpack-dev-server 提供了实时重加载的功能,但是不能局部刷新。必须配合后两步的配置才能实现局部刷新,这两步的背后其实是借助了HotModuleReplacementPlugin。

webpack-dev-server搭配HotModuleReplacementPlugin 实现热更新

我们需要的是,更新修改的模块,但是不要刷新页面。这个时候就需要用到模块热替换。

模块热替换(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允许在运行时更新各种模块,而无需进行完全刷新。

特性

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

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

启用

// webpack.dev.js

const path = require('path');
const webpack = require('webpack'); // 主要多了这一行

module.exports = {
    entry: './src/index.js', // 入口文件
    output: {
        path: path.resolve(__dirname, 'dist'), // 输出到哪个文件夹
        filename: 'output.js' // 输出的文件名
    },
    mode: 'development', // 开发模式
    devServer: {
        // contentBase: path.resolve(__dirname, 'dist')  // contentBase是用来指定被访问html页面所在目录的;但是我本地报错了,使用下面的语句
        static: path.resolve(__dirname, "dist"),
        hot: true // 主要多了这一行

    },
    plugins: [ //  主要多了这一行
        new webpack.HotModuleReplacementPlugin()
    ]
};

参考 前端进阶面试题详细解答

我们修改一下文件,形成引用关系

//index.js

import { test } from './page1.js' 

document.write('hello world~1234')

test()
//page1.js

module.exports = {
  test: function () {
    console.log(11111)
  }
}

在入口页index.js面再添加一段

if (module.hot) {
    module.hot.accept();
}

思考💡:为什么平时修改代码的时候不用监听module.hot.accept也能实现热更新?

那是因为我们使用的 loader 已经在幕后帮我们实现了。

接下来执行npm run dev

然后我们修改page1.js,会发现页面并没有刷新,只是更新了部分文件

这样我们的热更新就实现了。

热更新原理

第一步,在 webpack 的 watch 模式下,文件系统中某一个文件发生修改,webpack 监听到文件变化,根据配置文件对模块重新编译打包,并将打包后的代码通过简单的 JavaScript 对象保存在内存中。

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

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

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

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

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

而第 10 步是决定 HMR 成功与否的关键步骤,在该步骤中,HotModulePlugin 将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。
最后一步,当 HMR 失败后,回退到 live reload 操作,也就是进行浏览器刷新来获取最新打包代码。

在初步体会了webpack的热更新之后,可能需要思考以下的问题

思考💡:为什么需要热更新?

Hot Module Replacement(以下简称 HMR)是 webpack 发展至今引入的最令人兴奋的特性之一 ,当你对代码进行修改并保存后,webpack 将对代码重新打包,并将新的模块发送到浏览器端,浏览器通过新的模块替换老的模块,这样在不刷新浏览器的前提下就能够对应用进行更新。例如,在开发 Web 页面过程中,当你点击按钮,出现一个弹窗的时候,发现弹窗标题没有对齐,这时候你修改 CSS 样式,然后保存,在浏览器没有刷新的前提下,标题样式发生了改变。感觉就像在 Chrome 的开发者工具中直接修改元素样式一样。

思考💡:HMR是怎样实现自动编译的?

webpack通过watch可以监听文件编译完成和监听文件的变化,webpack-dev-middleware可以调用webpack的API监听代码的变化,webpack-dev-middleware利用sockjs和webpack-dev-server/client建立webSocket长连接。将webpack的编译编译打包的各个阶段告诉浏览器端。主要告诉新模块hash的变化,然后webpack-dev-server/client是无法获取更新的代码的,通过webpack/hot/server获取更新的模块,然后HMR对比更新模块和模块的依赖。

思考💡:模块内容的变更浏览器又是如何感知的?

webpack-dev-middleware利用sockjs和webpack-dev-server/client建立webSocket长连接。将webpack的编译编译打包的各个阶段告诉浏览器端。

思考💡:以及新产生的两个文件又是干嘛的?

d04feccfa446b174bc10.hot-update.json

告知浏览器新的hash值,并且是哪个chunk发生了改变

main.d04feccfa446b174bc10.hot-update.js

告知浏览器,main 代码块中的/src/title.js模块变更的内容

首先是通过XMLHttpRequest的方式,利用上一次保存的hash值请求hot-update.json文件。这个描述文件的作用就是提供了修改的文件所在的chunkId。

然后通过JSONP的方式,利用hot-update.json返回的chunkId 及 上一次保存的hash 拼接文件名进而获取文件内容。

思考💡:怎么实现局部更新的?

当hot-update.js文件加载好后,就会执行window.webpackHotUpdate,进而调用了hotApply。hotApply根据模块ID找到旧模块然后将它删除,然后执行父模块中注册的accept回调,从而实现模块内容的局部更新。

思考💡:webpack 可以将不同的模块打包成 bundle 文件或者几个 chunk 文件,但是当我通过 webpack HMR 进行开发的过程中,我并没有在我的 dist 目录中找到 webpack 打包好的文件,它们去哪呢?

原来 webpack 将 bundle.js 文件打包到了内存中,不生成文件的原因就在于访问内存中的代码比访问文件系统中的文件更快,而且也减少了代码写入文件的开销,这一切都归功于memory-fs,memory-fs 是 webpack-dev-middleware 的一个依赖库,webpack-dev-middleware 将 webpack 原本的 outputFileSystem 替换成了MemoryFileSystem 实例,这样代码就将输出到内存中。

思考💡:通过查看 webpack-dev-server 的 package.json 文件,我们知道其依赖于 webpack-dev-middleware 库,那么 webpack-dev-middleware 在 HMR 过程中扮演什么角色?

webpack-dev-middleware扮演是中间件的角色,一头可以调用webpack暴露的API检测代码的变化,一头可以通过sockjs和webpack-dev-server/client建立webSocket长连接,将webapck打包编译的各个阶段发送给浏览器端。

思考💡:使用 HMR 的过程中,通过 Chrome 开发者工具我知道浏览器是通过 websocket 和 webpack-dev-server 进行通信的,但是 websocket 的 message 中并没有发现新模块代码。打包后的新模块又是通过什么方式发送到浏览器端的呢?为什么新的模块不通过 websocket 随消息一起发送到浏览器端呢?

功能块的解耦,各个模块各司其职,dev-server/client 只负责消息的传递而不负责新模块的获取,而这些工作应该有 HMR runtime 来完成,HMR runtime 才应该是获取新代码的地方。再就是因为不使用 webpack-dev-server 的前提,使用 webpack-hot-middleware 和 webpack 配合也可以完成模块热更新流程,在使用 webpack-hot-middleware 中有件有意思的事,它没有使用 websocket,而是使用的 EventSource。综上所述,HMR 的工作流中,不应该把新模块代码放在 websocket 消息中。

思考💡:浏览器拿到最新的模块代码,HMR 又是怎么将老的模块替换成新的模块,在替换的过程中怎样处理模块之间的依赖关系?

思考💡:当模块的热替换过程中,如果替换模块失败,有什么回退机制吗?

模块热更新的错误处理,如果在热更新过程中出现错误,热更新将回退到刷新浏览器

面试题:说一下webpack的热更新原理?

webpack通过watch可以监测代码的变化;webpack-dev-middleware可以调用webpack暴露的API检测代码变化,并且告诉webpack将代码保存到内存中;webpack-dev-middleware通过sockjs和webpack-dev-server/client建立webSocket长连接,将webpack打包阶段的各个状态告知浏览器端,最重要的是新模块的hash值。webpack-dev-server/client通过webpack/hot/dev-server中的HMR去请求新的更新模块,HMR主要借助JSONP。先拿到hash的json文件,然后根据hash拼接出更新的文件js,然后HotModulePlugin对比新旧模块和模块依赖完成更新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/377401.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三层架构+MVC

前言图 什么是三层架构 什么是三层架构 什么是系统架构 所谓系统架构是指&#xff0c;整合应用系统程序大的结构。经常提到的系统结构有两种&#xff1a;三层架构与MVC。这两种结构既有区别&#xff0c;又有联系。但这两种结构的使用&#xff0c;均是为了降低系统模块间的耦合…

这七个100%提高Python代码性能的技巧,一定要知道

B站|公众号&#xff1a;啥都会一点的研究生 相关阅读 整理了几个100%会踩的Python细节坑&#xff0c;提前防止脑血栓 整理了十个100%提高效率的Python编程技巧&#xff0c;更上一层楼 Python-列表&#xff0c;从基础到进阶用法大总结&#xff0c;进来查漏补缺 Python-元组&…

Web Spider案例 网洛克 第二题 JJEncode加密 练习(六)

文章目录一、资源推荐二、逆向目标三、抓包分析 & 下断分析逆向3.1 抓包分析3.2 下断分析逆向四、本地JS代码调试 & 完整JS加密代码4.1 本地JS代码调试4.2 完整JS加密代码五、python具体实现总结提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 …

跬智信息全新推出云原生数据底座玄武,助力国产化数据服务再次升级

2月28日&#xff0c;跬智信息&#xff08;Kyligence&#xff09;宣布全新推出国产化云原生数据底座开源项目玄武&#xff08;XUANWU&#xff09;&#xff0c;以助力企业加速数据平台上云&#xff0c;并实现国产化升级。玄武&#xff08;XUANWU&#xff09;是在容器化技术上形成…

分层测试(2)单元测试【必备】

1. 什么是单元测试&#xff1f; 对代码中的逻辑隔离的最小代码片段进行测试&#xff0c;验证其逻辑是否符合预期&#xff0c;单元可以是函数&#xff0c;方法&#xff0c;类&#xff0c;功能模块。 2. 单元测试的优点 掌握代码&#xff1a;单元测试允许开发人员了解单元提供…

35岁的测试被裁,公司地位还不如00后...

国内的互联网行业发展较快&#xff0c;所以造成了技术研发类员工工作强度比较大&#xff0c;同时技术的快速更新又需要员工不断的学习新的技术。因此淘汰率也比较高&#xff0c;超过35岁的基层研发类员工&#xff0c;往往因为家庭原因、身体原因&#xff0c;比较难以跟得上工作…

python公司企业编码条形码二维码生成系统

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;python编码 免费获取完整源码源文件配置说明教程等 在PyCharm中运行《企业编码生成系统》即可进入如图1所示的系统主界面。在该界面中可以选择要使用功能对应的菜单进行不同的操作。在选择功能菜单时&#xff0c;只需要输入…

Javascript的API基本内容(四)

一、日期对象 获取时间戳的方法&#xff0c;分别为 getTime 和 Date.now 和 new Date() // 1. 实例化const date new Date()// 2. 获取时间戳console.log(date.getTime()) // 还有一种获取时间戳的方法console.log(new Date())// 还有一种获取时间戳的方法console.log(Date.n…

maven进阶总结

maven进阶总结1. maven之间可以互相导包2. maven的依赖是具有传递性的3. 可选依赖和排除依赖&#xff1a;4. 继承与聚合4.1聚合4.2 继承5. 属性的配置与使用6. 版本管理8. 多环境开发和跳过测试&#xff08;了解&#xff09;1. maven之间可以互相导包 从当前maven的pom文件中&…

DNS服务器部署的详细操作(图文版)

DNS服务器的部署 打开虚拟机后查看已经开放的端口&#xff0c;可以看到没有TCP53、UDP53&#xff0c;说明DNS服务端口没有打开 打开我的电脑—双击CD驱动器— 选择安装可选的Windows组件 选择网络服务—域名系统&#xff08;DNS&#xff09;— 点击下一步后会弹出如下弹…

Kubernetes08:Controller (有状态应用)

Kubernetes08&#xff1a;Controller 1、无状态和有状态的区别 1&#xff09;无状态&#xff1a; 认为Pod都是相同的没有顺序要求不用考虑我在哪个node上运行随意进行伸缩和扩展 有状态&#xff1a; 上面的因素都需要考虑到让每个Pod都是独立的&#xff0c;保持Pod启动顺序…

mysql去重查询的三种方法

文章目录前言一、插入测试数据二、剔除重复数据方法1.方法一&#xff1a;使用distinct2.方法二&#xff1a;使用group by3.方法三&#xff1a;使用开窗函数总结前言 数据库生成环境中经常会遇到表中有重复的数据&#xff0c;或者进行关联过程中产生重复数据&#xff0c;下面介…

【API网关】Kong安装和基本操作

文章目录前言一、API网关选型和Kong的安装1. 什么是API网关2. API网关技术选型3. 安装postgresql和migrations4. 安装kong5. 安装konga二、基本的路由转发配置1. kong的8001、8000和1337端口号的关系2. 基本的路由转发配置3. kong集成consul实现服务发现和负载均衡4. kong配置j…

程序员推荐的良心网站合集!

今天来给大家推荐几个程序员必看的国外良心网站合集。 IBM developer 技术性很强的博客网站&#xff0c;网站自带真实示例代码和架构解决方案&#xff0c;大家可以在上面找到适合自己的语言方向开始学习交流。 https://developer.ibm.com/ infoq 技术论坛社区&#xff0c;内…

【JavaScript】根据元素内容遍历元素的方案

▒ 目录 ▒&#x1f6eb; 导读需求1️⃣ jQuery2️⃣ XPATH&#xff08;document.evaluate&#xff09;3️⃣ 原生js&#xff08;querySelectorAll & Array&#xff09;&#x1f6ec; 文章小结&#x1f4d6; 参考资料&#x1f6eb; 导读 需求 因业务需要&#xff0c;根据元…

文献阅读笔记 # 面向大规模多版本软件系统的代码克隆检测加速技术

面向大规模多版本软件系统的代码克隆检测加速技术&#xff0c;方维康 吴毅坚 赵文耘&#xff0c;《计算机应用与软件》复旦大学软件学院、复旦大学上海市数据科学重点实验室2022 April 面向大规模多版本软件系统的代码克隆检测加速技术 摘要 很多代码克隆检测方法主要针对软…

_Linux (网络版计算器简易实现)

文章目录1. 协议2. 网络版计算器简易实现代码链接3. 网络版计算器2-1. 约定的协议方案有两种2-3. 协议代码框架1. 自定义的协议方案2. json(库里的完整协议方案&#xff09;2-4. send和recv单独使用不安全2-5. 剩余代码写法讲解参考如下&#xff1a;2-6. 代码运行结果示意图&am…

9.5 PIM-SM

实验目的 熟悉PIM-SM的应用场景掌握PIM-SM的配置方法 实验拓扑 实验拓扑如图9-40所示&#xff1a; 图9-40&#xff1a;PIM-SM 实验步骤 &#xff08;1&#xff09;配置IP地址 MCS1的配置 MCS1的配置如图9-41所示&#xff1a; 图9-41&#xff1a;配置MCS1的IP地址 R1的配置 …

VMware安装FreeBSD虚拟机

1. 下载FreeBSD镜像地址 国内阿里云下载地址&#xff1a; freebsd-releases-ISO-IMAGES安装包下载_开源镜像站-阿里云 选择自己需要的版本下载。 2. 创建FreeBSD虚拟机 2.1. 选择操作系统类型 2.2. 导入FreeBSD镜像 3. 安装FreeBSD 第1步&#xff1a;保持默认让其自动进入…

复习知识点十之方法的重载

目录 方法的重载 练习1: 练习1: 数组遍历 练习2: 数组的最大值 练习3: 练习4: 复制数组 基本数据类型和引用数据类型 方法的重载 Java虚拟机会通过参数的不同来区分同名的方法 练习1: public class Test4 {public static void main(String[] args) {//调用方法 // …