.12-淺析webpack源碼之NodeWatchFileSystem模塊總覽

来源:http://www.cnblogs.com/QH-Jimmy/archive/2017/12/18/8059129.html
-Advertisement-
Play Games

剩下一個watch模塊,這個模塊比較深,先大概過一下整體涉及內容再分部講解。 流程圖如下: NodeWatchFileSystem 除去細節代碼,該模塊大體如下; 1、引入Watchpack模塊 2、接受一個inputFileSystem作為構造函數的參數 3、根據配置選項實例化一個Watchpac ...


  剩下一個watch模塊,這個模塊比較深,先大概過一下整體涉及內容再分部講解。

  流程圖如下:

NodeWatchFileSystem

const Watchpack = require("watchpack");

class NodeWatchFileSystem {
    constructor(inputFileSystem) {
        this.inputFileSystem = inputFileSystem;
        this.watcherOptions = {
            aggregateTimeout: 0
        };
        this.watcher = new Watchpack(this.watcherOptions);
    }

    watch(
        files, /*Array*/
        dirs, /*Array*/
        missing, /*Array*/
        startTime, /*number*/
        options, /*object*/
        callback, /*function*/
        callbackUndelayed /*function*/
    ) {
        // params validate...
        const oldWatcher = this.watcher;
        // 生成Watchpack對象
        this.watcher = new Watchpack(options);
        if (callbackUndelayed)
            this.watcher.once("change", callbackUndelayed);
        this.watcher.once("aggregated", (changes, removals) => { /**/ });
        // 調用watch方法
        this.watcher.watch(files.concat(missing), dirs.concat(missing), startTime);
        if (oldWatcher) {
            oldWatcher.close();
        }
        return {
            close: () => { /**/ },
            pause: () => { /**/ }
        };
    }
}

module.exports = NodeWatchFileSystem;

  除去細節代碼,該模塊大體如下;

1、引入Watchpack模塊

2、接受一個inputFileSystem作為構造函數的參數

3、根據配置選項實例化一個Watchpack類

4、核心watch方法為調用實例類的watch方法,傳入給定參數,綁定兩個一次性事件綁定並返回了一個對象

  模塊核心的方法調用的是Watchpack實體類上的,所以需要進一步探究該類。

  該模塊涉及到了nodejs的event模塊,內容非常簡單,這裡就不做介紹了,詳情可查看官網API:https://nodejs.org/dist/latest-v8.x/docs/api/events.html

 

Watchpack

var watcherManager = require("./watcherManager");
var EventEmitter = require("events").EventEmitter;
Watchpack.prototype = Object.create(EventEmitter.prototype);

class Watchpack {
    constructor(options) {
        EventEmitter.call(this);
        if (!options) options = {};
        if (!options.aggregateTimeout) options.aggregateTimeout = 200;
        this.options = options;
        this.watcherOptions = {
            ignored: options.ignored,
            poll: options.poll
        };
        this.fileWatchers = [];
        this.dirWatchers = [];
        this.mtimes = Object.create(null);
        this.paused = false;
        this.aggregatedChanges = [];
        this.aggregatedRemovals = [];
        this.aggregateTimeout = 0;
        this._onTimeout = this._onTimeout.bind(this);
    }
    watch(files, directories, startTime) {
        this.paused = false;
        var oldFileWatchers = this.fileWatchers;
        var oldDirWatchers = this.dirWatchers;
        this.fileWatchers = files.map(function(file) {
            return this._fileWatcher(file, watcherManager.watchFile(file, this.watcherOptions, startTime));
        }, this);
        this.dirWatchers = directories.map(function(dir) {
            return this._dirWatcher(dir, watcherManager.watchDirectory(dir, this.watcherOptions, startTime));
        }, this);
        oldFileWatchers.forEach(function(w) {
            w.close();
        }, this);
        oldDirWatchers.forEach(function(w) {
            w.close();
        }, this);
    };
    pause() { /**/ };
    getTimes() { /**/ };
    _fileWatcher(file, watcher) { /**/ };
    _dirWatcher(item, watcher) { /**/ };
    _onChange(item, mtime, file) { /**/ };
    _onRemove(item, file) { /**/ };
    _onTimeout() { /**/ };
    close() { /**/ };
}

module.exports = Watchpack;

function addWatchersToArray(watchers, array) { /**/ }

  本模塊引入了並繼承了nodejs的EventEmitter,並引入了新模塊watcherManager,主要內容羅列如下:

1、構造函數接受一個對象,鍵包括aggregateTimeout、ignored、poll,本例只傳入第一個並設置為0

2、核心方法為watch,依賴於引入的watchManager模塊

3、其餘方法均為工具方法

 

WatcherManager

var path = require("path");

class WatcherManager {
    constructor() {
        this.directoryWatchers = {};
    };
    // 工廠函數
    getDirectoryWatcher(directory, options) {
        // 引入模塊
        var DirectoryWatcher = require("./DirectoryWatcher");
        options = options || {};
        var key = directory + " " + JSON.stringify(options);
        if (!this.directoryWatchers[key]) {
            this.directoryWatchers[key] = new DirectoryWatcher(directory, options);
            // 文件監視結束則從容器刪除
            this.directoryWatchers[key].on("closed", function() {
                delete this.directoryWatchers[key];
            }.bind(this));
        }
        return this.directoryWatchers[key];
    };
    // 監視文件
    watchFile(p, options, startTime) {
        var directory = path.dirname(p);
        return this.getDirectoryWatcher(directory, options).watch(p, startTime);
    };
    // 監視目錄
    watchDirectory(directory, options, startTime) {
        return this.getDirectoryWatcher(directory, options).watch(directory, startTime);
    };
}
module.exports = new WatcherManager();

  可以看出這是一個中間處理函數,其中構造函數生成了一個容器,容器的鍵為目錄+參數生成的一個字元串,當監視關閉後會並立即刪除。

  這個模塊類似於tapable,是一個監視對象管理器。

 

  然後是監視核心實現模塊,模塊內容比較多,這裡只簡單看一下構造函數以及watch方法:

var EventEmitter = require("events").EventEmitter;
var async = require("async");
var chokidar = require("chokidar");
var fs = require("graceful-fs");

class Watcher {
    constructor(directoryWatcher, filePath, startTime) {
        EventEmitter.call(this);
        this.directoryWatcher = directoryWatcher;
        this.path = filePath;
        this.startTime = startTime && +startTime;
        this.data = 0;
    };
    checkStartTime(mtime, initial) { /**/ };
    close() { /**/ };
}

function DirectoryWatcher(directoryPath, options) {
    EventEmitter.call(this);
    this.options = options;
    this.path = directoryPath;
    this.files = Object.create(null);
    this.directories = Object.create(null);
    this.watcher = chokidar.watch(directoryPath, {
        ignoreInitial: true,
        persistent: true,
        followSymlinks: false,
        depth: 0,
        atomic: false,
        alwaysStat: true,
        ignorePermissionErrors: true,
        ignored: options.ignored,
        usePolling: options.poll ? true : undefined,
        interval: typeof options.poll === "number" ? options.poll : undefined,
        disableGlobbing: true
    });
    this.watcher.on("add", this.onFileAdded.bind(this));
    this.watcher.on("addDir", this.onDirectoryAdded.bind(this));
    this.watcher.on("change", this.onChange.bind(this));
    this.watcher.on("unlink", this.onFileUnlinked.bind(this));
    this.watcher.on("unlinkDir", this.onDirectoryUnlinked.bind(this));
    this.watcher.on("error", this.onWatcherError.bind(this));
    // ...
}

DirectoryWatcher.prototype.watch = function watch(filePath, startTime) {
    this.watchers[withoutCase(filePath)] = this.watchers[withoutCase(filePath)] || [];
    this.refs++;
    var watcher = new Watcher(this, filePath, startTime);
    watcher.on("closed", function() { /**/ }.bind(this));
    // ...
    return watcher;
};

// ...

module.exports = DirectoryWatcher;

  從構造函數和模塊引入可以得到很多信息,如下:

1、引入了graceful-js模塊,可以看出底層還是利用nodejs的fs模塊來進行監視

2、所有的監視事件都是基於nodejs的EventEmitter模塊來進行操作

3、內部還有一個輔助類Watcher

4、根據構造函數的代碼,監視的操作包含(可能不限於)新增文件、新增文件夾、改變內容、刪除文件、刪除文件夾等

 

  async模塊是一個類似於tapable的輔助工具,用於非同步處理批量方法,詳細內容可自行去網上查閱。

  構造函數中,該模塊又再次引用了chokidar模塊,並調用其watch方法進行初始化,看似調用方法,源碼簡化後如下:

class FSWatcher {
    // ...
}
exports.FSWatcher = FSWatcher;
exports.watch = function(paths, options) {
    return new FSWatcher(options).add(paths);
};

  假的,這還是個new操作,只是為了方便把兩步合成到了一個方法中。

 

  所有的模塊整理如上,下麵幾節再來剖析每一塊內容。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 原文鏈接:https://www.cnblogs.com/itbainianmei/p/6062249.html 1.每個組件模板template,不再支持片段代碼 之前: <template> <h3>vue-router+vue-loader</h3> <p>hshsh</p> </templa ...
  • 在把微信自定義菜單編輯工具加上又拍雲CDN嘗嘗鮮時,發現頁面上的JS莫名的報錯了。在排查許久無果後,突然想起CDN有個頁面壓縮功能。 菜單編輯器頁面部分JS腳本使用到了頁面註釋進行了語法綁定,CDN開啟壓縮後,直接去除了語法綁定代碼導致程式報錯。 ...
  • IE瀏覽器下的漸變背景 IE瀏覽器下漸變背景的使用需要使用IE的漸變濾鏡。如下代碼: filter: progid:DXImageTransform.Microsoft.gradient(startcolorstr=red,endcolorstr=blue,gradientType=1); 相關說明 ...
  • 萬事開頭難,一個好的Hello World程式可以節省我們好多的學習時間,幫助我們快速入門。Hello World程式之所以是入門必讀必會,就是因為其代碼量少,簡單易懂。但我覺得,還應該做到功能豐富,涉及的知識點多。這樣才是一個好的初學者入門指引程式。 之所以選擇Vue,不僅因為其流行,還因為其輕量 ...
  • 前話 好久沒寫教程了(可能會誤導新手的菜鳥教程( ̄▽ ̄)")。 這是我的github,歡迎前端大大們和我一起學習交流 "https://github.com/pwcong" 最近入職公司做前端實習,這幾個星期來學到了移動端H5頁面適配。(以前根本沒做過移動端網頁/(ㄒoㄒ)/~~,還是微信端的) 所 ...
  • 來自:https://segmentfault.com/a/1190000009294321 (侵刪) git 源碼地址 https://github.com/ustbhuangyi/vue-sell(侵刪) html代碼 生成一個動畫小球的div,並且生成五個小球,五個是為了生成一定數量的小球來作 ...
  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.or ...
  • Vue Vue近幾年來特別的受關註,三年前的時候angularJS霸占前端JS框架市場很長時間,接著react框架橫空出世,因為它有一個特性是虛擬DOM,從性能上碾軋angularJS,這個時候,vue1.0悄悄 的問世了,它的優雅,輕便也吸引了一部分用戶,開始收到關註,16年中旬,VUE2.0問世 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...