.12-浅析webpack源码之NodeWatchFileSystem模块总览

  剩下一个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类,并根据传入参数调用类的watch方法

4、绑定两个一次性事件绑定并返回了一个对象

  模块核心的方法调用的是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、根据构造函数的代码,监视的操作包含(可能不限于)新增文件、新增文件夹、改变内容、删除文件、删除文件夹等

  所有的模块整理如上,下面几节再来剖析每一块内容。

时间: 2024-07-30 21:28:53

.12-浅析webpack源码之NodeWatchFileSystem模块总览的相关文章

.10-浅析webpack源码之graceful-fs模块

在cachedInput.output.watch三大文件系统中,output非常简单,没有必要讲,其余两个模块依赖于input模块,而input主要是引用了graceful-fs的部分API,所以这节来讲讲graceful-fs. 上一节整理的源码如下: var fs = require('fs') // ...工具方法 module.exports = patch(require('./fs.js')) if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATC

.11-浅析webpack源码之Storage模块

至此已完成NodeJsInputFileSysten模块的讲解,下一步就是实际实用的模块: compiler.inputFileSystem = new CachedInputFileSystem(new NodeJsInputFileSystem(), 60000); 挂载到compiler对象上的输入模块其实是带有缓存的输入模块,源码整理如下(用ES6的class重写): class CachedInputFileSystem { constructor() { // fileSystem

.15-浅析webpack源码之WebpackOptionsApply模块之插件王中王

总体过了一下后面的流程,发现Compiler模块确实不适合单独讲解,这里继续讲解后面的代码: compiler.options = new WebpackOptionsApply().process(options, compiler); 这行代码与之前设置options默认值非常相似,但是复杂程度根本不是一个次元的. 这一节只能简单的看一眼内部到底有多少东西,整理后源码如下: "use strict"; const OptionsApply = require("./Opt

webpack源码-依赖收集

webpack源码-依赖收集 version:3.12.0 程序主要流程: 触发make钩子 Compilation.js 执行EntryOptionPlugin 中注册的make钩子 执行compilation.addEntry 执行compilation._addModuleChain Compilation.js 执行moduleFactory.create(this.semaphore.avaiable 初始化为100) Compilation.js 执行this.buildModule

nginx源码分析之模块初始化

在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要通过结合源码来分析模块的初始化过程. 稍微了解nginx的人都知道nginx是高度模块化的,各个功能都封装在模块中,而各个模块的初始化则是根据配置文件来进行的,下面我们会看到nginx边解析配置文件中的指令,边初始化指令所属的模块,指令其实就是指示怎样初始化模块的. 模块初始化框架 模块的初始化主要

nginx源码分析--nginx模块解析

nginx的模块非常之多,可以认为所有代码都是以模块的形式组织,这包括核心模块和功能模块,针对不同的应用场合,并非所有的功能模块都要被用到,附录A给出的是默认configure(即简单的http服务器应用)下被连接的模块,这里虽说是模块连接,但nginx不会像apache或lighttpd那样在编译时生成so动态库而在程序执行时再进行动态加载,nginx模块源文件会在生成nginx时就直接被编译到其二进制执行文件中,所以如果要选用不同的功能模块,必须对nginx做重新配置和编译.对于功能模块的选

thttpd源码解析 定时器模块

thttpd源码解析 定时器模块 thttpd是非常轻量级的http服务器,可执行文件仅50kB.名称中的第一个t表示tiny, turbo, 或throttling 与lighttpd.memcached.redis相比非常小巧,仅有不到8k行,而后三者大小分别为:60k,13k,86k 支持HTTP/1.1和CGI:采用IO复用实现,单线程,可移植:实现了基于URL的文件流量限制功能 特别适用于大量静态数据访问的场景,如图片存储 2004年已经停止维护,有一个关于X-Forwarded-Fo

webpack源码分析——配置调试环境

无论是阅读webpack源码,还是编写webpack的plugin和loader,配置调试环境都是很有必要的.weabpack的运行环境是nodejs,调试webpack就是调试nodejs程序.我们平时使用的IDE如eclipse.webstorm都支持nodejs的调试.本文以eclipse(Version: Oxygen.1a Release (4.7.1a))为例,进行讲解. 在这个例子里面,我们使用webpack <entry> [<entry>] <output&

兄弟连区块链教程open-ethereum-pool矿池源码分析unlocker模块

兄弟连区块链教程open-ethereum-pool以太坊矿池源码分析unlocker模块open-ethereum-pool以太坊矿池-unlocker模块 unlocker模块配置 json"unlocker": {????"enabled": false,????"poolFee": 1.0,????"poolFeeAddress": "",????"donate": true,?