node实现watcher的困境
@(node,watcher)
watcher,在如今的前端领域已经数见不鲜了。目前流行的gulp流程工具提供了watcher的选项,是我们在开发过程中不需要手动进行触发构建流程,转而根据文件(目录)内容改变来触发。
深入到watcher实现层,其实是基于node的fs.watch API,但是fs.watch有很多“不确定性”,下文会一一解答。
fs.watch
(fs.FSWatcher) fs.watch(filename[, options][, listener])
watch API很简单,接受三个参数,并返回一个FSWatcher对象。
filename可以是文件,也可是目录;
options为可选对象,默认为 { persistent: true, recursive: false },其中persistent属性意味着:watcher进程会一直watch该文件(目录),即watcher进程阻塞;recursive属性意味着:如果监听的是目录,则目录下属的目录和文件也会被监听,recursive属性存在兼容性问题,在linux系统下无效,在windows和OSX下正常。
listener为回调函数,接受两个参数,分别为event和filename,其中事件有两种类型,“rename”和“change”,而filename也有兼容性问题,在使用时也要注意兼容性判断。
问题
在上一节中简单介绍了watch API,也简单提到了一些兼容性问题,在此列举出来:
- recursive属性在linux下失效;
- watch目录时,回调函数中的filename只在linux和windows下可以获取;
- node在任何情况下都不确保filename可以获取到
解决方案
轮训
node提供了另一个接口,
fs.watchFile(filename[, options], listener)
返回值同为FSWatcher,参数filename可为目录和文件,options默认为
{ persistent: true, interval: 5007 },其中interval则为node轮训该文件的时间间隔,listener接受两个参数,即类行为fs.Stat的curr和prev对象,我们可通过
curr.mtime == prev.mtime
判断文件是否发生改动。
不管在何种系统设计中,轮训的方式都是兼容性保底方案,只要我们的系统支持fs.watch方法,就不用采用该种方式进行兼容。
那么合适可以采用轮训呢?我认为,大概分两种情况:
- 需要针对文件的元信息判断是否触发事件
- 监控的文件所在的操作系统,如果是NFS, SMB等网络文件系统,fs.watch并不提供功能,因此只能使用轮训方式(watch方法是基于文件系统的特性编写的,在linux下基于“inotify”,windows下基于“ReadDirectoryChangesW”)
手动适配
针对非网络文件系统,watch API的兼容性就在于是否递归watch以及OSX下filename获取的问题,因此我们可以通过编码方式解决:
- 采用默认的options配置,即
{ persistent: true, recursive: false },通过walker便利目录,针对单个文件作watcher - 针对单个文件做watch,OSX可以获取到filename
通过简单的处理,一个简易的watcher就实现了,配合着EventEmit,就可以通过事件的方式完成watcher任务。
参考代码:
'use strict';
var fs = require('fs');
var path = require('path');
var os = require('os');
var watchList = {};
var timer = {};
var walk = function (dir, callback, filter) {
fs.readdirSync(dir).forEach(function (item) {
var fullname = path.join(dir, item);
if (fs.statSync(fullname).isDirectory()){
if (!filter(fullname)){
return;
}
watch(fullname, callback, filter);
walk(fullname, callback, filter);
}
});
};
var watch = function (name, callback, filter) {
if (watchList[name]) {
watchList[name].close();
}
watchList[name] = fs.watch(name, function (event, filename) {
if (filename === null) {
return;
}
var fullname = path.join(name, filename);
var type;
var fstype;
if (!filter(fullname)) {
return;
}
// 检查文件、目录是否存在
if (!fs.existsSync(fullname)) {
// 如果目录被删除则关闭监视器
if (watchList[fullname]) {
fstype = 'directory';
watchList[fullname].close();
delete watchList[fullname];
} else {
fstype = 'file';
}
type = 'delete';
} else {
// 文件
if (fs.statSync(fullname).isFile()) {
fstype = 'file';
type = event == 'rename' ? 'create' : 'updated';
// 文件夹
} else if (event === 'rename') {
fstype = 'directory';
type = 'create';
watch(fullname, callback, filter);
walk(fullname, callback, filter);
}
}
var eventData = {
type: type,
target: filename,
parent: parent,
fstype: fstype
};
if (/windows/i.test(os.type())) {
// window 下的兼容处理
clearTimeout(timer[fullname]);
timer[fullname] = setTimeout(function() {
callback(eventData);
}, 16);
} else {
callback(eventData);
}
});
};
/**
* @param {String} 要监听的目录
* @param {Function} 文件、目录改变后的回调函数
* @param {Function} 过滤器(可选)
*/
module.exports = function (dir, callback, filter) {
// 排除“.”、“_”开头或者非英文命名的目录
var FILTER_RE = /[^\w\.\-$]/;
filter = filter || function (name) {
return !FILTER_RE.test(name);
};
watch(dir, callback, filter);
walk(dir, callback, filter);
};
node实现watcher的困境的更多相关文章
- Zookeeper——Watcher原理详解
文章目录 引言 正文 一.如何注册监听 二.如何触发监听事件 三.事件类型有哪些 四.Watcher可以被无限次触发么?为什么要这么设计? 五.Watcher实现原理 1. 客服端发送请求 a. 初始 ...
- [转载] 跟着实例学习zookeeper 的用法
原文: http://ifeve.com/zookeeper-curato-framework/ zookeeper 的原生客户端库过于底层, 用户为了使用 zookeeper需要编写大量的代码, 为 ...
- zookeeper 学习笔记 (C语言版本)
1.zookeeper简介 zookeeper是Hadoop的子项目,在大型分布式系统中,zookeeper封装好了一些复杂易出错的服务,提供简单易用的接口,给使用者提供高效稳定的服务.这些服务包括配 ...
- Vue2.0源码阅读笔记--双向绑定实现原理
上一篇 文章 了解了Vue.js的生命周期.这篇分析Observe Data过程,了解Vue.js的双向数据绑定实现原理. 一.实现双向绑定的做法 前端MVVM最令人激动的就是双向绑定机制了,实现双向 ...
- JavaScript 实现一个简单的MVVM前端框架(ES6语法)
前言 随着前端各大框架的崛起,为我们平时的开发带来了相当的便利,我们不能一直停留在应用层面,今天就自己动手实现一个乞丐版的MVVM小框架 完整代码github地址 效果 html代码 <div ...
- CuratorFramework使用
CuratorFrameworkFramework是ZooKeeper Client更高的抽象API 自动连接管理: 1. 当ZooKeeper客户端内部出现异常, 将自动进行重连或重试, 该过程对外 ...
- vue中的双向数据绑定详解
前言 什么是数据双向绑定? vue是一个mvvm框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化.这也算是vue的精髓之处了.值得注意的是,我 ...
- Vue 双向绑定原理
Vue.js最核心的功能有两个,一是响应式的数据绑定系统,二是组件系统. 一.访问器属性:Object.defineProperty ECMAScript 262v5带来的新东西,FF把它归入为jav ...
- vue 之 双向绑定原理
一.实现双向绑定 详细版: 前端MVVM实现双向数据绑定的做法大致有如下三种: 1.发布者-订阅者模式(backbone.js) 思路:使用自定义的data属性在HTML代码中指明绑定.所有绑定起来的 ...
随机推荐
- 用dubbo时遇到的一个序列化的坑
首先,这是标题党,问题并不是出现在序列化上,这是报错的一部分: Caused by: com.alibaba.dubbo.remoting.RemotingException: Failed to s ...
- 搞个这样的APP要多久?
这是一个“如有雷同,纯属巧合”的故事,外加一些废话,大家请勿对号入座.开始了…… 我有些尴尬地拿着水杯,正对面坐着来访的王总,他是在别处打拼的人,这几年据说收获颇丰,见移动互联网如火如荼,自然也想着要 ...
- Python高手之路【五】python基础之正则表达式
下图列出了Python支持的正则表达式元字符和语法: 字符点:匹配任意一个字符 import re st = 'python' result = re.findall('p.t',st) print( ...
- angularjs 依赖注入--自己学着实现
在用angular依赖注入时,感觉很好用,他的出现是 为了"削减计算机程序的耦合问题" ,我怀着敬畏与好奇的心情,轻轻的走进了angular源码,看看他到底是怎么实现的,我也想写个 ...
- webstorm下载&&安装过程&&打开项目
一.webstorm下载 WebStorm 是jetbrains公司旗下一款JavaScript 开发工具.被广大中国JS开发者誉为"Web前端开发神器"."最强大的HT ...
- Atitit.attilax软件研发与项目管理之道
Atitit.attilax软件研发与项目管理之道 1. 前言4 2. 鸣谢4 3. Genesis 创世记4 4. 软件发展史4 5. 箴言4 6. 使徒行传 4 7. attilax书 4 8. ...
- 【MySql】查询数据库中所有表及列的信息
SELECT TABLE_NAME, -- 表名 COLUMN_NAME, -- 字段名 DATA_TYPE, -- 字段类型 COLUMN_COMMENT -- 字段注释 FROM INFORMAT ...
- Vue.js——60分钟组件快速入门(上篇)
组件简介 组件系统是Vue.js其中一个重要的概念,它提供了一种抽象,让我们可以使用独立可复用的小组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树: 那么什么是组件呢?组件可以扩展HTML ...
- On cloud, be cloud native
本来不想起一个英文名,但是想来想去都没能想出一个简洁地表述该意思的中文释义,所以就用了一个英文名称,望见谅. Cloud Native是一个刚刚由VMware所提出一年左右的名词.其表示在设计并实现一 ...
- Spring8:一些常用的Spring Bean扩展接口
前言 Spring是一款非常强大的框架,可以说是几乎所有的企业级Java项目使用了Spring,而Bean又是Spring框架的核心. Spring框架运用了非常多的设计模式,从整体上看,它的设计严格 ...