我们大致了解了webpack HMR 原理 。可以看出以下几点核心思想:
1、监听文件变化
2、服务器与客户端通信
3、替换流程
4、降级操作
当然 。由于 webpack 本身有个很成熟的模块思想和生态 。因此整个架构设计会比我们实现的 HMR 复杂很多 。在模块热替换中 。是由 webpack 的全部流程出力来完成这一操作的 。而并没有局限于 webpack-dev-server 和 webpack 以及业务代码本身 。实际上 。起到更重要作用的是各类 loader 。它们需要使用 HMR API 来实现 Hot Reload 的逻辑 。决定什么时候注册模块、什么时候卸载模块;如何注册和卸载模块 。而 webpack 本身更像是一个调用方的角色 。不需要考虑具体的注册和反注册逻辑 。
HMR 的核心组织
经过了上面的分析 。我们基本上确认了一个思路 。也就是分析 webpack HMR 得出的结论 。但是由于我们只有 runtime 。所以实现 Hot Reload 变成了一个下图的简单流程:
1、Server 启动一个 HTTP 服务器 。并且注册和启动 WebSocket 服务 。用于届时与客户端通信
2、在启动 Static 服务器后返回页面前注入 HMR 的客户端代码 。业务方无需关心 HMR 的具体实现和添加对应的支持代码服务端监听磁盘文件的变更 。将文件变更通过 WebSocket 发送给客户端
3、客户端收到文件变更消息后进行对应的模块处理
4、(模块处理失败 。降级为 Live Reload)
live reload?
在实现 HMR 之前 。我们可以先实现一个简单的 Live Reload 来保证我们 1-3 步的实现没有异常 。
constKoa=require('koa')constWebSocket=require('ws')constchokidar=require('chokidar')constapp=newKoa()constfs=require('fs').promisesconstwss=newWebSocket.Server({port:8000})constdir='./static'constwatcher=chokidar.watch('./static',{ignored:/node_modules|.git|[/\]./})wss.on('connection',(ws)=>{watcher.on('add',path=>console.log(`File${path}added`)).on('change',path=>console.log(`File${path}hasbeenchanged`)).on('unlink',path=>console.log(`File${path}hasbeenmoved`)).on('all',async(event,path)=>{//SimpleLiveReloadws.send('reload')})ws.on('message',(message)=>{console.log('received:%s',message)})ws.send('HMRClientisReady')})constinjectedData=https://www.wangchuang8.com/``app.use(async(ctx,next)=>{letfile=ctx.pathif(ctx.path.endsWith('/')){file=ctx.path+'index.html'}letbodytry{body=awaitfs.readFile(dir+file,{encoding:'utf-8'})}catch(e){ctx.status=404returnnext()}if(file.endsWith('.html'))body=body.replace('
手机看代码不方便 。我把代码截图贴这里了
文章插图
文章插图
上述代码中 。简单的使用了 chokidar 这个文件监听库 。它极大的减轻了我们的工作量;而 WebSocket 和服务器的实现上暂不赘述 。之所以不直接使用 koa-static 的原因是因为我们需要对于 HTML 文件进行一些注入操作 。以上 Live Reload 的实现非常简单 。基本可以总结为一句话:得知文件变化后向客户端发送 reload 消息 。客户端收到消息执行页面刷新操作 。
实现了一个 Live Reload 之后 。接下来我们只需要变更注入的代码和发送到客户端的消息两个部分即可 。其实 Hot Reload 和 Live Reload 最大的区别也就是「最小模块替换」与「刷新页面」的区别 。因此其他部分都是不用变动的 。
替换 HTML 和 CSS 则是其中最简单的两项任务 。
HTML
通常来说 。我们要覆盖 HTML 中的内容 。除了刷新这一操作外 。还有一个就是 document.write() 。实际上我们也是通过这个函数来实现 HTML 的 Hot Reload 的:
//监听.on('all',async(event,path)=>{if(path.endsWith('.html')){body=awaitfs.readFile(path,{encoding:'utf-8'})constmessage=jsON.stringify({type:'html',content:body})ws.send(message)}})//注入letdata=https://www.wangchuang8.com/{}try{data=JSON.parse(event.data)}catch(e){//return}console.log(data)if(data.type==='html'){document.write(data.content);document.close();console.log('[HMR]updatedHTML');}
文章插图
推荐阅读
- 长这么大让你做的最尴尬的事是什么,至今想起来都觉得特别丢人呢?
- 有哪些时间长的适合情侣看的电影?
- 你经历过什么尴尬事?
- 金铲铲之战仙翰的祝福活动攻略 仙翰的祝福任务怎么做
- 男 第一次和女朋友在一起看电影,什么电影合适呢?可以留下回忆的那种?
- 女性被强奸后,身体受的损伤该怎么办?
- 现在什么工作最赚钱 虽然辛苦却能月入过万的五大职业
- 爱在七夕,有什么电影可以推荐一下?
- 街头霸王2摇杆如何放技能 街霸2出招表摇杆教学