實(shí)現(xiàn)構(gòu)建工具之增量打包

銜接上文,由于每次修改文件,都會(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)上的解決方案如下:

  1. 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;
});
  1. 建議使用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);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容