淺談webpack 自動(dòng)刷新與解析
前端需要頻繁的修改js和樣式,且需要根據(jù)瀏覽器的頁(yè)面效果不斷的做調(diào)整;而且往往我們的開發(fā)目錄和本地發(fā)布目錄不是同一個(gè),修改之后需要發(fā)布一下;另外一點(diǎn)就是并不是所有的效果都可以直接雙擊頁(yè)面就能看到,我們常常需要在本地用nginx建一個(gè)站點(diǎn)來(lái)觀察(自己電腦上ok了才放到測(cè)試環(huán)境去)。所以如果要用手工刷新瀏覽器和手動(dòng)(或點(diǎn)擊)發(fā)布,還要啟動(dòng)站點(diǎn),確實(shí)是個(gè)不小的體力活。而這三點(diǎn)webpack可以幫我們做到。
webpack-dev-serverwebpack
是通過(guò)webpack-dev-server(WDS)來(lái)實(shí)現(xiàn)自動(dòng)刷新。WDS是一個(gè)運(yùn)行在內(nèi)存中的開發(fā)服務(wù)器(一個(gè)express)。啟動(dòng)之后,它會(huì)檢測(cè)文件是否發(fā)生改變并再自動(dòng)編譯一次。
1.安裝
npm install webpack-dev-server --save-dev
先通過(guò)npm將其安裝到開發(fā)目錄。安裝完成之后會(huì)在node_modules/bin下找到。
2.npm啟動(dòng)
然后修改package.json:(基于上一節(jié))
"scripts": {
"start": "webpack-dev-server --env development",
"build": "webpack --env production"
}
現(xiàn)在就可以通過(guò)npm run start 或者 npm start來(lái)啟動(dòng)了。

啟動(dòng)之后,可以看到Project is running at http://localhost:8080 上面。打開頁(yè)面

說(shuō)明WDS已經(jīng)幫我們自動(dòng)建了一個(gè)站點(diǎn).我們修改component.js ,cmd中會(huì)出現(xiàn)編譯,頁(yè)面會(huì)自動(dòng)刷新。

3.直接啟動(dòng)
官網(wǎng)介紹可以直接通過(guò)下面的命令啟動(dòng)WDS。
webpack-dev-server --env development
但會(huì)出現(xiàn)webpack-dev-server --env development 不是內(nèi)部命令的提示,這種問(wèn)題都是環(huán)境變量的問(wèn)題,將你開發(fā)的bin目錄設(shè)置到環(huán)境變量中即可,比如我的目錄是‘E:\Html5\node_modules\.bin',就加上分號(hào)寫在后面。
C:\Users\Administrator.9BBOFZPACSCXLG2\AppData\Roaming\npm;C:\Program Files (x86)\Microsoft VS Code\bin;E:\Html5\node_modules\.bin

4.8080端口占用
如果默認(rèn)的8080端口占用,WDS會(huì)換一個(gè)。比如用nginx先發(fā)布一個(gè)。
server{
listen 8080;
location / {
root E:/Html5/build;
index index.html index.htm;
}
}
再啟動(dòng)WDS:

端口切到了8081。也可以手動(dòng)配置端口:
devServer:{
//...
port: 9000
}
nodemon 自動(dòng)啟動(dòng)
WDS是監(jiān)視開發(fā)文件的,webpack.config.js改變不會(huì)引起自動(dòng)啟動(dòng)。所以我們需要nodemon去做這件事情。
npm install nodemon --save-dev
先安裝在開發(fā)目錄,然后修改package.json:
"scripts": {
"start": "nodemon --watch webpack.config.js --exec \"webpack-dev-server --env development\"",
"build": "webpack --env production"
},
等于讓nodemon去監(jiān)視webpack.config.js,變化了就去啟動(dòng)它。

這樣就你可以讓你的雙手專心的開發(fā)了。
代理
不過(guò)有一點(diǎn)疑問(wèn),就是WDS這個(gè)站點(diǎn)的替代性,因?yàn)槲覀冏约翰渴鸬膎ginx有一些api的代理。如果掛在WDS的這個(gè)默認(rèn)站點(diǎn)上自然是無(wú)法訪問(wèn)的。換句話說(shuō)可否給WDS配置一個(gè)刷新路徑。如果文件改變?nèi)ニ⑿轮付ǖ牡刂罚蛘咦屛胰ヅ鋫€(gè)代理。既然它本身是一個(gè)http服務(wù)器,肯定也有代理的功能。搜了下果然有:https://github.com/webpack/webpack-dev-server/tree/master/examples/proxy-advanced
module.exports = {
context: __dirname,
entry: "./app.js",
devServer: {
proxy: {
"/api": {
target: "http://jsonplaceholder.typicode.com/",
changeOrigin: true,
pathRewrite: {
"^/api": ""
},
bypass: function(req) {
if(req.url === "/api/nope") {
return "/bypass.html";
}
}
}
}
}
}
即將api這個(gè)字段替換成http://jsonplaceholder.typicode.com/,并將其從原地址中刪掉,這樣就可以自己實(shí)現(xiàn)代理了。皆大歡喜!WDS是通過(guò)http-proxy-middleware來(lái)實(shí)現(xiàn)代理。更多參考:http://webpack.github.io/docs/webpack-dev-server.html;https://github.com/chimurai/http-proxy-middleware#options
but,這種刷新是怎么實(shí)現(xiàn)的呢?因?yàn)轫?yè)面上沒(méi)有嵌入什么別的js,去翻原碼 web-dev-server/server.js中有這么一段:
Server.prototype._watch = function(path) {
const watcher = chokidar.watch(path).on("change", function() {
this.sockWrite(this.sockets, "content-changed");
}.bind(this))
this.contentBaseWatchers.push(watcher);
}
用chokidar來(lái)監(jiān)視文件變化,server的內(nèi)部維護(hù)的有一個(gè)socket集合:
Server.prototype.sockWrite = function(sockets, type, data) {
sockets.forEach(function(sock) {
sock.write(JSON.stringify({
type: type,
data: data
}));
});
}
sock是一個(gè)sockjs對(duì)象。https://github.com/sockjs/sockjs-client,從http://localhost:8080/webpack-dev-server/頁(yè)面來(lái)看,sockjs是用來(lái)通信記錄日志的。
var onSocketMsg = {
hot: function() {
hot = true;
log("info", "[WDS] Hot Module Replacement enabled.");
},
invalid: function() {
log("info", "[WDS] App updated. Recompiling...");
sendMsg("Invalid");
},
hash: function(hash) {
currentHash = hash;
},
...
}
我們?cè)诳碼pp.js,其中有一個(gè)OnSocketMsg 對(duì)象。
var onSocketMsg = {
hot: function() {
hot = true;
log("info", "[WDS] Hot Module Replacement enabled.");
},
invalid: function() {
log("info", "[WDS] App updated. Recompiling...");
sendMsg("Invalid");
},
hash: function(hash) {
currentHash = hash;
},
"still-ok": function() {
log("info", "[WDS] Nothing changed.")
if(useWarningOverlay || useErrorOverlay) overlay.clear();
sendMsg("StillOk");
},
"log-level": function(level) {
logLevel = level;
},
"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: function() {
sendMsg("Ok");
if(useWarningOverlay || useErrorOverlay) overlay.clear();
if(initial) return initial = false;
reloadApp();
},
"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: function() {
log("error", "[WDS] Disconnected!");
sendMsg("Close");
}
};
ok的時(shí)候觸發(fā)一個(gè)reloadApp
function reloadApp() {
if(hot) {
log("info", "[WDS] App hot update...");
var hotEmitter = __webpack_require__("./node_modules/webpack/hot/emitter.js");
hotEmitter.emit("webpackHotUpdate", currentHash);
if(typeof self !== "undefined") {
// broadcast update to window
self.postMessage("webpackHotUpdate" + currentHash, "*");
}
} else {
log("info", "[WDS] App updated. Reloading...");
self.location.reload();
}
}
也就是說(shuō)WDS先檢測(cè)文件是否變化,然后通過(guò)sockjs通知到客戶端,這樣就實(shí)現(xiàn)了刷新。之前WebSocket的第三方只用過(guò)socket.io,看起來(lái)sockjs也蠻好用的。不必外帶一個(gè)js,在主js里面就可以寫了。
小結(jié):效率提高的一方面是將一些機(jī)械的重復(fù)性流程或動(dòng)作自動(dòng)化起來(lái)。WDS和nodemon就是兩個(gè)為你干活的小弟。
demo:webpack-ch2_jb51.rar
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
web網(wǎng)頁(yè)按比例顯示圖片實(shí)現(xiàn)原理及js代碼
由于上傳圖片的大小是未知的,在顯示成縮略圖的時(shí)候必須進(jìn)行按比例的縮放才能美觀地顯示,本文將為大家簡(jiǎn)單的介紹一種比較不錯(cuò)的方法,有此需求的朋友可以參考下2013-08-08
通過(guò)js為元素添加多項(xiàng)樣式,瀏覽器全兼容寫法
這篇文章主要介紹了通過(guò)js為元素添加多項(xiàng)樣式,瀏覽器全兼容寫法,需要的朋友可以參考下2014-08-08
導(dǎo)航跟隨滾動(dòng)條置頂移動(dòng)示例代碼
滾動(dòng)條滾動(dòng)時(shí)如何讓導(dǎo)航置頂移動(dòng),這種效果已經(jīng)在很多網(wǎng)看到了,所以本文也來(lái)實(shí)現(xiàn)一個(gè),感興趣的朋友可以學(xué)習(xí)下2013-09-09
js實(shí)現(xiàn)加載頁(yè)面就自動(dòng)觸發(fā)超鏈接的示例
下面小編就為大家?guī)?lái)一篇js實(shí)現(xiàn)加載頁(yè)面就自動(dòng)觸發(fā)超鏈接的示例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08

