.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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...