銜接上文,由于每次修改文件,都會(huì)觸發(fā)重新全量打包,在項(xiàng)目體積幾乎為0的情況下要花費(fèi)3.5s,希望能提升性能,只對(duì)修改過的文件進(jìn)行打包,但由于我們沒有用到代碼拆分code splitting,所以此處優(yōu)化為:不會(huì)codeSplicing未更改的文件
思路
- 精確獲取更改的文件
- 將文件路徑當(dāng)作參數(shù)傳入browserify函數(shù)
- browserify解析文件路徑,只codeSplicing該文件
思考
- 之前使用nodemon監(jiān)聽文件變化,沒有地方獲取變更文件的信息
- 需要改為使用node,fs模塊的watch函數(shù)進(jìn)行監(jiān)聽,能夠獲取到更改文件的信息
- 為了可用性,解析之前的nodemon配置進(jìn)行文件監(jiān)聽匹配
- 由于可能分為文件夾和文件,且文件夾深度未知,所以需要遞歸進(jìn)行監(jiān)聽以及過濾
實(shí)現(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) => {
// 忽略文件校驗(yàn)
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 => {
//遞歸對(duì)文件夾內(nèi)每個(gè)文件執(zhí)行一遍
fileFilter(join(path, fileName))
})
} else {
// 后綴校驗(yàn)
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)建');
})
}
}
測(cè)試
開始重新構(gòu)建
module1.js change
完成重新構(gòu)建
開始重新構(gòu)建
module1.js change
完成重新構(gòu)建
可以看到
- 確實(shí)實(shí)現(xiàn)了按照配置過濾及文件監(jiān)聽
- 能夠獲取到修改的文件名,以及修改類型
- 改一次文件watch可能會(huì)觸發(fā)兩次
關(guān)于觸發(fā)兩次的問題,發(fā)現(xiàn)官網(wǎng)說法如下
fs.watch API 跨平臺(tái)并非 100% 一致,并且在某些情況下不可用。
網(wǎng)友反饋
Node.js `fs.watch`:
* 不報(bào)告OS X上的文件名。
* 在OS X上使用Sublime等編輯器時(shí),根本不報(bào)告事件。
* 經(jīng)常報(bào)告兩次事件。
* 發(fā)布大多數(shù)更改為`rename`。
* 有[a lot of other issues](https://github.com/joyent/node/search?q=fs.watch&type=Issues)
* 不提供遞歸查看文件樹的簡(jiǎn)便方法。
Node.js `fs.watchFile`:
* 事件處理幾乎一樣糟糕。
* 也不提供任何遞歸觀看。
* 導(dǎo)致CPU利用率過高。
網(wǎng)上的解決方案如下:
- trick方式,但其實(shí)不是每次都會(huì)出發(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 => {
// 遇到刪除文件操作則重新打包一遍,及時(shí)發(fā)現(xiàn)錯(cuò)誤
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}),它會(huì)將更改的文件路徑傳入browserify.js,從而明確哪個(gè)模塊更改了,從而只針對(duì)該模塊進(jìn)行替換
browserify.js文件接收如下:
const [path, changeFilePath] = process.argv.splice(2);