背景
前端項目開發過程中熱更新的機制大家都知道,不知道你在開發的時候是否做了這方面的配置。
相信接觸最多的就是 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原創全棧乾貨+每週三深度技術文章