webpack 模塊熱替換原理
全稱(chēng)是Hot Module ReplaceMent(HMR),理解成熱模塊替換或者模塊熱替換都可以吧,和.net中的熱插拔一個(gè)意思,就是在運(yùn)行中對(duì)程序的模塊進(jìn)行更新。這個(gè)功能主要是用于開(kāi)發(fā)過(guò)程中,對(duì)生產(chǎn)環(huán)境沒(méi)有任何幫助(這一點(diǎn)區(qū)別.net熱插拔)。效果上就是界面的無(wú)刷新更新。
HMR基于WDS,style-loader可以通過(guò)它來(lái)實(shí)現(xiàn)無(wú)刷新更新樣式。但是對(duì)于JavaScript模塊就需要做一點(diǎn)額外的處理,怎么處理繼續(xù)往下看。因?yàn)镠MR是用于開(kāi)發(fā)環(huán)境的,所以我們修改下配置,做兩份準(zhǔn)備。一個(gè)用于生產(chǎn),一個(gè)用于開(kāi)發(fā)。
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); const PATHS = { app: path.join(__dirname, 'app'), build: path.join(__dirname, 'build'), }; const commonConfig={ entry: { app: PATHS.app, }, output: { path: PATHS.build, filename: '[name].js', }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack demo', }), ], } function developmentConfig(){ const config ={ devServer:{ //使能歷史記錄api historyApiFallback:true, hotOnly:true,//關(guān)閉熱替換 注釋掉這行就行 stats:'errors-only', host:process.env.Host, port:process.env.PORT, overlay:{ errors:true, warnings:true, } }, plugins: [ new webpack.HotModuleReplacementPlugin(), ], }; return Object.assign( {}, commonConfig, config, { plugins: commonConfig.plugins.concat(config.plugins), } ); } module.exports = function(env){ console.log("env",env); if(env=='development'){ return developmentConfig(); } return commonConfig; };
這個(gè)webpack.config.js建立了兩個(gè)配置,一個(gè)是commonConfig,一個(gè)是developmentConfig 兩者通過(guò)env參數(shù)來(lái)區(qū)分,但這個(gè)env參數(shù)是怎么來(lái)的呢?我們看看之前的package.json中的一段:
也就是說(shuō),如果按照上面的這個(gè)配置,我們通過(guò)npm start 啟動(dòng)的話(huà),進(jìn)入的就是開(kāi)發(fā)環(huán)境配置,如果是直接build,那么就是生產(chǎn)環(huán)境的方式。build方式是第一節(jié)里面講的 直接通過(guò)npm啟動(dòng)webpack,這就不帶WDS了。另外有了一個(gè)Object.assign語(yǔ)法,將配置合并。這個(gè)時(shí)候通過(guò)npm start啟動(dòng),控制臺(tái)打印出了兩條日志。
看起來(lái)HRM已經(jīng)啟動(dòng)了。但是此時(shí)更新一下component.js
日志顯示沒(méi)有東西被熱更新。而且這個(gè)39,36代表的是模塊Id,看起來(lái)很不直觀,這里可以通過(guò)一個(gè)插件使其更符合人意
plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin(), ],
這個(gè)時(shí)候再啟動(dòng)。
這樣名稱(chēng)就直觀了。但是我們期待的更新還是沒(méi)有出來(lái)。因?yàn)樾枰獙?shí)現(xiàn)一個(gè)接口
import component from './component'; let demoComponent=component(); document.body.appendChild(demoComponent); //HMR 接口 if(module.hot){ module.hot.accept('./component',()=>{ const nextComponent=component(); document.body.replaceChild(nextComponent,demoComponent); demoComponent=nextComponent; }) }
并修改component.js:
export default function () { var element = document.createElement('h1'); element.innerHTML = 'Hello webpack'; return element; }
這個(gè)時(shí)候頁(yè)面更新了。每次改動(dòng)頁(yè)面上都會(huì)增加一個(gè)帶有hot-update.js ,類(lèi)似于下面這樣:
webpackHotUpdate(0,{ /***/ "./app/component.js": /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony default export */ __webpack_exports__["default"] = function () { var element = document.createElement('h1'); element.innerHTML = 'Hello web '; element.className='box'; return element; }; /***/ }) })
通過(guò)webpackHotUpdate對(duì)相應(yīng)模塊進(jìn)行更新。0表示模塊的id,"./app/component.js"表示模塊對(duì)應(yīng)的name。結(jié)構(gòu)是webpack(id,{key:function(){}})。function外帶了一個(gè)括號(hào),不知道有什么作用。webpackHotUpdate的定義是這樣的:
this["webpackHotUpdate"] = function webpackHotUpdateCallback(chunkId, moreModules) { // eslint-disable-line no-unused-vars hotAddUpdateChunk(chunkId, moreModules); if(parentHotUpdateCallback) parentHotUpdateCallback(chunkId, moreModules); } ;
小結(jié):從結(jié)構(gòu)來(lái)看,一個(gè)是id,一個(gè)是對(duì)應(yīng)修改的模塊。但實(shí)際執(zhí)行更新的是hotApply方法。熱更新整個(gè)機(jī)制還是有點(diǎn)復(fù)雜,效果上像MVVM的那種綁定。有興趣的可以深入研究下。不建議在生產(chǎn)使用HMR,會(huì)讓整體文件變大,而且對(duì)生成沒(méi)有什么幫助,在下一節(jié)會(huì)講樣式的加載,style-loader就是用到了HMR。但對(duì)于js模塊還要寫(xiě)額外的代碼,這讓人有點(diǎn)不爽。
demo:webpack-ch3_jb51.rar
參考:
https://survivejs.com/webpack/developing/configuring-hmr/
https://webpack.js.org/configuration/dev-server/
系列:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
概述一個(gè)頁(yè)面從輸入U(xiǎn)RL到頁(yè)面加載完的過(guò)程
本文主要對(duì)一個(gè)頁(yè)面從輸入 URL 到頁(yè)面加載完的過(guò)程中都發(fā)生了什么事情進(jìn)行概述,對(duì)我們學(xué)習(xí)網(wǎng)絡(luò)語(yǔ)言有一定的幫助,下面就跟小編一起來(lái)看下吧2016-12-12關(guān)于javascript event flow 的一個(gè)bug詳解
描述了firefox,safari 有一個(gè)bug和DOM 3 規(guī)范不一致:在event.currentTarget等于event.target的時(shí)候(即event flow處于target phase時(shí)),會(huì)調(diào)用添加到currentTarget上的useCapture為true的listener2013-09-09javaScript手機(jī)號(hào)碼校驗(yàn)工具類(lèi)PhoneUtils詳解
這篇文章主要為大家詳細(xì)介紹了javaScript手機(jī)號(hào)碼校驗(yàn)工具類(lèi)PhoneUtils,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12javascript xml為數(shù)據(jù)源的下拉框控件
此控件以xml為數(shù)據(jù)源,可以進(jìn)行輸入的多屬性自動(dòng)適配2009-07-07移動(dòng)端H5開(kāi)發(fā) Turn.js實(shí)現(xiàn)很棒的翻書(shū)效果
這篇文章主要為大家詳細(xì)介紹了Turn.js實(shí)現(xiàn)很棒的翻書(shū)效果,對(duì)Turn.js翻書(shū)效果的實(shí)現(xiàn)進(jìn)行總結(jié),感興趣的小伙伴們可以參考一下2016-06-06淺談javascript的url參數(shù)parse和build函數(shù)
下面小編就為大家?guī)?lái)一篇淺談javascript的url參數(shù)parse和build函數(shù)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03一步步教你用js簡(jiǎn)單實(shí)現(xiàn)新年倒計(jì)時(shí)
一轉(zhuǎn)眼已經(jīng)臘月了,相信小伙伴們一定想知道我們距離2023新年還有多少天,下面這篇文章主要給大家介紹了關(guān)于如何一步步教你用js簡(jiǎn)單實(shí)現(xiàn)新年倒計(jì)時(shí)的相關(guān)資料,需要的朋友可以參考下2022-12-12跟我學(xué)Nodejs(三)--- Node.js模塊
這是本系列的第三篇文章了,前面2篇網(wǎng)友們反饋回來(lái)不少的消息,加上最近2天比較忙,一直沒(méi)來(lái)得及整理,周末了,趕緊給大家整理下發(fā)出來(lái),本文講的是node.js模塊2014-05-05詳解JavaScript的Symbol類(lèi)型、隱藏屬性、全局注冊(cè)表
這篇文章主要介紹了JavaScript的Symbol類(lèi)型、隱藏屬性、全局注冊(cè)表,本文主要對(duì)其使用方法和使用場(chǎng)景做個(gè)簡(jiǎn)單的介紹,需要的朋友可以參考下2022-05-05JavaScript中數(shù)組的22種方法必學(xué)(推薦)
這篇文章主要介紹了JavaScript中數(shù)組的22種方法必學(xué)(推薦)的相關(guān)資料,需要的朋友可以參考下2016-07-07