webpack.config
配置參數devServer
中存在inline
參數,參數能夠設置devServer的兩種模式inline
和iframe
,默認使用inline
css
devServer: {
contentBase: "./dist",
// inline: false,
hot: true,
},
複製代碼
官方是這樣描述inline
模式的html
Toggle between the dev-server's two different modes. By default the application will be served with inline mode enabled. This means that a script will be inserted in your bundle to take care of live reloading, and build messages will appear in the browser console.vue
大體意思是說在inline
模式下,在bundle.js
中會插入代碼來處理自動刷新,因爲沒看源碼,根據開關inline
模式之後,webpack-dev-server
啓動時候的控制檯輸出,和官方文檔說明,因此我的猜想,大體實現:webpack
bundle.js
插入socket
的片斷,在瀏覽器加載後創建socket
鏈接- 利用
webpack -watch
來監聽文件變化,發送socket
信息告知瀏覽器,瀏覽器接受信息調用reload
來實現刷新頁面刷新
經過分析瀏覽器請求(inline
模式會在加載bundle.js
後發起一個websocket
請求)和bundle.js
代碼確認了這一點(onSocketMsg
-> reloadApp
-> applyReload
),也就是說使用webpack-dev-server
啓動就能夠開啓自動刷新功能了web
HMR(Hot Module Replacement),添加、修改模塊(修改JS/CSS)後瀏覽器內容經過非刷新的方式自動更新,提升開發效率,要注意HMR只應在開發環境使用vue-cli
HMR在配置成功之後,修改CSS/JS不會進行頁面刷新(注意保存文件變動的時候,瀏覽器Tab頁是不是自動刷新)json
HMR配置有四個關鍵點:api
webpack-dev-server
做爲服務器啓動webpack.config
的devServer
中配置hot: true
webpack.config
的plugins增長HotModuleReplacementPlugin
module.hot.accept
增長HMR代碼webpack.config數組
module.exports = {
mode: 'development',
devtool: 'inline-source-map',
entry: {
main: __dirname + '/app/main.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
devServer: {
contentBase: "./dist",//本地服務器所加載的頁面所在的目錄
// inline: true, //實時刷新
hot: true,
},
plugins: [
new HtmlWebpackPlugin({
template: __dirname + '/app/index.tmpl.html'
}),
new webpack.HotModuleReplacementPlugin()
],
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
};
複製代碼
main.js瀏覽器
import './extra.js'
import './style.css'
if (module.hot) {
module.hot.accept('./extra.js', function() {
console.log('Accepting the updated printMe module!');
})
// 關閉指定子模塊的HMR
// module.hot.decline('./extra.js')
}
複製代碼
extra.js
if(module.hot){
// 監聽當前模塊HMR異常
// module.hot.accept(() => {
// console.log('error handler')
// })
// 關閉當前模塊HMR
// module.hot.decline()
// 存儲模塊數據
// module.hot.dispose(disposeHander)
// if (module.hot.data) {
// console.log(module.hot.data.a)
// }
// 移除模塊數據
// module.hot.removeDisposeHandler(disposeHander)
}
複製代碼
配置成功之後修改extra.js
或者style.css
保存,都會觸發HMR,控制檯會輸出[HMR]
開頭的日誌
PS:
css文件咱們會配置style-loader
,style-loader
中已經增長了module.hot.accept
的支持,因此即便不配置module.hot.accept
,對於css也能夠HMR,可是若是JS沒有調用module.hot.accept
,HMR執行找不到對應的內容,則會直接刷新頁面,能夠設置參數hotOnly: true
來防止自動刷新
webpack plugins未添加HotModuleReplacementPlugin
使用hot: true
的時候會報錯 Uncaught Error: [HMR] Hot Module Replacement is disabled.
官網配置分爲Node版本和Web版本,因爲本身也瞭解不深,因此不探討Node版本如何使用webpack
的HMR
要想使用HMR,須要利用到HMR的API,在添加了HotModuleReplacementPlugin
模塊後,就可使用module.hot
的API
用於模塊處理的API
module.hot.accept(dependencies,callback)
:添加須要HMR的模塊,以及觸發變動後的回調,dependencies
支持字符串或者字符串數組
module.hot.accept(errorHandler)
:被添加的模塊內部使用能夠捕獲HMR異常時候拋出的異常
module.hot.decline(dependencies)
:和accept
操做相反,標記一個不須要HMR管理的模塊
module.hot.decline()
:JS模塊中使用,將致使該模塊沒法被HMR
module.hot.addDisposeHandler
/moduel.hot.dispose(data =>{})
:即便使用HMR,每次更新後數據是不能進行保留的,若是想將數據傳遞給更新後的模塊,使用dispose
對全局對象data
賦值,以後能夠經過module.hot.data
來獲取
module.hot.removeDisposeHandler(callback)
:移除經過dispose
和addDisposeHandler
添加的回調,移除後module.hot.data
將變爲空對象{}
(調用dispose
以前module.hot.data
是undefined
)
用於HMR狀態管理的API
module.hot.status()
: HMR過程是存在變化狀態的,經過該函數來獲取到HMR的當前狀態
使用vue-cli3構建,經過vue-cli-service serve
啓動的項目,其自己會添加HMR,在vue.config.js
的devServer
中配置hot: false
能夠關閉。
可是項目中發現一個狀況,當配置了https: true
之後,必須指定host: localhost
,HMR功能才能夠開啓,不然HMR功能將失效(實際上是整個自動更新功能都失效了,不會發起socket
請求),而在webpack
中並無出現該狀況。
據此揣測,vue-cli-service
在使用https: true
的時候,socket
連接必須指定使用hostname
才能夠進行構建,根據查找bundle.js
代碼,確認該邏輯(能夠在bundle.js
查找socket
關鍵字,能夠看到hostname
相關檢測邏輯)。
本小節內容都不是人話,可跳過~
1、應用程序(Applicationn):
socket
服務器發送的更新信息2、編譯部分(Compiler):
修改文件之後,編譯器觸發更新操做,更新兩部份內容:manifest
和chunks
manifest
包含了新的變化的全部模塊的代碼塊,編譯器確保module IDs
和chunk IDs
在本次構建中
3、模塊(Module)
隻影響HMR中包含了的模塊(module.hot.accept()
),好比:style-loader
,其中實現了HMR接口,因此在樣式更新的時候,新樣式替換老樣式
若是模塊沒有HMR的處理,則更新操做持續冒泡,也就是模塊樹中只要單個模塊發生了更新,整個都會從新加載
4、運行過程(Runtime)
運行過程會持續使用check
和apply
來進行module.hot.staus
的更新
check
: 請求更新清單,請求成功後,比較更新的內容和當前的內容,將全部更新的內容進行存儲直到全部內容更下載完成,切換到ready
狀態準備
apply
: 將updated的模塊標記爲invalid
,一直冒泡標記到entry point
,全部無效模塊處理完成後,更新全部的accept
的handlers
都會被觸發,而後狀態切換爲idle
狀態
查看bundle.js
中的代碼,將3.1中的內容翻譯一下:
webpack-dev-server
配置了hot:true
的基礎上,bundle.js
中的reloadApp()
會進行HMR的邏輯(另外一個邏輯是以前面的自動刷新),這個時候會開始判斷HMR
的status
,併發起一個AJAX請求chunk.hot-update.json
,該請求會返回一個JSON對象{
c: {
main: true
},
h: nextChunk
}
複製代碼
status
,會比較返回的nextChunk
和當前的chunk
是否相同,若是相同結束HMR,若是不一樣將nextChunk
存儲並繼續進行HMRchunk
拼接<script>
的src,並將<script>
動態插入html的<head>
中(好比個人配置就是:main.chunk.hot-update.js),操做後chunk數組序號+1<head>
插入了新的腳本<script src="main.chunk.hot-update.js"></script>
,實現動態更新JS而不刷新頁面補充:對於添加了module.hot.dispose
的部分,建立了全局對象data={}
來保存數據,從而實現從新加載JS之後,仍然能夠訪問該數值的內容
HMR的核心點有兩個:
- 如何在服務端文件變化以後通知客戶端?使用socket
- 如何判斷文件是否發生變化?客戶端和服務端都保存
chunk
,比較是否發生變化
webpack的內容其實真的有點多,僅僅HMR包含的內容就不少,本身的總結也不夠完整,只能說是恰好入門。不過整個過程一邊本身猜測,一遍跟源碼,而後驗證本身猜測,真的是頗有意思的過程。
webpack官方文檔:
CSDN上一篇特別棒的資料: