Web前端熱更新

tiu5發表於2017-11-06

熱更新是什麼?

簡單來說,熱更新一般是指手機裡的app有小規模更新,以直接打補丁的形式更新。
相對應的,另一種更新方式就是下載新的安裝包,重新安裝。
所以熱更新在手遊裡頭是比較常見的,畢竟遊戲應用個個都幾百兆起步。

那Web前端有熱更新?

按上面那個說法,Web應該是不存在熱更新的。
因為網頁的架構是B/S,即 瀏覽器+伺服器 , 它不像手機app一樣是 C/S 客戶端+伺服器
所以在網頁這一塊是無法推送補丁讓瀏覽器去更新的。

那為什麼我搜前端熱更新有好多文章在講?

網上大多數前端熱更新講的都是熱載入 hot-loader 或者是模組熱更替 HMR

熱載入是什麼?

  • 問題背景
    前端頁面是由 HTML+CSS+JavaScript 組成的,我們前端開發頁面除錯的時候,
    一般是這樣:編輯器修改儲存 --> 切換瀏覽器重新整理
    ps:所以前端汪F5鍵是用得最多的。
  • 怎麼解決
    能不能我這邊修改完,瀏覽器就自動重新載入那些修改過的檔案?
    想要解決這個問題,那麼我們就要去檢測程式碼檔案是否修改了,然後通知瀏覽器去重新載入。
    那我們就需要一個瀏覽器與伺服器之間的通訊機制。
    在早期,瀏覽器與伺服器間的通訊機制就只有http協議,可http協議是無狀態協議,這就很尷尬了,而且伺服器無法主動給瀏覽器發訊息,那就能瀏覽器不斷跟伺服器請求。
  • 還好在HTML5裡頭新增了websocket
    websocket 是一種允許瀏覽器與伺服器間建立tcp長連結的通訊機制
    tcp協議:雙向通訊,有狀態
    這樣的話,我們就可以通過伺服器檢測檔案修改,有修改了,我們就通知瀏覽器,沒有我們就不通知。

盜圖
盜圖

所以大概流程就如下:

  • 伺服器檢測程式碼是否修改了
  • 修改了通知瀏覽器
  • 瀏覽器根據修改的檔案情況選擇區域性重新整理或全域性重新整理

具體例項

試一下

  • 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
    所以當你修改了檔案時,它只是重新打包了,並通知瀏覽器過載一遍頁面。

執行webpack-dev-server
執行webpack-dev-server

$ 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>複製程式碼

實際頁面index.html程式碼
實際頁面index.html程式碼

這樣應該很清晰了吧
首先入口檔案main.js引入了hello.jsmain.css
然後hello.js又引入了style.csstest.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檔案,頁面只是區域性重新整理

多了的js檔案其實就是一些樣式的更替
多了的js檔案其實就是一些樣式的更替

  • 那是不是用了HMR就可以實現任何修改都是區域性重新整理呢?
    不是的。
    如果只是修改了CSS樣式,那可以通過JS以打補丁的形式去進行替換樣式。而且也不是說改樣式就都能實現區域性重新整理,如果你改的是style.css這個檔案,它的結果是會重新載入頁面,為什麼呢?因為style.css這裡頭用到了CSS模組的東西,所以沒辦法說直接打個補丁就能搞定。

結論

熱載入這個東西,首要就是靠伺服器與瀏覽器之間的通訊,有了通訊才能通知瀏覽器什麼時候去重新整理,而重新整理又分全域性和區域性,這個要看服務端改了哪些程式碼檔案,而這些檔案如果可以區域性重新整理就區域性重新整理,不行的話就只能重新載入頁面了。

參考資料

前端開發熱更新原理解讀

相關文章