fs模塊是一個(gè)比較龐大的模塊,在前面也介紹了該模塊中最核心的一點(diǎn)東西,雖然核心的這點(diǎn)東西,在整個(gè)fs模塊中占據(jù)的比例比較小,但是如果只是我們平常使用的話,基本已經(jīng)夠用了,其他的一些方法,屬于能力提升時(shí)需要學(xué)習(xí)的的內(nèi)容了,所以在后面就不再繼續(xù)了,本篇屬于fs模塊中的最后一篇,也不是把fs模塊中的其他API都給一一列舉出來(lái),這里再說(shuō)最后一個(gè)我看來(lái)很重要的方法,監(jiān)聽(tīng)文件或者目錄的的方法watchFile。
概總
這里之所以在最后把這個(gè)watchFile方法寫入到這里,是因?yàn)樵谇岸说囊粋€(gè)流行的構(gòu)建工具grunt中,有一個(gè)grunt-contrib-watch模塊,可以用于監(jiān)聽(tīng)整個(gè)項(xiàng)目中,文件是否有變化,不知道有沒(méi)有人去看過(guò)該部分的源碼,是如何實(shí)現(xiàn)這個(gè)模塊的呢?(我是還沒(méi)有去看過(guò),基礎(chǔ)學(xué)習(xí)完成之后,再去研究下)
所以,這里提前看下,fs模塊中的watchFile是如何實(shí)現(xiàn)的,等以后去看grunt中的watch模塊時(shí),就可以更得心應(yīng)手了,所以,想法和我相同的朋友們,就繼續(xù)看下去吧。。
fs.watchFile方法
該方法是用于監(jiān)聽(tīng)指定文件的一個(gè)方法,其使用方法為
fs.watchFile(filename,[option],listener);
其中:
1:filename:必須,需要被監(jiān)聽(tīng)的文件的完整的路徑以及文件名
2:option:可選,option支持兩個(gè)參數(shù),persistent屬性和interval屬性:
3:listener:必須,被監(jiān)聽(tīng)文件發(fā)生改變時(shí)調(diào)用的回調(diào)函數(shù)
回調(diào)函數(shù)傳入兩個(gè)參數(shù)callback(curr,prev),它們都是fs.Stats的實(shí)例,關(guān)于該實(shí)例的詳細(xì)介紹,請(qǐng)參考前篇文章,curr表示修改之后的的信息對(duì)象,prev表示本次修改之前的信息對(duì)象。
下面看下,一個(gè)示例:
var fs = require("fs");  fs.watchFile("./message.txt",function(curr,prev){   if(Date.parse(prev.ctime) == 0){     console.log("message.txt被創(chuàng)建");   }else if(Date.parse(curr.ctime) == 0){     console.log("message.txt被刪除");   }else if(Date.parse(prev.mtime) != Date.parse(curr.mtime)){     console.log("message.txt被修改");   } }); 運(yùn)行上述代碼,然后在與你.js的文件的同目錄下,進(jìn)行操作,創(chuàng)建message.txt,修改,刪除等操作,來(lái)查看控制臺(tái)的顯示。這只是一個(gè)簡(jiǎn)單的演示,如果需要其他的數(shù)據(jù),那么就可以查看curr和prev中,能攜帶的數(shù)據(jù),然后根據(jù)不同的數(shù)據(jù),完成不同的操作。也就自己實(shí)現(xiàn)一些插件的功能。
當(dāng)然,也可以通過(guò)設(shè)置option的屬性值,使用不同的配置來(lái)監(jiān)聽(tīng)對(duì)應(yīng)的文件,這里關(guān)于配置新的示例,就不再占用篇幅了,有興趣的可以自己測(cè)試一下。
watchFile的源碼實(shí)現(xiàn)
看完了示例,接下來(lái)就是源碼了,只有了解了最根本的源碼實(shí)現(xiàn),才能更好更高效的使用對(duì)應(yīng)的API,請(qǐng)認(rèn)真看源碼中的注釋:
// Stat Change Watchers // StatWatcher構(gòu)造函數(shù)定義 function StatWatcher() {   //把EventEmitter內(nèi)部的實(shí)例化屬性添加到this對(duì)象上去。   //而EventEmitter的原型鏈屬性和方法,不會(huì)被添加到this對(duì)象   //所以,基本上,也就是把EventEmitter實(shí)例中的domain,_events,_maxListeners這三個(gè)屬性   //添加到了this對(duì)象上去了。   EventEmitter.call(this);    //把this緩存到self變量中,便于下面的閉包回調(diào)使用該創(chuàng)建閉包時(shí)的this對(duì)象   var self = this;    //調(diào)用C++實(shí)現(xiàn)的StatWatcher構(gòu)造函數(shù),并把返回的對(duì)象,賦值到this對(duì)象的_handle屬性上   this._handle = new binding.StatWatcher();    // uv_fs_poll is a little more powerful than ev_stat but we curb it for   // the sake of backwards compatibility   var oldStatus = -1;    //當(dāng)C++中實(shí)現(xiàn)的StatWatcher實(shí)例化后的對(duì)象,定義它的onchange事件。   // 我測(cè)試過(guò)new binding.StatWatcher();實(shí)例化之后,是沒(méi)有onchange屬性的   // 所以,這里應(yīng)該是屬于直接定義改屬性的,那么定義之后,在nodejs的C++代碼實(shí)現(xiàn)中   // 是如何判斷這個(gè)屬性存在,然后在符合一定的條件下,又去執(zhí)行這個(gè)屬性的呢?   // 這是我疑惑的地方,這個(gè)需要當(dāng)學(xué)習(xí)到更多這方面的知識(shí)后,再去了解一下。   // 經(jīng)過(guò)我的測(cè)試,這個(gè)屬性,是在實(shí)例的start執(zhí)行時(shí)需要的,如果沒(méi)有定義該屬性   // 那么,在使用start方法,開(kāi)始監(jiān)聽(tīng)事件時(shí),會(huì)被拋出異常的   // 拋出異常,是因?yàn)槟惚O(jiān)聽(tīng)的文件,當(dāng)前不存在~~   // 如果監(jiān)聽(tīng)的文件,當(dāng)前已經(jīng)存在,則不會(huì)執(zhí)行onchange的回調(diào)   this._handle.onchange = function(current, previous, newStatus) {     // 當(dāng)實(shí)例被話之后,當(dāng)被監(jiān)聽(tīng)的文件,被更改時(shí),都會(huì)觸發(fā)該屬性的回調(diào)函數(shù)     // 并且傳入三個(gè)參數(shù)      // 這里的三個(gè)判斷,當(dāng)前不知道為什么會(huì)在這個(gè)時(shí)候,不執(zhí)行~~     if (oldStatus === -1 &&       newStatus === -1 &&       current.nlink === previous.nlink) return;      oldStatus = newStatus;      // 觸發(fā)self對(duì)象的中的change事件,并且把current和previous對(duì)象,     // 傳入到change事件的回調(diào)函數(shù)     // 在本構(gòu)造函數(shù)內(nèi)部,是沒(méi)有繼承EventEmitter構(gòu)造函數(shù)原型鏈中的方法的     // 但是這里,卻使用了原型鏈中的emit方法。why?     self.emit('change', current, previous);   };    this._handle.onstop = function() {     self.emit('stop');   }; }  // 把EventEmitter原型鏈的屬性和方法,擴(kuò)展到StatWatcher對(duì)象的原型鏈中 // 更確切的說(shuō)明就是,StatWatcher.prototype = EventEmitter.prototype; util.inherits(StatWatcher, EventEmitter);  // 在StatWatcher重新定義來(lái)原型鏈之后,再執(zhí)行其他的擴(kuò)展,以防止原型鏈斷鏈的情況 StatWatcher.prototype.start = function(filename, persistent, interval) {   nullCheck(filename);   this._handle.start(pathModule._makeLong(filename), persistent, interval); };  StatWatcher.prototype.stop = function() {   this._handle.stop(); };  //緩存Watcher的一個(gè)對(duì)象 var statWatchers = {}; function inStatWatchers(filename) {   //判斷filename是否在statWatchers中,如果是則返回緩存的實(shí)例   return Object.prototype.hasOwnProperty.call(statWatchers, filename) &&   statWatchers[filename]; }  fs.watchFile = function(filename) {   //判斷fileName是否合法   //如果不合法,則拋出一個(gè)異常然后停止執(zhí)行   nullCheck(filename);    //調(diào)用path模塊的方法,返回文件的絕對(duì)路徑   filename = pathModule.resolve(filename);   var stat;   var listener;    //默認(rèn)的配置信息,這里也說(shuō)明來(lái)下,為何監(jiān)聽(tīng)間隔為5007ms,   //只是,我表示,我是沒(méi)有看懂下面的英文注釋要說(shuō)啥的   var options = {     // Poll interval in milliseconds. 5007 is what libev used to use. It's     // a little on the slow side but let's stick with it for now to keep     // behavioral changes to a minimum.     interval: 5007,     persistent: true   };    //對(duì)參數(shù)進(jìn)行判斷,判斷是否有自定義的option,如果有,使用自定義的   //沒(méi)有定義的,使用默認(rèn)值   //回調(diào)函數(shù)賦值   if (util.isObject(arguments[1])) {     options = util._extend(options, arguments[1]);     listener = arguments[2];   } else {     listener = arguments[1];   }    //回調(diào)函數(shù)是必須的,如果沒(méi)有回調(diào)函數(shù),則直接拋出一個(gè)異常,并停止運(yùn)行   if (!listener) {     throw new Error('watchFile requires a listener function');   }    //看完上面的inStatWatchers的源碼之后,覺(jué)得這里是否可以再次優(yōu)化一次?   //stat =inStatWatchers(filename);   //if(!stat){     //stat = statWatchers[filename] = new StatWatcher();     //stat.start(filename,options.persistent,options.interval);   //}   //這樣的話,就可以節(jié)省一次對(duì)象的查找和取值了    //判斷該文件,是否已經(jīng)創(chuàng)建了StatWatcher實(shí)例   if (inStatWatchers(filename)) {     //如果之前已經(jīng)創(chuàng)建過(guò)了,則使用之前創(chuàng)建過(guò)的StatWatcher實(shí)例     stat = statWatchers[filename];   } else {     //如果沒(méi)有,則 重新創(chuàng)建一個(gè),并把創(chuàng)建的示例,保存下來(lái)     //用于接下來(lái)的,玩意又對(duì)該文件再次添加watchFile時(shí),使用     stat = statWatchers[filename] = new StatWatcher();     //并且,對(duì)實(shí)例執(zhí)行start的方法,應(yīng)該是啟動(dòng)該實(shí)例不     stat.start(filename, options.persistent, options.interval);   }    //對(duì)該實(shí)例,監(jiān)聽(tīng)change事件,并設(shè)置回調(diào)函數(shù)   stat.addListener('change', listener);    return stat; }; 注:在前面的源碼解釋中,出現(xiàn)了幾個(gè)概念,我認(rèn)為是有必要在這里補(bǔ)充一下的,因?yàn)槿绻麑?duì)這幾個(gè)概念的了解不深刻,那么久可能不理解為什么會(huì)這么寫,或者意識(shí)不到,必須這么寫才能保證代碼正確的執(zhí)行的。
1:只繼承實(shí)例內(nèi)部屬性和方法,涉及代碼:EventEmitter.call(this);
2:原型鏈的斷鏈概念,涉及代碼:util.inherits(StatWatcher, EventEmitter);
上述兩個(gè)概念的一些相關(guān)內(nèi)容,請(qǐng)參考:對(duì)象繼承的方法小結(jié),原型鏈斷鏈的原因,淺析賦值表達(dá)式 主站蜘蛛池模板: 丰都县| 额尔古纳市| 东乡县| 鱼台县| 崇仁县| 齐齐哈尔市| 南平市| 文登市| 滦平县| 神池县| 高安市| 古丈县| 漳州市| 永安市| 密云县| 竹山县| 台东县| 盖州市| 麦盖提县| 绵阳市| 甘孜县| 河间市| 邹平县| 台湾省| 精河县| 饶平县| 育儿| 澳门| 临清市| 兴城市| 和龙市| 马山县| 长垣县| 鄱阳县| 满洲里市| 临夏市| 铅山县| 陆河县| 沧源| 襄汾县| 温州市|