【webpack】-- 自動刷新與解析

前端須要頻繁的修改js和樣式,且須要根據瀏覽器的頁面效果不斷的作調整;並且每每咱們的開發目錄和本地發佈目錄不是同一個,修改以後須要發佈一下;另一點就是並非全部的效果均可以直接雙擊頁面就能看到,咱們經常須要在本地用nginx建一個站點來觀察(本身電腦上ok了才放到測試環境去)。因此若是要用手工刷新瀏覽器和手動(或點擊)發佈,還要啓動站點,確實是個不小的體力活。而這三點webpack能夠幫咱們作到。html

webpack-dev-server

webpack是經過webpack-dev-server(WDS)來實現自動刷新。WDS是一個運行在內存中的開發服務器(一個express)。啓動以後,它會檢測文件是否發生改變並再自動編譯一次。

1.安裝

npm install webpack-dev-server --save-dev

先經過npm將其安裝到開發目錄。安裝完成以後會在node_modules/bin下找到。前端

2.npm啓動

而後修改package.json:(基於上一節)node

 "scripts": {
    "start": "webpack-dev-server --env development",
    "build": "webpack --env production"
  }

如今就能夠經過npm run start 或者 npm start來啓動了。webpack

啓動以後,能夠看到Project is running at http://localhost:8080 上面。打開頁面nginx

說明WDS已經幫咱們自動建了一個站點.咱們修改component.js ,cmd中會出現編譯,頁面會自動刷新。git

3.直接啓動

官網介紹能夠直接經過下面的命令啓動WDS。github

webpack-dev-server --env development

但會出現webpack-dev-server --env development 不是內部命令的提示,這種問題都是環境變量的問題,將你開發的bin目錄設置到環境變量中便可,好比個人目錄是‘E:\Html5\node_modules\.bin’,就加上分號寫在後面。web

C:\Users\Administrator.9BBOFZPACSCXLG2\AppData\Roaming\npm;C:\Program Files (x86)\Microsoft VS Code\bin;E:\Html5\node_modules\.bin

4.8080端口占用

若是默認的8080端口占用,WDS會換一個。好比用nginx先發佈一個。express

   server{
      listen       8080;
      location / {
           root   E:/Html5/build;
           index  index.html index.htm;
        }
    }

再啓動WDS:npm

端口切到了8081。也能夠手動配置端口:

 devServer:{
   //...
    port: 9000
}

nodemon 自動啓動

 WDS是監視開發文件的,webpack.config.js改變不會引發自動啓動。因此咱們須要nodemon去作這件事情。

npm install nodemon --save-dev

先安裝在開發目錄,而後修改package.json:

 "scripts": {
   "start": "nodemon --watch webpack.config.js --exec \"webpack-dev-server --env development\"",
    "build": "webpack --env production"
  },

等於讓nodemon去監視webpack.config.js,變化了就去啓動它。

這樣就你可讓你的雙手專心的開發了。

代理

不過有一點疑問,就是WDS這個站點的替代性,由於咱們本身部署的nginx有一些api的代理。若是掛在WDS的這個默認站點上天然是沒法訪問的。換句話說能否給WDS配置一個刷新路徑。若是文件改變去刷新指定的地址,或者讓我去配個代理。既然它自己是一個http服務器,確定也有代理的功能。搜了下果真有: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這個字段替換成http://jsonplaceholder.typicode.com/,並將其從原地址中刪掉,這樣就能夠本身實現代理了。皆大歡喜!WDS是經過 http-proxy-middleware 來實現代理。更多參考:http://webpack.github.io/docs/webpack-dev-server.html#bypass-the-proxy;https://github.com/chimurai/http-proxy-middleware#options

but,這種刷新是怎麼實現的呢?由於頁面上沒有嵌入什麼別的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來監視文件變化,server的內部維護的有一個socket集合:

Server.prototype.sockWrite = function(sockets, type, data) {
    sockets.forEach(function(sock) {
        sock.write(JSON.stringify({
            type: type,
            data: data
        }));
    });
}

sock是一個sockjs對象。https://github.com/sockjs/sockjs-client,從http://localhost:8080/webpack-dev-server/頁面來看,sockjs是用來通訊記錄日誌的。  

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;
    },
...
}

咱們在看app.js,其中有一個OnSocketMsg 對象。

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");
    }
};
View Code

ok的時候觸發一個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();
    }
}

也就是說WDS先檢測文件是否變化,而後經過sockjs通知到客戶端,這樣就實現了刷新。以前WebSocket的第三方只用過socket.io,看起來sockjs也蠻好用的。沒必要外帶一個js,在主js裏面就能夠寫了。

小結:效率提升的一方面是將一些機械的重複性流程或動做自動化起來。WDS和nodemon就是兩個爲你幹活的小弟。 

demo:http://files.cnblogs.com/files/stoneniqiu/webpack-ch2.zip

相關文章
相關標籤/搜索