简单聊聊前端开发中的热更新原理
背景
前端项目开发过程中热更新的机制大家都知道,不知道你在开发的时候是否做了这方面的配置。
相信接触最多的就是 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原创全栈干货+每周三深度技术文章
相关推荐
background-color: blue;background-color: yellow;<input type="button" value="变蓝" @click="changeColorT