Webpack--模块热替换(HMR)

一、概述

(1)live reload

只要检测到代码改动就会自动重新构建,然后触发网页刷新

(2)webapack中的模块热替换

可以让代码在页面不刷新的前提下得到最新的改动,甚至不需要重新发起请求就能看到更新后的效果。

二、开启HMR

(1)HMR是需要手动开启的,并且有一些必要条件。

(2)确保项目是基于webpack-dev-server或webpack-dev-middle进行开发的,webpack本身的命令行并不支持HMR。

const webpack = require('webpack');
module.exports = {
  //...
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    hot: true
  }
}

1、配置产生的结果是Webpack会为每个模块绑定一个module.hot对象,这个对象包含了HMR的API。借助这些API不仅可以实现对特定模块开启或者关系HMR,也可以添加热替换之外的逻辑。

2、调用HMR API 有两种方式,一种是手动地添加这部分代码;另一种是借助一些现成地工具,如react-hot-loader、vue-loader等。

3、如果应用的逻辑比较简单,可以直接手动添加代码来开启HMR。

//index.js
import { add } from 'util.js';
add(2, 3);
if (module.hot) {
  module.hot.accept();
}

假设index.js是应用的入口,可以把调用HMR API的代码放在入口中,这样HMR对于index.js和其依赖的所有模块都会生效。当发现有模块发生变动时,HMR会使应用在当前浏览器环境下重新执行一遍index.js(包括其依赖)的内容,但页面本身不会刷新。

三、HMR大致原理

(1)在本地开发环境下,浏览器是客户端,webpack-dev-server(WDS)相当于服务端

(2)HMR的核心是客户端从服务端拉取更新后的资源(准确得说,HMR拉取的不是整个资源文件,而是chunk diff即chunk需要更新的部分)

1、WDS对本地源文件进行监听,当本地资源发生变化时WDS通过websocket向浏览器推送更新事件,并带上这次构建的hash,让客户端与上一次资源进行比对。

2、客户端发现有差别,会向WDS发送一个请求来获取更改文件的列表,即哪些模块有了改动。通常这个请求的名字为[hash].hot-update.json。

3、请求返回结果会告诉客户端,需要更新的chunk名字和hash。客户端借助这些信息继续向WDS获取该chunk的增量更新。

4、客户端处理增量更新。

四、HMR API

moudle.hot.decline:将当前文件的HMR关掉,当前文件发生改变时禁止使用HMR进行更新,只能刷新整个页面。

module.hot.accept(['./util.js']):当util.js发生改变时仍然可以启用HMR更新

五、热更新原理

(1)配置热更新

(2)npm run dev 进入 webpack-dev-server.js文件

1、webpack(config)生成一个compiler

2、new Server(compiler, options, log)生成一个server,启动一个本地服务

3、启动websocket服务,通过websocket,可以建立本地服务和浏览器的双向通信。

(3)进入webpack-dev-server/lib/Server.js

updateCompiler(this.compiler, this.options);
this.setupHooks();
this.setupDevMiddleware();

1、server里先调用updateCompiler

1)两段关键代码:一个是获取websocket客户端代码路径,另一个是根据配置获取webpack热更新代码路径。

2)作用:修改webpack.config.js的entry配置,将webpack-dev-server/client(socket)和webpack/hot/dev-server(检查更新逻辑)注入客户端。

// 修改后的entry入口
{ entry:
    { index: 
        [
            // 上面获取的clientEntry
            'xxx/node_modules/webpack-dev-server/client/index.js?http://localhost:8080',
            // 上面获取的hotEntry
            'xxx/node_modules/webpack/hot/dev-server.js',
            // 开发配置的入口
            './src/index.js'
    	],
    },
} 

b、setupHooks方法

1)用来注册监听事件的,监听每次webpack编译完成。(监听了compiler.done)

2)当监听到一次webpack编译结束,就会调用_sendStats方法通过websocket给浏览器发送通知,okhash事件,这样浏览器就可以拿到最新的hash值了,做检查更新逻辑

c、setupDevMiddleware

1)webpack监听文件变化

2)这个方法主要执行了webpack-dev-middleware库,作用是本地文件的编译和输出以及监听,webpack-dev-server只负责启动服务和前置准备工作,所有文件相关的操作都抽离到webpack-dev-middleware库了(调用了compiler.watch方法)

3)webpack-dev-middleware最后执行setFs方法,这个方法主要目的就是将编译后的文件打包到内存

(4)浏览器接收到热更新的通知

1)/webpack-dev-server/client/index.js,注入到客户端的代码,即websocket,接收到okhash事件推送。hash事件:更新最新一次打包后的hash值。 ok事件:进行热更新检查

2)ok回调函数调用reloadApp,方法又利用node.js的EventEmitter,发出webpackHotUpdate消息

3)'xxx/node_modules/webpack/hot/dev-server.js',注入到客户端的另外一个入口代码,会监听webpackHotUpdate这个事件并且获取到最新的hash,事件内部调用check

4)check方法内部,检查更新调用的是module.hot.check方法,此方法是通过HotModuleReplacementPlugin注入的

5)module.hot.check方法内部,利用上一次保存的hash值,调用hotDownloadManifest发送xxx/hash.hot-update.json的ajax请求,请求结果获取热更新模块,以及下次热更新的Hash 标识,并进入热更新准备阶段

6)调用hotDownloadUpdateChunk发送xxx/hash.hot-update.js 请求,通过JSONP方式(JSONP获取的代码可以直接执行)

7)通过xx/hash.hot-update.js 请求获取回来的新编译后的代码是在一个webpackHotUpdate函数体内部的。也就是要立即执行webpackHotUpdate这个方法。

8) webpackHotUpdate方法内部调用hotAddUpdateChunk方法,此方法会把更新的模块moreModules赋值给全局全量hotUpdate,再调用hotUpdateDownloaded方法,hotUpdateDownloaded方法会调用hotApply进行代码的替换。

9)hotUpdate代码做的事

a、删除过期的模块,就是需要替换的模块

b、将新的模块添加到 modules 中

c、通过__webpack_require__执行相关模块的代码

(5)过程图 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值