zoukankan      html  css  js  c++  java
  • .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类

    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操作,只是为了方便把两步合成到了一个方法中。

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

  • 相关阅读:
    macOS 上配置 Lua
    Oracle.ManagedDataAccess.dll
    offer
    Costura.Fody
    日志系统
    实战框架ABP
    什么是算法?
    HTTP状态码->HTTP Status Code
    How to untar a TAR file using Apache Commons
    python实践3:cursor() — 数据库连接操作
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/8059129.html
Copyright © 2011-2022 走看看