webpack 實現 HMR 及其實現原理

前言

在這以前我老是容易把熱重載和模塊熱替換混淆成一個概念,在本身動手實現以後才發現二者仍是有一些差異的。javascript

  • 熱加載能夠經過開啓webpack-dev-server來實現,每次修改代碼都會刷新整個頁面
  • 模塊熱替換也稱爲HMR,代碼更新時只會更新被修改部分都顯示 HMR相比熱加載的好處:
  • 針對於樣式調試更加方便
  • 只會更新被修改代碼的那部分顯示,提高開發效率
  • 保留在徹底從新加載頁面時丟失的應用程序狀態。

實現HMR

HMR的有兩種實現方式,一種是經過插件HotModuleReplacementPlugin和devserver配和實現,一種是經過在自定義開發服務下,使用插件webpack-dev-middleware和webpack-Hot-middleware配合實現HMRjava

經過插件HotModuleReplacementPlugin()

1.配置 在webpack.config.js中配置devServernode

devServer: {
		contentBase: './dist',  // 起一個在dist文件夾下的服務器
		open: true,  // 自動打開瀏覽器並訪問服務器地址
		port: 8085,
                hot: true,      // 開啓HMR功能
                 hotOnly: true   // 即便HMR不生效,也不自動刷新
	},
複製代碼

pluginsp配置中使用HotModuleReplacementPlugin插件webpack

plugins: [
   ...// 其餘插件
    new webpack.HotModuleReplacementPlugin()
    ],
複製代碼

2.判斷 而後進行手動判斷進行模塊熱更新,若是你不想作如下判斷那麼可使用module.hot.accept(),整個項目作hmr只要有代碼變化就進行更新。git

if(module.hot) {
    module.hot.accept('./number', () => {
      // 使用更新過的 library 模塊執行某些操做...
    })
}
複製代碼

3.啓動 最重要一點不要忘了修改啓動命令github

"start": "webpack-dev-server"
複製代碼

此時運行npm start,便可實現模塊熱更新web

經過 Node.js API

經過本身在本地搭建一個服務器,利用webpack-dev-middleware和webpack-Hot-middleware兩個插件來配合實現HMR. 1.安裝express

// 安裝express, webpack-dev-middleware , webpack-Hot-middleware
cnpm install express webpack-dev-middleware webpack-Hot-middleware -D
複製代碼

2.配置dev.server.js dev.server.jsnpm

const path = require('path');
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require("webpack-Hot-middleware")
const config = require('./webpack.dev.js');

const complier = webpack(config);   // 編譯器,編譯器執行一次就會從新打包一下代碼
const app = express();  // 生成一個實例
const {devServer: {port, contentBase}} = config
const DIST_DIR = path.resolve(__dirname, '../', contentBase);  // 設置靜態訪問文件路徑
// 等同於const DIST_DIR = path.resolve(__dirname, '../dist');


let devMiddleware = webpackDevMiddleware(complier, {  // 使用編譯器
    publicPath: config.output.publicPath,
    quiet: true, //向控制檯顯示任何內容
    noInfo: true
})

let hotMiddleware = webpackHotMiddleware(complier,{
    log: false,
    heartbeat: 2000
 })

app.use(devMiddleware)

app.use(hotMiddleware)

// 設置訪問靜態文件的路徑
app.use(express.static(DIST_DIR))


app.listen(port, () => {
    console.log("成功啓動:localhost:"+ port)
})  //監聽端口
複製代碼

webpack.dev.js配置json

module.exports = {
        entry: {             // 入口文件配置
        //實現刷新瀏覽器webpack-hot-middleware/client?noInfo=true&reload=true 是必填的
        main: ['webpack-hot-middleware/client?noInfo=true&reload=true', './src/index.js']
    },
       devServer: {
        contentBase: 'dist',
        port: 8081
    },
       plugins: [  
        new webpack.NamedModulesPlugin(),  //用於啓動HMR時能夠顯示模塊的相對路徑
        new webpack.HotModuleReplacementPlugin(), 
        new OpenBrowserPlugin({ url: 'http://localhost:8081' }), // 自動打開瀏覽器
    ],
    ...// 其餘配置
}
複製代碼

完整實如今這裏

webpack-hot-middleware的配置項

配置項能夠經過query 方式添加到webpack config中的路徑來傳遞給客戶端 配置項都有

  • path - 中間件爲事件流提供的路徑
  • name - 捆綁名稱,專門用於多編譯器模式
  • timeout - 嘗試從新鏈接後斷開鏈接後等待的時間
  • overlay - 設置爲false禁用基於DOM的客戶端覆蓋。
  • reload - 設置爲true在Webpack卡住時自動從新加載頁面。
  • noInfo - 設置爲true禁用信息控制檯日誌記錄。
  • quiet - 設置爲true禁用全部控制檯日誌記錄。
  • dynamicPublicPath - 設置爲true使用webpack publicPath做爲前綴path。(咱們能夠__webpack_public_path__在入口點的運行時動態設置,參見output.- publicPath的註釋)
  • autoConnect - 設置爲false用於防止從客戶端自動打開鏈接到Webpack後端 - 若是須要使用該setOptionsAndConnect功能修改選項

經過傳遞第二個參數,能夠將配置選項傳遞給中間件

webpackHotMiddleware(webpack,{
    log: false,
    path: "/__what",
    heartbeat: 2000
})
複製代碼
  • log - 用於記錄行的函數,傳遞false到禁用。默認爲console.log
  • path - 中間件將服務事件流的路徑必須與客戶端設置相匹配
  • heartbeat - 多長時間將心跳更新發送到客戶端以保持鏈接的活動。應小於客戶的timeout設置 - 一般設置爲其一半值。 更多配置在這裏webpack-hot-middleware

注意:經過express啓動服務器後,devServer中的配置就不起做用了。

3.啓動命令

"start": "node ./build/dev-server.js",
複製代碼

啓動命令npm start,便可實現HMR的功能

HMR實現原理

1.HMR的更新流程

  • 修改了一個或多個文件。
  • 文件系統接收更改並通知Webpack。
  • Webpack從新編譯構建一個或多個模塊,並通知HMR服務器進行了更新。
  • HMR Server使用websockets通知HMR Runtime須要更新。(HMR運行時經過HTTP請求這些更新。)
  • HMR運行時再替換更新中的模塊。若是肯定這些模塊沒法更新,則觸發整個頁面刷新

2.HMR 工做流程圖解 此爲更加詳細的流程分析:

image
上圖是webpack 配合 webpack-dev-server 進行應用開發的模塊熱更新流程圖。

  • 上圖底部紅色框內是服務端,而上面的橙色框是瀏覽器端。
  • 綠色的方框是 webpack 代碼控制的區域。藍色方框是 webpack-dev-server 代碼控制的區域,洋紅色的方框是文件系統,文件修改後的變化就發生在這,而青色的方框是應用自己 步驟分析:
  • 第一步,在 webpack 的 watch 模式下,文件系統中某一個文件發生修改,webpack 監聽到文件變化,根據配置文件對模塊從新編譯打包,並將打包後的代碼經過簡單的 JavaScript 對象保存在內存中。
  • 第二步是 webpack-dev-server 和 webpack 之間的接口交互,而在這一步,主要是 dev-server 的中間件 webpack-dev-middleware 和 webpack 之間的交互,webpack-dev-middleware 調用 webpack 暴露的 API對代碼變化進行監控,而且告訴 webpack,將代碼打包到內存中。
  • 第三步是 webpack-dev-server 對文件變化的一個監控,這一步不一樣於第一步,並非監控代碼變化從新打包。當咱們在配置文件中配置了devServer.watchContentBase 爲 true 的時候,Server 會監聽這些配置文件夾中靜態文件的變化,變化後會通知瀏覽器端對應用進行 live reload。注意,這兒是瀏覽器刷新,和 HMR 是兩個概念。
  • 第四步也是 webpack-dev-server 代碼的工做,該步驟主要是經過 sockjs(webpack-dev-server 的依賴)在瀏覽器端和服務端之間創建一個 websocket 長鏈接,將 webpack 編譯打包的各個階段的狀態信息告知瀏覽器端,同時也包括第三步中 Server 監聽靜態文件變化的信息。瀏覽器端根據這些 socket 消息進行不一樣的操做。固然服務端傳遞的最主要信息仍是新模塊的 hash 值,後面的步驟根據這一 hash 值來進行模塊熱替換。
  • webpack-dev-server/client 端並不可以請求更新的代碼,也不會執行熱更模塊操做,而把這些工做又交回給了 webpack,webpack/hot/dev-server 的工做就是根據 webpack-dev-server/client 傳給它的信息以及 dev-server 的配置決定是刷新瀏覽器呢仍是進行模塊熱更新。固然若是僅僅是刷新瀏覽器,也就沒有後面那些步驟了。
  • HotModuleReplacement.runtime 是客戶端 HMR 的中樞,它接收到上一步傳遞給他的新模塊的 hash 值,它經過 JsonpMainTemplate.runtime 向 server 端發送 Ajax 請求,服務端返回一個 json,該 json 包含了全部要更新的模塊的 hash 值,獲取到更新列表後,該模塊再次經過 jsonp 請求,獲取到最新的模塊代碼。這就是上圖中 七、八、9 步驟。
  • 而第 10 步是決定 HMR 成功與否的關鍵步驟,在該步驟中,HotModulePlugin 將會對新舊模塊進行對比,決定是否更新模塊,在決定更新模塊後,檢查模塊之間的依賴關係,更新模塊的同時更新模塊間的依賴引用。
  • 最後一步,當 HMR 失敗後,回退到 live reload 操做,也就是進行瀏覽器刷新來獲取最新打包代碼。

參考連接

相關文章
相關標籤/搜索