熱更新是什麼?
簡單來說,熱更新一般是指手機裡的app有小規模更新,以直接打補丁的形式更新。
相對應的,另一種更新方式就是下載新的安裝包,重新安裝。
所以熱更新在手遊裡頭是比較常見的,畢竟遊戲應用個個都幾百兆起步。
那Web前端有熱更新?
按上面那個說法,Web應該是不存在熱更新的。
因為網頁的架構是B/S,即 瀏覽器+伺服器 , 它不像手機app一樣是 C/S 客戶端+伺服器
所以在網頁這一塊是無法推送補丁讓瀏覽器去更新的。
那為什麼我搜前端熱更新有好多文章在講?
網上大多數前端熱更新講的都是熱載入 hot-loader 或者是模組熱更替 HMR
熱載入是什麼?
- 問題背景
前端頁面是由 HTML+CSS+JavaScript 組成的,我們前端開發頁面除錯的時候,
一般是這樣:編輯器修改儲存 --> 切換瀏覽器重新整理
ps:所以前端汪F5鍵是用得最多的。 - 怎麼解決
能不能我這邊修改完,瀏覽器就自動重新載入那些修改過的檔案?
想要解決這個問題,那麼我們就要去檢測程式碼檔案是否修改了,然後通知瀏覽器去重新載入。
那我們就需要一個瀏覽器與伺服器之間的通訊機制。
在早期,瀏覽器與伺服器間的通訊機制就只有http協議,可http協議是無狀態協議,這就很尷尬了,而且伺服器無法主動給瀏覽器發訊息,那就能瀏覽器不斷跟伺服器請求。 - 還好在HTML5裡頭新增了websocket
websocket 是一種允許瀏覽器與伺服器間建立tcp長連結的通訊機制
tcp協議:雙向通訊,有狀態
這樣的話,我們就可以通過伺服器檢測檔案修改,有修改了,我們就通知瀏覽器,沒有我們就不通知。
所以大概流程就如下:
- 伺服器檢測程式碼是否修改了
- 修改了通知瀏覽器
- 瀏覽器根據修改的檔案情況選擇區域性重新整理或全域性重新整理
具體例項
- vue-loader
- webpack-dev-server
根據配置可以開啟HMR模組
官方文件:doc.webpack-china.org/guides/hot-…
個人小demo: gitee.com/gitzt/webpa…
試一下
- package.json 配置檔案
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack", "server": "webpack-dev-server --open", "server-hot": "webpack-dev-server --hot" },複製程式碼
- webpack.config.js 配置檔案
所以我們在// 本地開發伺服器 devServer: { // 本地伺服器載入資源所在路徑 contentBase: "./public", // true 表示所有跳轉都是index.html historyApiFallback: true, // 當原始檔修改時,自動重新整理頁面 inline: true, // 埠號,若不設定,預設為8080 port: 3000, },複製程式碼
webpack-demo
目錄下,執行npm run server
就等於執行了webpack-dev-server --open
這樣我們就啟用了webpack-dev-server
但是我們配置項沒有配置啟用HMR
所以當你修改了檔案時,它只是重新打包了,並通知瀏覽器過載一遍頁面。
$ npm run server
是我執行的命令,根據package.json 配置檔案這個命令等同於webpack-dev-server --open
下面那些輸出語句,一些是webpack-dev-server
啟用狀態,還有webpack
的打包檔案的打包過程
為什麼輸出的是這個?
- webpack.config.js 配置檔案
module.exports = { // 入口檔案 entry: __dirname + "/app/main.js", // 輸入檔案 output: { // 輸出路徑 path: __dirname + "/public", // 輸出檔名 filename: "bundle.js" },複製程式碼
/app/main.js
檔案// ES6匯入模組的語法,所以入口檔案跟下面兩個檔案有關聯 import hello from "./hello"; import "./main.css"; // 簡單的DOM操作 document.querySelector("#root").appendChild(hello());複製程式碼
/app/hello.js
檔案import style from "./style.css"; // 下面這個是CommonJS的檔案匯入,因為node.js本身是支援CommonJS的 let test = require("./test.json"); // 這個是CommonJS的模組匯出 module.exports = function () { let hello = document.createElement('div'); hello.textContent = test.hhh; // 這裡的hello這個元素的class的取值是來自上面匯入"./style.css"檔案,這涉及到CSS模組 hello.className = style.hello; return hello; };複製程式碼
/app/main.css
檔案body{ color: blue; font-size: 64px; }複製程式碼
/app/style.css
檔案.hello{ color: red; }複製程式碼
/app/test.json
檔案{ "hhh":"this a message from .json" }複製程式碼
哦,還有
index.html
檔案<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>webpack demo</title> </head> <body> <div id="root"> </div> <script src="bundle.js"></script> </body> </html>複製程式碼
這樣應該很清晰了吧
首先入口檔案main.js
引入了hello.js
和main.css
然後hello.js
又引入了style.css
和test.json
所以上面這些與入口檔案有直接聯絡或間接聯絡的檔案都會通過webpack去打包,輸出為一個bundle.js
而index.html
是引用了bundle.js
檔案的,所以就出現了這個效果。
你好像沒講到webpack-dev-server的作用哦
- 我們上面執行的是沒有啟用HMR,所以這時候我們修改
main.css
檔案body{ color: blue; /* font-size: 64px; */ } 你會看到終端webpack在重新打包,打包完後瀏覽器自動重新整理了頁面,然後字型變成預設大小複製程式碼
- 如果啟用了HMR呢?
這時候我們要用到的命令是$ npm run server-hot
同樣修改main.css
檔案,我們會發現字型大小變了,可是瀏覽器並沒有重新整理,而且發現改一次就多一個js檔案,頁面只是區域性重新整理
- 那是不是用了HMR就可以實現任何修改都是區域性重新整理呢?
不是的。
如果只是修改了CSS樣式,那可以通過JS以打補丁的形式去進行替換樣式。而且也不是說改樣式就都能實現區域性重新整理,如果你改的是style.css
這個檔案,它的結果是會重新載入頁面,為什麼呢?因為style.css
這裡頭用到了CSS模組的東西,所以沒辦法說直接打個補丁就能搞定。
結論
熱載入這個東西,首要就是靠伺服器與瀏覽器之間的通訊,有了通訊才能通知瀏覽器什麼時候去重新整理,而重新整理又分全域性和區域性,這個要看服務端改了哪些程式碼檔案,而這些檔案如果可以區域性重新整理就區域性重新整理,不行的話就只能重新載入頁面了。