1. 程式人生 > >webpack loader 生成虛擬檔案的方案

webpack loader 生成虛擬檔案的方案

此文已由作者張磊授權網易雲社群釋出。

歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。

前言

使用 webpack 的時候,難免需要寫一些 loader,接著就會遇到一個很糾結的問題。該 loader 會生成一個檔案,一般這個檔案的生成時機都是在 loader 處理所有的檔案後。一般有兩種處理方案。一種是寫一個 plugin,監聽對應的事件;一種是生成一個臨時檔案,將每次讀到的內容都寫在 臨時檔案 中。第一種在使用的時候也很麻煩,需要同時在 loader 和 plugin 加一下對應的邏輯。第二種,寫入臨時檔案的這個過程很是讓人糾結。很明顯,兩種方案對於有一定潔癖的人來說,都不優雅,那麼就來尋找一種方案,既不需要寫 plugin,又不需要寫入 臨時檔案 中。

解決方案

在 github 上找到一個可用解決方案的 loader,這個 loader 看起來是關於虛擬檔案生成的,使用很簡單,指定名字,指定內容,生成一個虛擬檔案,研究了一下,對解決問題很有幫助。關鍵程式碼如下:

// index.jsimport * as fsPatch from './fs-patch';// 省略...fsPatch.add( this.fs, {    path:    file,    content: src
});// 省略...

這個檔案傳入 loader.fs,看起來是對 fs 打補丁,接著再來看 fs-patch.js

// fs-patch.jsimport path from 'path';const NS   = __filename;

export function patch( fs ) {    if ( fs[ NS ] )        return;    const virtualFS = {
        files: {},

        add( options ) {            const file = path.resolve( options.path );
            virtualFS.files[ file ] = {
                path:    file,
                content: options.content
            };
        }

    };
    fs[ NS ] = virtualFS;


    createPatchFn( fs, 'readFile', function( orig, args, file, encoding, cb ) {        var rfile = path.resolve( file );        var vfile = virtualFS.files[ rfile ];        if ( vfile ) {            if ( typeof(encoding) === 'function' ) {
                cb       = encoding;
                encoding = null;
            }            var content = vfile.content;            if ( encoding != null )
                content = content.toString( encoding );

            cb( null, content )            return;
        }        return orig.apply( this, args );
    });
    createPatchFn( fs, 'readFileSync', function( orig, args, file, encoding ) {        var rfile = path.resolve( file );        var vfile = virtualFS.files[ rfile ];        if ( vfile ) {            var content = vfile.content;            if ( encoding != null )
                content = content.toString( encoding );            return content;
        }        return orig.apply( this, args );
    });

    createPatchFn( fs, 'stat', function( orig, args, p, cb ) {        var rp = path.resolve( p );        var vfile = virtualFS.files[ rp ];        if ( vfile ) {            var vstat = {
                dev: 8675309,
                nlink: 1,
                uid: 501,
                gid: 20,
                rdev: 0,
                blksize: 4096,
                ino: 44700000,
                mode: 33188,
                size: vfile.content.length,
                 isFile() { return true; },
                isDirectory() { return false; },
                isBlockDevice() { return false; },
                isCharacterDevice() { return false; },
                isSymbolicLink() { return false; },
                isFIFO() { return false; },
                isSocket() { return false; },
            };
            cb( null, vstat );            return;
        }        return orig.apply( this, args );
    });
    createPatchFn( fs, 'statSync', function( orig, args, p ) {        var rp = path.resolve( p );        var vfile = virtualFS.files[ rp ];        if ( vfile ) {            var vstat = {
                dev: 8675309,
                nlink: 1,
                uid: 501,
                gid: 20,
                rdev: 0,
                blksize: 4096,
                ino: 44700000,
                mode: 33188,
                size: vfile.content.length,
                 isFile() { return true; },
                isDirectory() { return false; },
                isBlockDevice() { return false; },
                isCharacterDevice() { return false; },
                isSymbolicLink() { return false; },
                isFIFO() { return false; },
                isSocket() { return false; },
            };            return vstat;
        }        return orig.apply( this, args );
    });

};

export function add( fs, options ) {
    patch( fs );
    fs[ NS ].add( options );
}function createPatchFn( obj, name, fn ) {    const origin  = obj[ name ];
    obj[ name ] = function() {        const args = Array.prototype.slice.call( arguments );        return fn.apply( this, [origin, args].concat( args ) );
    };
}

程式碼分析

可以看到 fs-patch.js 直接劫持了 loader.fs,重寫了 fs 的一些方法,而重寫的這些方法就是生成虛擬檔案的關鍵。劫持後的 fs 在訪問這些方法的時候,首先去從快取中獲取路徑對應的內容,不存在則再從硬碟中讀取。

優缺點

優點是不需要生成臨時檔案或者另寫一個 plugin,缺點在檔案比較大或者計算比較頻繁,對機器的要求會比較高。


免費體驗雲安全(易盾)內容安全、驗證碼等服務

更多網易技術、產品、運營經驗分享請點選

 



相關文章:
【推薦】 什麼是高防伺服器?