最近想了解一下webpack的功能,基于现在的4.x版本,过了一遍官方文档,内容实在太多,根本记不住...,不过发现了几个有意思的扩展内容,HMR就是其中之一(另一个是PWA的SW-Service Worker),所以就花点时间整理下

1、自动刷新

webpack.config配置参数devServer中存在inline参数,参数可以设置devServer的两种模式inlineiframe,默认使用inline

devServer: {contentBase: "./dist",// inline: false,hot: true,
},

官方是这样描述inline模式的

Toggle between the dev-server's two different modes. By default the application will be served with inline mode enabled. This means that a script will be inserted in your bundle to take care of live reloading, and build messages will appear in the browser console.

大致意思是说在inline模式下,在bundle.js中会插入代码来处理自动刷新,由于没看源码,根据开关inline模式以后,webpack-dev-server启动时候的控制台输出,和官方文档说明,所以个人猜测,大致实现:

  1. bundle.js插入socket的片段,在浏览器加载后建立socket连接
  2. 利用webpack -watch来监听文件变化,发送socket信息告知浏览器,浏览器接受信息调用reload来实现刷新页面刷新

通过分析浏览器请求(inline模式会在加载bundle.js后发起一个websocket请求)和bundle.js代码确认了这一点(onSocketMsg-> reloadApp-> applyReload),也就是说使用webpack-dev-server启动就可以开启自动刷新功能了

2. HMR

HMR(Hot Module Replacement),添加、修改模块(修改JS/CSS)后浏览器内容通过非刷新的方式自动更新,提高开发效率,要注意HMR只应在开发环境使用

HMR在配置成功以后,修改CSS/JS不会进行页面刷新(注意保存文件变更的时候,浏览器Tab页是否是自动刷新)

2.1 HMR配置

HMR配置有四个关键点:

  1. 使用webpack-dev-server作为服务器启动
  2. webpack.configdevServer中配置hot: true
  3. webpack.config的plugins增加HotModuleReplacementPlugin
  4. 使用module.hot.accept增加HMR代码

webpack.config

module.exports = {mode: 'development',devtool: 'inline-source-map',entry: {main: __dirname + '/app/main.js',},output: {filename: '[name].bundle.js',path: path.resolve(__dirname, 'dist'),},devServer: {contentBase: "./dist",//本地服务器所加载的页面所在的目录// inline: true, //实时刷新hot: true,},plugins: [new HtmlWebpackPlugin({template: __dirname + '/app/index.tmpl.html'}),new webpack.HotModuleReplacementPlugin()],module: {rules: [{test: /\.css$/,use: ['style-loader', 'css-loader']}]},
};

main.js

import './extra.js'
import './style.css'if (module.hot) {module.hot.accept('./extra.js', function() {console.log('Accepting the updated printMe module!');})// 关闭指定子模块的HMR// module.hot.decline('./extra.js')
}

extra.js

if(module.hot){// 监听当前模块HMR异常// module.hot.accept(() => {//     console.log('error handler')// })// 关闭当前模块HMR// module.hot.decline()// 存储模块数据// module.hot.dispose(disposeHander)// if (module.hot.data) {//    console.log(module.hot.data.a)// }// 移除模块数据// module.hot.removeDisposeHandler(disposeHander)
}

配置成功以后修改extra.js或者style.css保存,都会触发HMR,控制台会输出[HMR]开头的日志

PS:

  1. css文件我们会配置style-loaderstyle-loader中已经增加了module.hot.accept的支持,所以即使不配置module.hot.accept,对于css也可以HMR,但是如果JS没有调用module.hot.accept,HMR执行找不到对应的内容,则会直接刷新页面,可以设置参数hotOnly: true来防止自动刷新

  2. webpack plugins未添加HotModuleReplacementPlugin使用hot: true的时候会报错 Uncaught Error: [HMR] Hot Module Replacement is disabled.

  3. 官网配置分为Node版本和Web版本,由于自己也了解不深,所以不探讨Node版本如何使用webpack的HMR

2.2 使用HMR API

要想使用HMR,需要利用到HMR的API,在添加了HotModuleReplacementPlugin模块后,就可以使用module.hot的API

Module API

用于模块处理的API

2.2.1 accept

module.hot.accept(dependencies,callback):添加需要HMR的模块,以及触发变更后的回调,dependencies支持字符串或者字符串数组

module.hot.accept(errorHandler):被添加的模块内部使用可以捕获HMR异常时候抛出的异常

2.2.2 decline

module.hot.decline(dependencies):和accept操作相反,标记一个不需要HMR管理的模块

module.hot.decline():JS模块中使用,将导致该模块无法被HMR

2.2.3 dispose

module.hot.addDisposeHandler/moduel.hot.dispose(data =>{}):即使使用HMR,每次更新后数据是不能进行保留的,如果想将数据传递给更新后的模块,使用dispose对全局对象data赋值,之后可以通过module.hot.data来获取

module.hot.removeDisposeHandler(callback):移除通过disposeaddDisposeHandler添加的回调,移除后module.hot.data将变为空对象{}(调用dispose之前module.hot.dataundefined)

Management API

用于HMR状态管理的API

2.2.4 status

module.hot.status(): HMR过程是存在变化状态的,通过该函数来获取到HMR的当前状态

2.3 vue-cli3配置HMR

使用vue-cli3构建,通过vue-cli-service serve启动的项目,其本身会添加HMR,在vue.config.jsdevServer中配置hot: false可以关闭。

但是项目中发现一个情况,当配置了https: true以后,必须指定host: localhost,HMR功能才可以开启,否则HMR功能将失效(其实是整个自动更新功能都失效了,不会发起socket请求),而在webpack中并没有出现该情况。

据此揣测,vue-cli-service在使用https: true的时候,socket链接必须指定使用hostname才可以进行构建,根据查找bundle.js代码,确认该逻辑(可以在bundle.js查找socket关键字,可以看到hostname相关检测逻辑)。

3、HMR实现

3.1 工作原理

本小节内容都不是人话,可跳过~

一、应用程序(Applicationn):

  1. 应用程序在获取到socket服务器发送的更新信息
  2. 检查HMR的运行状态
  3. HMR运行环境异步下载更新内容并通知应用程序
  4. 应用程序告诉HMR运行环境去应用更新
  5. HMR运行环境应用变更信息

二、编译部分(Compiler):

修改文件以后,编译器触发更新操作,更新两部分内容:manifestchunks

manifest包含了新的变化的所有模块的代码块,编译器确保module IDschunk IDs在本次构建中

三、模块(Module)

只影响HMR中包含了的模块(module.hot.accept()),比如:style-loader,其中实现了HMR接口,所以在样式更新的时候,新样式替换老样式

如果模块没有HMR的处理,则更新操作持续冒泡,也就是模块树中只要单个模块发生了更新,整个都会重新加载

四、运行过程(Runtime)

运行过程会持续使用checkapply来进行module.hot.staus的更新

check: 请求更新清单,请求成功后,比较更新的内容和当前的内容,将所有更新的内容进行存储直到所有内容更下载完成,切换到ready状态准备

apply: 将updated的模块标记为invalid,一直冒泡标记到entry point,所有无效模块处理完成后,更新所有的accepthandlers都会被触发,然后状态切换为idle状态

3.2 原理解析

查看bundle.js中的代码,将3.1中的内容翻译一下:

  1. webpack-dev-server配置了hot:true的基础上,bundle.js中的reloadApp()会进行HMR的逻辑(另一个逻辑是之前面的自动刷新),这个时候会开始判断HMRstatus,并发起一个AJAX请求chunk.hot-update.json,该请求会返回一个JSON对象
{c: {main: true},h: nextChunk
}
  1. 请求成功以后,继续更新HMR的status,会比较返回的nextChunk和当前的chunk是否相同,如果相同结束HMR,如果不同将nextChunk存储并继续进行HMR
  2. 根据chunk拼接<script>的src,并将<script>动态插入html的<head>中(比如我的配置就是:main.chunk.hot-update.js),操作后chunk数组序号+1
  3. 由于<head>插入了新的脚本<script src="main.chunk.hot-update.js"></script>,实现动态更新JS而不刷新页面

补充:对于添加了module.hot.dispose的部分,创建了全局对象data={}来保存数据,从而实现重新加载JS以后,仍然可以访问该数值的内容

3. 总结

HMR的核心点有两个:

  1. 如何在服务端文件变化之后通知客户端?使用socket
  2. 如何判断文件是否发生变化?客户端和服务端都保存chunk,比较是否发生变化

webpack的内容其实真的有点多,仅仅HMR包含的内容就很多,自己的总结也不够完整,只能说是刚好入门。不过整个过程一边自己猜想,一遍跟源码,然后验证自己猜想,真的是很有意思的过程。

4. 参考

webpack官方文档:

HMR

HMR API

Dev Server

CSDN上一篇特别棒的资料:

彻底弄懂webpack-dev-server的热更新

转载于:https://juejin.im/post/5c86ec276fb9a04a10301f5b

Webpack 自动刷新和HMR相关推荐

  1. 深入理解webpack自动刷新浏览器

    文章目录 1. 自动刷新浏览器 1.1 监听文件变化 1.1.1 方式 1.1.2 原理 1.2 自动刷新浏览器 1.2.1 原理 2. 模块热更新 2.1 两种方式 2.1.1 webpack-de ...

  2. Express结合Webpack的全栈自动刷新

    在以前的一篇文章BrowserSync,迅捷从免F5开始中,我介绍了BrowserSync这样一个出色的开发工具.通过BrowserSync我感受到了这样一个理念:如果在一次ctrl + s保存后可以 ...

  3. Webpack 2 视频教程 007 - 配置 WDS 进行浏览器自动刷新

    原文发表于我的技术博客 这是我免费发布的高质量超清「Webpack 2 视频教程」. Webpack 作为目前前端开发必备的框架,Webpack 发布了 2.0 版本,此视频就是基于 2.0 的版本讲 ...

  4. vuejs webpack模板里import路径中@符号是什么意思以及不能自动刷新解决方法

    问: 用vuejs的webpack模板生成的项目中,router/index.js里面有一句: import Hello from '@/components/Hello' 这里路径前面的" ...

  5. webpack配置---设置快捷打包和浏览器自动刷新

    1.设置快捷打包 找到package.json中scripts 之后再输入命令npm run build 2.设置浏览器自动刷新 要先安装html-webpack-plugin这个插件再配置 3.将c ...

  6. webpack4.x热更新,自动刷新

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

  7. [vue-cli]在使用vue-cli开发vue项目时,自动刷新页面的原理你了解吗?

    [vue-cli]在使用vue-cli开发vue项目时,自动刷新页面的原理你了解吗? 自动刷新页面并不是vue-cli的功能,而是webpack的hot-module-replacement-plug ...

  8. 结合 live-reload 实现自动刷新

    结合 live-reload 实现自动刷新 前端工程师日常开发最频繁(实际上最浪费时间)的操作是什么?可能你已经想到了,就是刷新页面,要让变更生效,需要重新加载,刷新页面的操作就变成了重复低效的操作. ...

  9. hmr webpack 不编译_webpack hmr

    参考: hmr技术支持程序运行时的模块(amd.commonJS等)的修改.添加和删除,而不用整个程序重新加载,这可以提升开发的效率: hmr后程序的状态可以得到保存 仅仅改变变化的部分,其余不变 调 ...

最新文章

  1. c# 自定义控件使其填充方格且自动变换大小
  2. 和平精英清明节服务器维修时间,和平精英体验服关服维护要多久 和平精英体验服什么时候开放...
  3. 关于异常“The 'Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine”的处理...
  4. 数据结构之图:加权有向图与dijkstra算法找到最短路径,Python——28
  5. 不拥抱算法的张小龙,还能带着微信继续避免失败?
  6. tensorflow中协调器 tf.train.Coordinator 和入队线程启动器 tf.train.start_queue_runners
  7. NPOI SetColumnHidden隐藏列不起作用的原因
  8. afudos备份bios不动_afudos备份bios不动_老鸟经验之谈刷坏BIOS后的2种恢复方法
  9. JMeter并发测试工具
  10. 科创板发行上市审核44个问题解答汇编(总11期)
  11. 纵享丝滑滑动切换的周月日历,水滴效果,可高度定制,仿小米日历
  12. 【花雕动手做】有趣好玩的音乐可视化系列小项目(22)--LED无限魔方
  13. 树莓派自己加内存条_拔掉MacBookPro,用8GB树莓派4工作一天,体验原来是这样的...
  14. async-profiler的使用与RocketMQ性能优化案例
  15. vbnet 直线 弧形在autoCad中的画法
  16. 如何截取音频片段并制作成手机铃声
  17. IG赢了,让我们先理直气壮的喊出那句 我们是冠军!
  18. 2014年度总结——软件产品化的简要理解
  19. 区块链NFT之OpenSea
  20. 《大话Java性能优化》面向对象及基础类型相关部分

热门文章

  1. js计算两点之间距离
  2. ISO9001内审员试卷(转载)
  3. $.parseJSON的使用规则
  4. AMD64与IA64的区别
  5. 语义分割的评价指标(OA, F1 score)计算(不使用sklearn)
  6. S5PV210的开发板刷机
  7. 全网最全,华为可信专业级认证介绍
  8. 几年前担任一个大型项目的项目经理工作职责
  9. python线上培训班5月是淡季
  10. FFmpeg源代码:avformat_write_header/av_write_frame/av_interleaved_write_frame/av_write_trailer