銜接上文闷愤,由于每次修改文件课蔬,都會觸發(fā)重新全量打包何暇,在項目體積幾乎為0的情況下要花費3.5s句柠,希望能提升性能咸包,只對修改過的文件進行打包桃序,但由于我們沒有用到代碼拆分code splitting
,所以此處優(yōu)化為:不會codeSplicing未更改的文件
思路
- 精確獲取更改的文件
- 將文件路徑當(dāng)作參數(shù)傳入browserify函數(shù)
- browserify解析文件路徑烂瘫,只codeSplicing該文件
思考
- 之前使用nodemon監(jiān)聽文件變化媒熊,沒有地方獲取變更文件的信息
- 需要改為使用node,fs模塊的watch函數(shù)進行監(jiān)聽坟比,能夠獲取到更改文件的信息
- 為了可用性芦鳍,解析之前的nodemon配置進行文件監(jiān)聽匹配
- 由于可能分為文件夾和文件,且文件夾深度未知葛账,所以需要遞歸進行監(jiān)聽以及過濾
實現(xiàn)
收集nodemon配置柠衅,解析字段
const config = {
"watch": ["."],
"ext": "js,ts,css",
"ignore": ["./dist/chunk.js", "./test/temp*.js"],
"exec": "node ./src/browserify.js ./test/index.js"
}
遞歸過濾,監(jiān)聽
const extSet = new Set(config.ext.split(','))
const ignoreList = config.ignore.concat(['node_modules', '.git'])
const fileFilter = (path) => {
// 忽略文件校驗
const isIgnore = ignoreList.some(item => {
return new RegExp(`${item.replace(/\./g, '\.').replace(/\\/g, '\\\\').replace(/\*/g, '.*')}$`).test(path)
})
if (isIgnore) return
//區(qū)分文件或文件夾
const stat = fs.statSync(path)
const isDirectory = stat.isDirectory()
if (isDirectory) {
const dir = fs.readdirSync(path)
dir.forEach(fileName => {
//遞歸對文件夾內(nèi)每個文件執(zhí)行一遍
fileFilter(join(path, fileName))
})
} else {
// 后綴校驗
const ext = extname(path).replace(/^\./, '')
if (!ext || !extSet.has(ext)) return
//符合配置籍琳,開啟監(jiān)聽
fs.watch(path, (eventType, filename) => {
console.log('開始重新構(gòu)建');
console.log(filename, eventType);
execSync(config.exec)
console.log('完成重新構(gòu)建');
})
}
}
測試
開始重新構(gòu)建
module1.js change
完成重新構(gòu)建
開始重新構(gòu)建
module1.js change
完成重新構(gòu)建
可以看到
- 確實實現(xiàn)了按照配置過濾及文件監(jiān)聽
- 能夠獲取到修改的文件名菲宴,以及修改類型
- 改一次文件watch可能會觸發(fā)兩次
關(guān)于觸發(fā)兩次的問題,發(fā)現(xiàn)官網(wǎng)說法如下
fs.watch API 跨平臺并非 100% 一致趋急,并且在某些情況下不可用喝峦。
網(wǎng)友反饋
Node.js `fs.watch`:
* 不報告OS X上的文件名。
* 在OS X上使用Sublime等編輯器時呜达,根本不報告事件谣蠢。
* 經(jīng)常報告兩次事件。
* 發(fā)布大多數(shù)更改為`rename`闻丑。
* 有[a lot of other issues](https://github.com/joyent/node/search?q=fs.watch&type=Issues)
* 不提供遞歸查看文件樹的簡便方法漩怎。
Node.js `fs.watchFile`:
* 事件處理幾乎一樣糟糕。
* 也不提供任何遞歸觀看嗦嗡。
* 導(dǎo)致CPU利用率過高勋锤。
網(wǎng)上的解決方案如下:
- trick方式,但其實不是每次都會出發(fā)兩次watch侥祭,所以不可取
var fs = require('fs');
var working = false;
fs.watch('directory', function (event, filename) {
if (filename && event == 'change' && active == false) {
active = true;
//do stuff to the new file added
active = false;
});
- 建議使用
chokidar
(https://github.com/paulmillr/chokidar)
好的叁执,話不多說茄厘,這就使用chokidar,代碼如下:
const chokidar = require('chokidar');
const { execSync } = require("child_process");
// One-liner for current directory
const watcher = chokidar.watch('.', {
persistent: true,
ignored: ['./dist/chunk.js', './test/temp *.js', '.git', '.history', 'node_modules', './src/moduleFuncCache.js'],
ignoreInitial: false,
followSymlinks: true,
cwd: '.',
disableGlobbing: false,
usePolling: false,
interval: 100,
binaryInterval: 300,
alwaysStat: false,
depth: 99,
awaitWriteFinish: {
stabilityThreshold: 2000,
pollInterval: 100
},
ignorePermissionErrors: false,
atomic: true
})
watcher.on('change', path => {
console.log(`更改文件${path}`);
//增量更改模塊谈宛,需傳入更改的文件的路徑
execSync(`node ./src/browserify.js ./test/index.js ${path}`)
})
.on('unlink', path => {
// 遇到刪除文件操作則重新打包一遍次哈,及時發(fā)現(xiàn)錯誤
execSync(`node ./src/browserify.js ./test/index.js`)
});
更改js和css代碼,效果如下:
更改文件test/module2.js
更改文件asserts/css/color.css
注意這句execSync(
node ./src/browserify.js ./test/index.js ${path})
吆录,它會將更改的文件路徑傳入browserify.js窑滞,從而明確哪個模塊更改了,從而只針對該模塊進行替換
browserify.js文件接收如下:
const [path, changeFilePath] = process.argv.splice(2);