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

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

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

  1. .9-浅析webpack源码之NodeEnvironmentPlugin模块总览

    介绍Compiler的构造比较无趣,不如先过后面的,在用到compiler的时候再做讲解. 这一节主要讲这行代码: // 不管这里 compiler = new Compiler(); compile ...

  2. .3-浅析webpack源码之预编译总览

    写在前面: 本来一开始想沿用之前vue源码的标题:webpack源码之***,但是这个工具比较巨大,所以为防止有人觉得我装逼跑来喷我(或者随时鸽),加上浅析二字,以示怂. 既然是浅析,那么案例就不必太 ...

  3. .14-浅析webpack源码之Watchpack模块

    解决掉了最头疼的DirectoryWatcher内部实现,这一节可以结束NodeWatchFileSystem模块. 关于watch的应用场景,仔细思考了下,这不就是热重载的核心嘛. 首先是监视文件, ...

  4. .13-浅析webpack源码之WatcherManager模块

    从模块流可以看出,这个NodeWatchFileSystem模块非常深,这里暂时不会深入到chokidar模块,有点太偏离本系列文章了,从WatcherManager开始讲解. 流程如图: 源码非常简 ...

  5. .6-浅析webpack源码之validateSchema模块

    validateSchema模块 首先来看错误检测: const webpackOptionsValidationErrors = validateSchema(webpackOptionsSchem ...

  6. .4-浅析webpack源码之convert-argv模块

    上一节看了一眼预编译的总体代码,这一节分析convert-argv模块. 这个模块主要是对命令参数的解析,也是yargs框架的核心用处. 生成默认配置文件名数组 module.exports = fu ...

  7. .15-浅析webpack源码之WebpackOptionsApply模块-plugin事件流总览

    总体过了一下后面的流程,发现Compiler模块确实不适合单独讲解,这里继续讲解后面的代码: compiler.options = new WebpackOptionsApply().process( ...

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

    至此已完成NodeJsInputFileSysten模块的讲解,下一步就是实际实用的模块: compiler.inputFileSystem = new CachedInputFileSystem(n ...

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

    在cachedInput.output.watch三大文件系统中,output非常简单,没有必要讲,其余两个模块依赖于input模块,而input主要是引用了graceful-fs的部分API,所以这 ...

随机推荐

  1. pycharm 2017最新激活码

    BIG3CLIK6F-eyJsaWNlbnNlSWQiOiJCSUczQ0xJSzZGIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYXNzaWduZWVOYW1lIjoiI ...

  2. 数据库-SQL语句:删除和修改语句-列类型-列约束

    使用MySQL客户端连接服务器的两种方式: (1)交互模式: ——查 mysql.exe  -h127.0.0.1  -uroot  -p mysql   -uroot (2)脚本模式:——增删改 m ...

  3. Linux下配置yum

    [一]用本地文件创建本地yum源 1. 创建iso存放目录和挂载目录 mkdir /mnt/iso mkdir /mnt/cdrom 2. 将iso镜像文件上传到/mnt/iso文件夹下 3. 将/m ...

  4. 在excel中如何利用vba通过网址读取网页title(网址是https的)?

    昨天在百度知道上提了这个问题,我保存了些百度知道我回答的网址,想利用excel直接读取出网址的title,请问vba代码怎么写?(要支持https的) excel大神帮我回答了,在这记录下: Func ...

  5. 服务器安装ubuntu 14.04 server,开机启动屏幕不停滚动错误WRITE SAME failed. Manually zeroing

    昨天给服务器上安装了一个Ubuntu-14.04-server系统,安装完成后系统可以正常启动,但屏幕上一直滚动着一个错误,sda1:WRITE SAME failed. Manually zeroi ...

  6. h5移动网页唤起App

    最近这个困惑了很久,不断的有一些坑,目前还有疑问关于iOS唤起无效时会出现弹框的问题,这个最后再说 1.首先可能需要判断当前浏览器的来源(目前开发的App还没有上架,所以针对腾讯出品的大家广为人知的微 ...

  7. Reading | 《TensorFlow:实战Google深度学习框架》

    目录 三.TensorFlow入门 1. TensorFlow计算模型--计算图 I. 计算图的概念 II. 计算图的使用 2.TensorFlow数据类型--张量 I. 张量的概念 II. 张量的使 ...

  8. bash编程-cut、printf

    1. cut cut命令用于从行中截取字符串. SYNOPSIS cut OPTION... [FILE]... ** OPTION** -d CHAR:以指定的字符为分隔符(不要用空格作为分隔符): ...

  9. 附加题2 :git 简单练习

    目的: 练习git 基本操作 理解版本管理 Task 1:在码云上新建一个项目,项目命名 『Helloworld』 ,设置为公开访问. step 0: 在你计算机安装git.参照互联网教程<gi ...

  10. 【sping揭秘】14、@before @AfterThrowing

    @before 基础模式,我们先直接看一下就可以了,比较简单,网上一堆... 不是我装逼哈,我学了那么久spring,aop的皮毛也就是网上的那些blog内容,稍微高级点的我也不会,这里跳过基础部分 ...