摘要:背景前端项目开发过程中热更新的机制大家都知道,不知道你在开发的时候是否做了这方面的配置。其实热更新的原理并不复杂,或者说很简单。服务器和浏览器规定好消息的规则,是刷新页面还是更新。另外对模块热更新和原理有兴趣的可以研究下,后面可能也会介绍。
背景
前端项目开发过程中热更新的机制大家都知道,不知道你在开发的时候是否做了这方面的配置。
相信接触最多的就是 webpack 的热更新,文件保存后页面自动刷新,或者 css 自动更新,页面的样式在不刷新页面的情况下就会更新。
还有就是模块热替换。
热更新机制很好玩,能提升不少开发效率,但是只是处于会用的阶段不是我们的目的,我们应该适当的深入学习下,看看他背后的原理,一个是否思考过,一个是否能自己实现。
热更新原理咱们这里主要说下怎样自己实现一个热更新,也就是文件更改了会自动刷新页面,可以同步 pc 和 移动端,css 更改了可以不刷新页面就应用最新的 css。
其实热更新的原理并不复杂,或者说很简单。
咱们一步一步的分析下。
本文不是要告诉你一些 api如何使用,而是利用架构的思维去分析和解决问题。
【分析】
文件内容变更了,浏览器是怎么知道的呢?
css 文件内容变更了,没有刷新页面 怎么加载最新的内容呢?
只要解决了上面两个问题,我们就算是完成了。因为剩下得就是编码了,这都好说。
【结果】
文件变更了,我怎样通知浏览器?
浏览器和服务器保持着连接。 服务器有什么事儿直接通过当前的链接告诉浏览器就可以了。
连接肯定是长连接,不然怎么实时通信。
保持长连接有哪些方法呢? 轮询?eventSorce? 都不够好。
有么有更好的方案呢?那就是 - websocket
浏览器和服务器先建立好链接,服务器就可以直接通知到客户端了。这个时候无论是 pc 上还是手机上都可以随时根据需要刷新或者加载资源。
css 更新,css 本身是可以通过 dom 去操作的。浏览器只要知道是 css更新了,直接重新加载当前的 css 文件就可以了。
架构思维咱们在重新捋捋这个架构。
服务器和浏览器通过 websocket 建立链接。
服务器和浏览器规定好消息的规则,是刷新页面还是更新 css。
基本架构有了,其他的就是编码实现了。
服务端使用 node 创建一个 ws 服务。
浏览器使用 websocket 创建一个链接和服务器进行链接。
双方通过对应的 api 进行数据的操作。
代码实现本文只是讲解下思路,并没有实现文件的监听,文件监听后面会介绍。咱暂时先确定好两个消息规则:
浏览器收到 命令为:htmlFileChange ,此时浏览器刷新;
浏览器收到命令为:cssFileChange,此时不刷新页面,自动加载 css 文件;
具体代码如下:
服务端://web-socket.js 创建 ws 服务 var ws = require("nodejs-websocket");//需要安装这个包 module.exports = function(){ return function () { console.log("重度前端提醒,开始建立连接...") var sessions = [];//存放每一个链接对象 var server = ws.createServer(function (conn) { sessions.push(conn);//将新的链接对象存放在数组中 conn.on("text", function (str) { console.log("收到的信息为:" + str) sessions.forEach(item=>{ item.sendText(str) //所有客户端都发送消息 }); }); conn.on("close", function (code, reason) { console.log("关闭连接") }); conn.on("error", function (code, reason) { console.log("异常关闭") }); }).listen(6152) console.log("WebSocket建立完毕") } }
//server.js http 服务代码
let http = require("http"); let fs = require("fs"); let webSocket = require("./node/web-socket"); const BASEROOT = process.cwd();//获得当前的执行路径 //读取 index.html内容 let getPageHtml = function () { let data = fs.readFileSync(BASEROOT+"/html/index.html"); return data.toString(); } //读取 index.css内容 let getPageCss = function () { let data = fs.readFileSync(BASEROOT + "/html/index.css"); return data.toString(); } //node 端 开启 ws 服务 webSocket()(); http.createServer(function (req, res) {//创建 http 服务 let body = "",url = req.url; req.on("data", function (chunk) { body += chunk; }); req.on("end", function () { //路由简单处理 根据不同路径输出不同内容给浏览器 if(url.indexOf("/index.css")>-1){ res.write(getPageCss()); }else{ res.write(getPageHtml()); } res.end(); }); }).listen(6151); console.log("重度前端提醒...... server start");页面截图 客户端
//index.html 布局代码省略 const nick = ["a", "b", "c", "d", "e", "f", "g", "aa", "cc"]; let index = 0; // Create WebSocket connection. const socket = new WebSocket("ws://10.70.69.191:6152"); // Connection opened socket.addEventListener("open", function (event) { socket.send(navigator.userAgent); }); // 监听服务器推送的消息 socket.addEventListener("message", function (event) { if (index > nick.length) { index = 0;//只是为了每次输出不同的昵称,没实际意义 } var ele = document.createElement("div"); ele.innerHTML = nick[index] + ":" + event.data; if (event.data === "htmlFileChange") { //html 文件更新了 刷新当前页面 location.reload(); } if (event.data === "cssFileChange") { //css 文件更新了 刷新当前页面 reloadCss(); } document.getElementById("content").append(ele); index += 1; }); //重新加载 css function reloadCss() { var cssUrl = [], links = document.getElementsByTagName("link"), len = links.length; for (var i = 0; i < len; i++) { var url = links[i].href; document.getElementsByTagName("head")[0].appendChild(getLinkNode(url)); //创建新的 css 标签 document.getElementsByTagName("head")[0].removeChild(links[i]); //移除原有 css } console.log(document.getElementsByTagName("head")[0]) function getLinkNode(cssUrl) { var node = document.createElement("link"); node.href = cssUrl; node.rel = "stylesheet"; return node; } } document.getElementById("btn1").onclick = function () { socket.send(document.getElementById("message").value); document.getElementById("message").value = ""; }
index.css 内容
input { outline: none; } #content { height: 400px; width: 400px; border: solid 1px #ccc; color: red; }
代码倒是次要的。解决问题的思路才重要。有了解决问题的架构思维,代码实现都好说。
写到这里咱们还能顺便实现一个群聊。
本质就是服务器和浏览器怎样实时通信,解决了这个问题,其他的都是小事儿。
这个技术实现还是比较简单的。
另外对模块热更新和 websocket 原理有兴趣的可以研究下,后面可能也会介绍。
总结本文主要介绍
简易版热更新的原理;
热更新实现思路和代码实现;
架构思维:简单的带出架构思维的作用;
希望本文对你有用。
原创不易、请多鼓励
自家观点、欢迎打脸
https://github.com/bigerfe/ho...
作者:微信公众号 - 重度前端 主笔:八门
欢迎关注 重度前端-每周5原创全栈干货+每周三深度技术文章
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/101478.html
摘要:从事开发的程序员,对于前后端分离模式多半不陌生,这也是目前主流的开发模式,具体关于前后端分离的模式可以参看文章你不得不了解的前后端分离原理,在这里写者不进行说明。原理图如下,前后端在一个进程同一个端口中,通过热替换更新的,而不是全量重启。 从事 Web 开发的程序员,对于前后端分离模式多半不陌生,这也是目前主流的 Web 开发模式,具体关于前后端分离的模式可以参看文章《你不得不了解的前...
摘要:联想到我在微信小程序上的开发体验,真心觉得如果有热更新机制的话,开发效率要高很多。热更新示例下面通过例子来进一步解释热更新机制。 想必作为前端大佬的你,工作中应该用过 webpack,并且对热更新的特性也有了解。如果没有,当然也没关系。 下面我要讲的,是我对 Webpack 热更新机制的一些认识和理解,不足之处,欢迎指正。 首先: 热更新是啥? 热更新,是指 Hot Module Re...
摘要:如果你的项目中使用了的话,你会很幸运,借助插件可以实现项目的热更新。对模板更新的处理目前项目中使用的是的模板引擎。 showImg(https://segmentfault.com/img/bVrAa7);这个是组内一位同学在平时开发中,发现调试不便,为团队开发的热更新工具。很厉害,文章中的技术实现内容也是我了解了他的具体实现思路后,整理出来的。 工具源码EHU(esl-hot-upd...
阅读 2721·2023-04-26 02:28
阅读 2549·2021-09-27 13:36
阅读 3122·2021-09-03 10:29
阅读 2750·2021-08-26 14:14
阅读 2100·2019-08-30 15:56
阅读 830·2019-08-29 13:46
阅读 2608·2019-08-29 13:15
阅读 454·2019-08-29 11:29