webpack-dev-server原理分析與HMR實現
建議在github閱讀,我會保證內容及時更新,並歡迎star,issue。如果你想深入瞭解webpack-dev-server的內部原理,你也可以檢視我寫的這個打包工具,通過它可以完成三種打包方式,其中devServer模式就是通過webpack-dev-server來完成的,並且支援HMR(webpack-dev-server雖然說可以支援無重新整理更新資料,但是在大多數情況下都是重新整理頁面的,而該打包工具已經無需重新整理而完成資料更新了)。對於webpack的HMR不瞭解的可以檢視這裡。其中也牽涉到webpack-dev-middleware中介軟體。希望對您有用
webpack-dev-server在我們的entry中新增的hot模組內容
看看下面的方法你就知道了,在hot模式下,我們的entry最後都會被新增兩個檔案:
module.exports = function addDevServerEntrypoints(webpackOptions, devServerOptions) {
if(devServerOptions.inline !== false) {
//表示是inline模式而不是iframe模式
const domain = createDomain(devServerOptions);
const devClient = [`${require .resolve("../../client/")}?${domain}`];
//客戶端內容
if(devServerOptions.hotOnly)
devClient.push("webpack/hot/only-dev-server");
else if(devServerOptions.hot)
devClient.push("webpack/hot/dev-server");
//配置了不同的webpack而檔案到客戶端檔案中
[].concat(webpackOptions).forEach(function (wpOpt) {
if(typeof wpOpt.entry === "object" && !Array.isArray(wpOpt.entry)) {
/*
entry:{
index:'./index.js', => entry:[]
index1:'./index1.js'
}
*/
Object.keys(wpOpt.entry).forEach(function(key) {
wpOpt.entry[key] = devClient.concat(wpOpt.entry[key]);
});
//新增我們自己的入口檔案
} else if(typeof wpOpt.entry === "function") {
wpOpt.entry = wpOpt.entry(devClient);
//如果entry是一個函式那麼我們把devClient陣列傳入
} else {
wpOpt.entry = devClient.concat(wpOpt.entry);
//陣列直接傳入
}
});
}
};
(1)首先看看”webpack/hot/only-dev-server”的檔案內容:
if(module.hot) {
var lastHash;
//___webpack_hash__
//Access to the hash of the compilation.
//Only available with the HotModuleReplacementPlugin or the ExtendedAPIPlugin
var upToDate = function upToDate() {
return lastHash.indexOf(__webpack_hash__) >= 0;
//如果兩個hash相同那麼表示沒有更新
};
//檢查更新
var check = function check() {
//Check all currently loaded modules for updates and apply updates if found.
module.hot.check().then(function(updatedModules) {
//沒有更新的模組直接返回
if(!updatedModules) {
console.warn("[HMR] Cannot find update. Need to do a full reload!");
console.warn("[HMR] (Probably because of restarting the webpack-dev-server)");
return;
}
//apply方法:If status() != "ready" it throws an error.
//開始更新
return module.hot.apply({
ignoreUnaccepted: true,
ignoreDeclined: true,
ignoreErrored: true,
onUnaccepted: function(data) {
console.warn("Ignored an update to unaccepted module " + data.chain.join(" -> "));
},
onDeclined: function(data) {
console.warn("Ignored an update to declined module " + data.chain.join(" -> "));
},
onErrored: function(data) {
console.warn("Ignored an error while updating module " + data.moduleId + " (" + data.type + ")");
}
//renewedModules表示哪些模組已經更新了
}).then(function(renewedModules) {
if(!upToDate()) {
check();
}
//更新的模組updatedModules,renewedModules表示哪些模組已經更新了
require("./log-apply-result")(updatedModules, renewedModules);
if(upToDate()) {
console.log("[HMR] App is up to date.");
}
});
}).catch(function(err) {
var status = module.hot.status();
if(["abort", "fail"].indexOf(status) >= 0) {
console.warn("[HMR] Cannot check for update. Need to do a full reload!");
console.warn("[HMR] " + err.stack || err.message);
} else {
console.warn("[HMR] Update check failed: " + err.stack || err.message);
}
});
};
var hotEmitter = require("./emitter");
//emitter模組內容,也就是匯出一個events例項
/*
var EventEmitter = require("events");
module.exports = new EventEmitter();
*/
hotEmitter.on("webpackHotUpdate", function(currentHash) {
lastHash = currentHash;
//表示本次更新後得到的hash值
if(!upToDate()) {
//有更新
var status = module.hot.status();
if(status === "idle") {
console.log("[HMR] Checking for updates on the server...");
check();
} else if(["abort", "fail"].indexOf(status) >= 0) {
console.warn("[HMR] Cannot apply update as a previous update " + status + "ed. Need to do a full reload!");
}
}
});
console.log("[HMR] Waiting for update signal from WDS...");
} else {
throw new Error("[HMR] Hot Module Replacement is disabled.");
}
./log-apply-result模組內容如下:
module.exports = function(updatedModules, renewedModules) {
//renewedModules表示哪些模組被更新了,剩餘的模組表示,哪些模組由於 ignoreDeclined,ignoreUnaccepted配置沒有更新
var unacceptedModules = updatedModules.filter(function(moduleId) {
return renewedModules && renewedModules.indexOf(moduleId) < 0;
});
//哪些模組無法HMR,列印log
if(unacceptedModules.length > 0) {
console.warn("[HMR] The following modules couldn't be hot updated: (They would need a full reload!)");
unacceptedModules.forEach(function(moduleId) {
console.warn("[HMR] - " + moduleId);
});
}
//沒有模組更新,表示模組是最新的
if(!renewedModules || renewedModules.length === 0) {
console.log("[HMR] Nothing hot updated.");
} else {
console.log("[HMR] Updated modules:");
//更新的模組
renewedModules.forEach(function(moduleId) {
console.log("[HMR] - " + moduleId);
});
//每一個moduleId都是數字那麼建議使用NamedModulesPlugin
var numberIds = renewedModules.every(function(moduleId) {
return typeof moduleId === "number";
});
if(numberIds)
console.log("[HMR] Consider using the NamedModulesPlugin for module names.");
}
};
所以”webpack/hot/only-dev-server”的檔案內容就是檢查哪些模組更新了(通過webpackHotUpdate事件完成),其中哪些模組更新成功,而哪些模組由於某種原因沒有更新成功。其中沒有更新的原因可能是如下的:
ignoreUnaccepted ignoreDecline ignoreErrored
至於模組什麼時候接受到需要更新是和webpack的打包過程有關的,這裡也給出觸發更新的時機:
ok: function() {
sendMsg("Ok");
if(useWarningOverlay || useErrorOverlay) overlay.clear();
if(initial) return initial = false;
reloadApp();
},
warnings: function(warnings) {
log("info", "[WDS] Warnings while compiling.");
var strippedWarnings = warnings.map(function(warning) {
return stripAnsi(warning);
});
sendMsg("Warnings", strippedWarnings);
for(var i = 0; i < strippedWarnings.length; i++)
console.warn(strippedWarnings[i]);
if(useWarningOverlay) overlay.showMessage(warnings);
if(initial) return initial = false;
reloadApp();
},
function reloadApp() {
//如果開啟了HMR模式
if(hot) {
log("info", "[WDS] App hot update...");
var hotEmitter = require("webpack/hot/emitter");
hotEmitter.emit("webpackHotUpdate", currentHash);
//重新啟動webpack/hot/emitter,同時設定當前hash
if(typeof self !== "undefined" && self.window) {
// broadcast update to window
self.postMessage("webpackHotUpdate" + currentHash, "*");
}
} else {
//如果不是Hotupdate那麼我們直接reload我們的window就可以了
log("info", "[WDS] App updated. Reloading...");
self.location.reload();
}
}
也就是說當客戶端接受到伺服器端傳送的ok和warning資訊的時候,同時支援HMR的情況下就會要求檢查更新,同時傳送過來的還有伺服器端本次編譯的hash值。我們繼續深入一步,看看伺服器什麼時候傳送’ok’和’warning’訊息:
Server.prototype._sendStats = function(sockets, stats, force) {
if(!force &&
stats &&
(!stats.errors || stats.errors.length === 0) &&
stats.assets &&
stats.assets.every(function(asset) {
return !asset.emitted;
//每一個asset都是沒有emitted屬性,表示沒有發生變化。如果發生變化那麼這個assets肯定有emitted屬性
})
)
return this.sockWrite(sockets, "still-ok");
this.sockWrite(sockets, "hash", stats.hash);
//設定hash
if(stats.errors.length > 0)
this.sockWrite(sockets, "errors", stats.errors);
else if(stats.warnings.length > 0)
this.sockWrite(sockets, "warnings", stats.warnings);
else
this.sockWrite(sockets, "ok");
}
也就是說更新是通過上面這個方法完成的,我們看看上面這個方法什麼時候呼叫就可以了:
compiler.plugin("done", function(stats) {
this._sendStats(this.sockets, stats.toJson(clientStats));
this._stats = stats;
}.bind(this));
是不是豁然開朗了,也就是每次compiler的’done’鉤子函式被呼叫的時候就會要求客戶端去檢查模組更新,進而完成HMR基本功能!
(2)再來看看webpack/hot/dev-server
if(module.hot) {
var lastHash;
//__webpack_hash__是每次編譯的hash值是全域性的
//Only available with the HotModuleReplacementPlugin or the ExtendedAPIPlugin
var upToDate = function upToDate() {
return lastHash.indexOf(__webpack_hash__) >= 0;
};
var check = function check() {
// check([autoApply], callback: (err: Error, outdatedModules: Module[]) => void
// If autoApply is truthy the callback will be called with all modules that were disposed. apply() is automatically called with autoApply as options parameter.(傳入哪些程式碼已經被更新的模組)
//If autoApply is not set the callback will be called with all modules that will be disposed on apply(). (不是true那麼傳入的是哪些需要被apply處理的模組)
module.hot.check(true).then(function(updatedModules) {
//檢查所有要更新的模組,如果沒有模組要更新那麼回撥函式就是null
if(!updatedModules) {
console.warn("[HMR] Cannot find update. Need to do a full reload!");
console.warn("[HMR] (Probably because of restarting the webpack-dev-server)");
window.location.reload();
return;
}
//如果還有更新
if(!upToDate()) {
check();
}
require("./log-apply-result")(updatedModules, updatedModules);
//已經被更新的模組都是updatedModules
if(upToDate()) {
console.log("[HMR] App is up to date.");
}
}).catch(function(err) {
var status = module.hot.status();
//如果報錯直接全域性reload
if(["abort", "fail"].indexOf(status) >= 0) {
console.warn("[HMR] Cannot apply update. Need to do a full reload!");
console.warn("[HMR] " + err.stack || err.message);
window.location.reload();
} else {
console.warn("[HMR] Update failed: " + err.stack || err.message);
}
});
};
var hotEmitter = require("./emitter");
//獲取MyEmitter物件
hotEmitter.on("webpackHotUpdate", function(currentHash) {
lastHash = currentHash;
if(!upToDate() && module.hot.status() === "idle") {
//呼叫module.hot.status方法獲取狀態
console.log("[HMR] Checking for updates on the server...");
check();
}
});
console.log("[HMR] Waiting for update signal from WDS...");
} else {
throw new Error("[HMR] Hot Module Replacement is disabled.");
}
也就是說webpack/hot/dev-server相較於前面在入口檔案中新增的”webpack/hot/only-dev-server”來說,區別在於後者傳入的是哪些已經被更新的模組,也就是已經被自己模組本身dispose處理了。如下:
if (module.hot) {
module.hot.accept();
// dispose handler
module.hot.dispose(() => {
window.clearInterval(intervalId);
});
}
(3)如果你注意到上面其實我們還添加了一個client/index.js,這個客戶端程式碼只是添加了我們的客戶端的socket.js程式碼,這時候我們客戶端就可以獲取到伺服器端傳送到的socket命令
var onSocketMsg = {
//設定hot為true
hot: function() {
hot = true;
log("info", "[WDS] Hot Module Replacement enabled.");
},
//列印invalid
invalid: function() {
log("info", "[WDS] App updated. Recompiling...");
sendMsg("Invalid");
},
//設定hash
hash: function(hash) {
currentHash = hash;
},
//繼續可用
"still-ok": function() {
log("info", "[WDS] Nothing changed.")
if(useWarningOverlay || useErrorOverlay) overlay.clear();
sendMsg("StillOk");
},
//設定log級別
"log-level": function(level) {
logLevel = level;
},
/*
Shows a full-screen overlay in the browser when there are compiler errors or warnings.
Disabled by default. If you want to show only compiler errors:
overlay: true
If you want to show warnings as well as errors:
overlay: {
warnings: true,
errors: true
}
*/
"overlay": function(overlay) {
if(typeof document !== "undefined") {
if(typeof(overlay) === "boolean") {
useWarningOverlay = overlay;
useErrorOverlay = overlay;
} else if(overlay) {
useWarningOverlay = overlay.warnings;
useErrorOverlay = overlay.errors;
}
}
},
//ok
ok: function() {
sendMsg("Ok");
if(useWarningOverlay || useErrorOverlay) overlay.clear();
if(initial) return initial = false;
reloadApp();
},
//客戶端檢測到伺服器端有更新,通過chokidar檢測到檔案的變化
"content-changed": function() {
log("info", "[WDS] Content base changed. Reloading...")
self.location.reload();
},
warnings: function(warnings) {
log("info", "[WDS] Warnings while compiling.");
var strippedWarnings = warnings.map(function(warning) {
return stripAnsi(warning);
});
sendMsg("Warnings", strippedWarnings);
for(var i = 0; i < strippedWarnings.length; i++)
console.warn(strippedWarnings[i]);
if(useWarningOverlay) overlay.showMessage(warnings);
if(initial) return initial = false;
reloadApp();
},
errors: function(errors) {
log("info", "[WDS] Errors while compiling. Reload prevented.");
var strippedErrors = errors.map(function(error) {
return stripAnsi(error);
});
sendMsg("Errors", strippedErrors);
for(var i = 0; i < strippedErrors.length; i++)
console.error(strippedErrors[i]);
if(useErrorOverlay) overlay.showMessage(errors);
},
//傳送訊息close
close: function() {
log("error", "[WDS] Disconnected!");
sendMsg("Close");
}
};
socket(socketUrl, onSocketMsg);
module.hot等相關方法什麼時候被呼叫
其實通過上面的分析,我們肯定有一點疑問就是,我們的這些module.hot等方法是在什麼時候呼叫的,其實看看HotModuleReplacementPlugin就明白了,下面貼出一部分程式碼:
parser.plugin("call module.hot.accept", function(expr) {
if(!this.state.compilation.hotUpdateChunkTemplate) return false;
if(expr.arguments.length >= 1) {
var arg = this.evaluateExpression(expr.arguments[0]);
var params = [],
requests = [];
if(arg.isString()) {
params = [arg];
} else if(arg.isArray()) {
params = arg.items.filter(function(param) {
return param.isString();
});
}
if(params.length > 0) {
params.forEach(function(param, idx) {
var request = param.string;
var dep = new ModuleHotAcceptDependency(request, param.range);
dep.optional = true;
dep.loc = Object.create(expr.loc);
dep.loc.index = idx;
this.state.module.addDependency(dep);
requests.push(request);
}.bind(this));
if(expr.arguments.length > 1)
this.applyPluginsBailResult("hot accept callback", expr.arguments[1], requests);
else
this.applyPluginsBailResult("hot accept without callback", expr, requests);
}
}
});
parser.plugin("call module.hot.decline", function(expr) {
if(!this.state.compilation.hotUpdateChunkTemplate) return false;
if(expr.arguments.length === 1) {
var arg = this.evaluateExpression(expr.arguments[0]);
var params = [];
if(arg.isString()) {
params = [arg];
} else if(arg.isArray()) {
params = arg.items.filter(function(param) {
return param.isString();
});
}
params.forEach(function(param, idx) {
var dep = new ModuleHotDeclineDependency(param.string, param.range);
dep.optional = true;
dep.loc = Object.create(expr.loc);
dep.loc.index = idx;
this.state.module.addDependency(dep);
}.bind(this));
}
});
parser.plugin("expression module.hot", function() {
return true;
});
});
});
也就是我們關注的這些module.hot.decline方法都是在Parser上封裝的!
如何寫出支援HMR的程式碼
這裡就是一個例子,你也可以檢視這個倉庫,然後克隆下來,執行”node ./bin/wcf –dev”命令,你就會發現訪問localhost:8080的時候程式碼是可以支援HMR(你可以修改test目錄下的所有的檔案),而不會出現頁面重新整理的情況。
import * as dom from './dom';
import * as time from './time';
import pulse from './pulse';
require('./styles.scss');
const UPDATE_INTERVAL = 1000; // milliseconds
const intervalId = window.setInterval(() => {
dom.writeTextToElement('upTime', time.getElapsedSeconds() + ' seconds');
dom.writeTextToElement('lastPulse', pulse());
}, UPDATE_INTERVAL);
// Activate Webpack HMR
if (module.hot) {
module.hot.accept();
// dispose handler
module.hot.dispose(() => {
window.clearInterval(intervalId);
});
}
其中accept函式簽名如下:
accept(dependencies: string[], callback: (updatedDependencies) => void) => void
accept(dependency: string, callback: () => void) => void
//直接接受當前模組某一個依賴模組的HMR
此時表示,我們這個模組支援HMR,任何其依賴的模組變化都會被捕捉到。當依賴的模組更新後回撥函式被呼叫。當然,如果是下面這種方式:
accept([errHandler]) => void
那麼表示我們接受當前模組所有依賴的模組的程式碼更新,而且這種更新不會冒泡到父級中去。這當我們模組沒有匯出任何東西的情況下有用(比如entry)。
其中decline函式
上面的例子中我們的dom.js是如下方式寫的:
import $ from 'jquery';
export function writeTextToElement(id, text) {
$('#' + id).text(text);
}
if (module.hot) {
module.hot.decline('jquery');//不接受jquery更新
}
其中decline方法簽名如下:
decline(dependencies: string[]) => void
decline(dependency: string) => void
這表明我們不會接受特定模組的更新,如果該模組更新了,那麼更新失敗同時失敗程式碼為”decline”。而上面的程式碼表明我們不會接受jquery模組的更新。當前也可以是如下模式:
decline() => void
這表明我們當前的模組是不會更新的,也就是不會HMR。如果更新了那麼錯誤程式碼為”decline”;
其中dispose函式
函式簽名如下:
dispose(callback: (data: object) => void) => void
addDisposeHandler(callback: (data: object) => void) => void
這表示我們會新增一個一次性的處理函式,這個函式在當前模組更新後會被呼叫。此時,你需要移除或者銷燬一些持久的資源,如果你想將當前的狀態資訊轉移到更新後的模組中,此時可以新增到data物件中,以後可以通過module.hot.data訪問。如下面的例子用於儲存指定模組例項化的時間,從而防止模組更新後資料丟失(重新整理後還是會丟失的)。
let moduleStartTime = getCurrentSeconds();
function getCurrentSeconds() {
return Math.round(new Date().getTime() / 1000);
// return new Date().getTime() / 1000;
}
export function getElapsedSeconds() {
return getCurrentSeconds() - moduleStartTime;
}
// Activate Webpack HMR
if (module.hot) {
const data = module.hot.data || {};
// Update our moduleStartTime if we are in the process of reloading
if (data.moduleStartTime)
moduleStartTime = data.moduleStartTime;
// dispose handler to pass our moduleStart time to the next version of our module
// 首次進入我們把當前時間儲存到moduleStartTime中以後就可以直接訪問
module.hot.dispose((data) => {
data.moduleStartTime = moduleStartTime;
});
}
hotUpdateChunkFilename vs hotUpdateMainFilename
當你修改了test目錄下的檔案的時候,比如修改了scss檔案,此時你會發現在頁面中多出了一個script元素,內容如下:
<script type="text/javascript" charset="utf-8" src="0.188304c98f697ecd01b3.hot-update.js"></script>
其中內容是:
webpackHotUpdate(0,{
/***/ 15:
/***/ (function(module, exports, __webpack_require__) {
exports = module.exports = __webpack_require__(46)();
// imports
// module
exports.push([module.i, "html {\n border: 1px solid yellow;\n background-color: pink; }\n\nbody {\n background-color: lightgray;\n color: black; }\n body div {\n font-weight: bold; }\n body div span {\n font-weight: normal; }\n", ""]);
// exports
/***/ })
})
//# sourceMappingURL=0.188304c98f697ecd01b3.hot-update.js.map
從內容你也可以看出,只是將我們修改的模組push到exports物件中!而hotUpdateChunkFilename就是為了讓你能夠執行script的src中的值的!而同樣的hotUpdateMainFilename是一個json檔案用於指定哪些模組發生了變化,在output目錄下。
webpack和webpack-dev-server關係
webpack首先打包成檔案放在具體的目錄下,並通過publicPath配置成了虛擬路徑,而當通過URL訪問伺服器的時候就會從req.url尋找了具體的輸出檔案,最後得到這個輸出檔案並原樣傳送到客戶端。看下面的方法你就明白了:
var pathJoin = require("./PathJoin");
var urlParse = require("url").parse;
function getFilenameFromUrl(publicPath, outputPath, url) {
var filename;
// localPrefix is the folder our bundle should be in
// 第二個引數如果為false那麼查詢字串不會被decode或者解析
// 第三個引數為true,那麼//foo/bar被解析為{host: 'foo', pathname: '/bar'},也就是第一個"//"後,'/'前解析為host
// 如配置為 publicPath: "/assets/"將會得到下面的結果:
/*
Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: null,
query: null,
pathname: '/assets/
path: '/assets/',
href: '/assets/'
}
*/
var localPrefix = urlParse(publicPath || "/", false, true);
var urlObject = urlParse(url);
//URL是http請求的真實路徑,如http://localhost:1337/hello/world,那麼req.url得到的就是/hello/world
// publicPath has the hostname that is not the same as request url's, should fail
// 訪問的url的hostname和publicPath中配置的host不一致,直接返回。這隻有在publicPath是絕對URL的情況下出現
if(localPrefix.hostname !== null && urlObject.hostname !== null &&
localPrefix.hostname !== urlObject.hostname) {
return false;
}
// publicPath is not in url, so it should fail
// publicPath和req.url必須一樣
if(publicPath && localPrefix.hostname === urlObject.hostname && url.indexOf(publicPath) !== 0) {
return false;
}
// strip localPrefix from the start of url
// 如果url中的pathname和publicPath一致,那麼請求成功,檔名為urlObject中除去publicPath那一部分的結果
// 如上面/hello/world表示req.url,而且publicPath為/hello/那麼得到的檔名就是world
if(urlObject.pathname.indexOf(localPrefix.pathname) === 0) {
filename = urlObject.pathname.substr(localPrefix.pathname.length);
}
if(!urlObject.hostname && localPrefix.hostname &&
url.indexOf(localPrefix.path) !== 0) {
return false;
}
// and if not match, use outputPath as filename
//如果有檔名那麼從output.path中獲取該檔案,檔名為我們獲取到的檔名。否則返回我們的outputPath
//也就是說:如果沒有filename那麼我們直接獲取到我們的output.path這個目錄
return filename ? pathJoin(outputPath, filename) : outputPath;
}
module.exports = getFilenameFromUrl;
上面這個從URL到路徑的轉化就是通過webpack-dev-middleware來完成的。
參考資料:
相關推薦
webpack-dev-server原理分析與HMR實現
建議在github閱讀,我會保證內容及時更新,並歡迎star,issue。如果你想深入瞭解webpack-dev-server的內部原理,你也可以檢視我寫的這個打包工具,通過它可以完成三種打包方式,其中devServer模式就是通過webpack-dev-ser
tomcat原理分析與簡單實現
一、思路概述 1.tomcat實際是執行在jvm中的一個程序。我們把它定義為【中介軟體】,顧名思義,他是一個在java專案與jvm之間的 中間容器。我們的web專案沒有入口方法(main方法),那麼他是如何執行起來併為客戶端返回資料的呢? 2.web專案[就javaee而講]
機器學習系列文章:Apriori關聯規則分析演算法原理分析與程式碼實現
1.關聯規則淺談 關聯規則(Association Rules)是反映一個事物與其他事物之間的相互依存性和關聯性,如果兩個或多個事物之間存在一定的關聯關係,那麼,其中一個事物就能通過其他事物預測到。關聯規則是資料探勘的一個重要技術,用於從大量資料中挖掘出有價值的資料
歸併排序演算法原理分析與程式碼實現
歸併排序是建立在歸併操作上的一種有效的排序演算法。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用,歸併排序將兩個已排序的表合併成一個表。 歸併排序基本原理
第五篇:樸素貝葉斯分類演算法原理分析與程式碼實現
1 #==================================== 2 # 輸入: 3 # 空 4 # 輸出: 5 # postingList: 文件列表 6 # classVec: 分類標籤列表 7 #=
詳情介紹webpack-dev-server,iframe與inline的區別
webpack-dev-server用法 記錄下 webpack-dev-server 的用法. 首先,我們來看看基本的 webpack.config.js 的寫法 module.exports = { entry: './src/js/index.j
C++智慧指標原理分析與簡單實現
一個簡單智慧指標實現的思路如下: 智慧指標,簡單來講是使用引用計數的方法,來跟蹤監控指標。當引用計數為0時就delete 所跟蹤的目標指標,釋放記憶體 智慧指標將一個指標封裝到一個類中,當呼叫
Spring裝配基本屬性的原理分析與程式碼實現
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-ins
Apriori 關聯分析算法原理分析與代碼實現
muc items blog 具體實現 itblog run 任務 name subset 轉自穆晨 閱讀目錄 前言 關聯分析領域的一些概念 Apriori算法基本原理 頻繁項集檢索實現思路與實現代碼 關聯規則學習實現思路與實現代碼 小結 回到頂部 前言
K-Means 聚類算法原理分析與代碼實現
oat 得到 ssi targe fan readline txt __name__ 輸出 轉自穆晨 閱讀目錄 前言 現實中的聚類分析問題 - 總統大選 K-Means 聚類算法 K-Means性能優化 二分K-Means算法 小結 回到頂部 前言 在
webpack-dev-server 小記 原理介紹 概念解讀
style 事情 可能 asc 監聽文件 地址 dev ces 內容 使用 DevServer 提供 HTTP 服務而不是使用本地文件預覽 監聽文件的變化並自動刷新網頁,做到實時預覽 支持 Source Map,以方便調試 對於這些,Webpack 都為我們考慮好了。W
webpack4 系列教程(十五):開發模式與webpack-dev-server
作者按:因為教程所示圖片使用的是 github 倉庫圖片,網速過慢的朋友請移步《webpack4 系列教程(十五):開發模式與 webpack-dev-server》原文地址。更歡迎來我的小站看更多原創內容:godbmw.com,進行“姿勢”交流 ♪(^∇^*) 0. 課程介紹和資料 &g
webpack-dev-server實現專案熱部署
文章目錄 webpack-dev-server webpack-dev-server作用 新增webpack-dev-server 修改原始碼 執行結果 webpack-dev-server webp
webpack-dev-server啟動後,localhost:8080返回index.html的原理
webpack-dev-server是一個採用Node.js Express實現的微型伺服器, 內部使用webpack-dev-middleware來響應傳送到伺服器監聽單口的HTTP請求。 webpack-dev-server主要用於前端專案的本地開發和除錯。 具體使用,只需要在package.json
使用webpack-dev-server自動打包並實現debug除錯
webpack-dev-server 是一個開發伺服器,它的功能就是可以實現熱載入,並且自動重新整理瀏覽器 準備工作: 建立一個程式目錄test,將html頁面拷貝進來,在目錄下新建src、dist目錄 將main.js(入口js檔案)、vue.min.js以及mode
Vue系列之 => 使用webpack-dev-server工具實現自動打包編譯
安裝webpack-dev-server (webpack版本3.6.0,webpack-dev-server版本2.11.3)注意版本相容問題,不然會有N多錯誤。 1 npm i [email protected] -D //安裝到本地依賴 webpack
選擇排序原理分析與java程式碼實現
1、選擇排序改進了氣泡排序,將必要的交換次數從 O(N^2 )減少到 O(N)次(理解選擇排序可以先看一下我的上一篇氣泡排序的部落格)不幸的是比較次數仍然保持為 O(N^2 )。然而,選擇排序仍然為大記錄量的排序提出了一個非常重要的改進,因為這些大量的記錄需要在記憶體中移動,
淺析資料庫連線池原理分析與實現
1 動機在專案初期對於資料庫的使用就是開啟一個連線並進行使用,使用過後關閉連線釋放資源,並且在後臺簡單測試中,並沒有出現問題。但是在與前端對接之後,發現頻繁地開啟和關閉連線會對效能造成很大的影響,而且之前假設的情況是接受的請求都是同步的,但是前端可能傳送非同步請求,當兩個請求
webpack-dev-server live reloading 技術實現
.html sock 系統監控 master 文件管理器 另一個 des file fse webpack-dev-server live reloading https://github.com/webpack/webpack-dev-server Use we
webpack-dev-server啟動失敗
target href -s 失敗 http ima pack pac html 學習webpack-dev-server過程中,項目路徑下執行webpack-dev-server,老是報錯,原來是配置項在colors在webpack2.0以上版本不需要進行配置了,