概述
最近面試被問到了 webpack 熱加載的實現原理,所以去研究了一下,記錄下來供以后開發時參考,相信對其它人也有用。
熱加載原理
這一部分我沒有去看源碼,只是看了別人的分析理清了一下思路,參考資料:
主要流程如下:
1.首先 webpack-dev-server 會建立一個服務器,並且和瀏覽器建立 websocket 通信。
2.服務器監聽文件變化,當文件變化的時候,會重新打包相應的 chunk,然后向瀏覽器發射 hash 和 ok 事件,通知瀏覽器對應的 chunkid 等信息。
3.瀏覽器監聽 hash 和 ok 事件,再接受信息之后,通過 jsonp 向服務端請求對應的熱更新代碼。
4.最后瀏覽器把 jsonp 獲得的代碼注入到 html 的 head 里面去執行,從而實現了對應的模塊替換。
vue-loader 是怎么熱更新的
vue-loader的源碼如下:
const hotReloadAPIPath = JSON.stringify(require.resolve('vue-hot-reload-api'))
const genTemplateHotReloadCode = (id, request) => {
return `
module.hot.accept(${request}, function () {
api.rerender('${id}', {
render: render,
staticRenderFns: staticRenderFns
})
})
`.trim()
}
exports.genHotReloadCode = (id, functional, templateRequest) => {
return `
/* hot reload */
if (module.hot) {
var api = require(${hotReloadAPIPath})
api.install(require('vue'))
if (api.compatible) {
module.hot.accept()
if (!api.isRecorded('${id}')) {
api.createRecord('${id}', component.options)
} else {
api.${functional ? 'rerender' : 'reload'}('${id}', component.options)
}
${templateRequest ? genTemplateHotReloadCode(id, templateRequest) : ''}
}
}
`.trim()
}
可以看到,它其實在編譯單文件組件的時候,會把熱加載相關的代碼注入到編譯后的代碼里面去,從而實現熱更新的。
vue-loader 熱更新的狀態怎么保留
通過vue-loader說明,我們可以看到對於狀態的保留規則:
1.當編輯一個 template 的時候,組件會重新渲染。(rerender)
2.當編輯一個 script 的時候,組件會就地銷毀並重新創建。(reload)
3.當編輯一個 style 的時候,通過 vue-style-loader 自行熱重載,並不影響狀態。
vue-loader 源碼里面的 api 是什么
里面的 api 其實是 vue-hot-reload-api,這個庫其實就控制了組件在重新渲染(rerender)和重新創建(reload)的時候做的事。
module.hot.accept
通過上面的源碼可以看到,module.hot.accept
其實並沒有傳遞參數,這是什么用法呢?通過查閱webpack官方文檔,這個通常寫在對應的模塊里面,作用是標明這個模塊可以熱加載,在這個模塊改變的時候會熱加載這個模塊。
所以,vue-loader 的熱重載原理其實是:打包后的單文件組件在修改之后,會借用 devserver 的熱重載功能更新單文件組件,並且通過監聽module.hot.data
的變化,使用module.hot.accept
中的 api 對組件進行重新渲染或者重新創建。