webpack-loader詳解

webpack-loader是什么?

image

loader是一個函數(shù),有一個參數(shù)是源文件內(nèi)容,有內(nèi)部的this,最后的loader必須有返回值否則會報(bào)錯:Final loader didn't return a Buffer or String

原因:

如果是處理順序排在最后一個的 Loader,那么它的返回值將最終交給webpack的require,換句話說,它一定是一段用字符串來存儲可執(zhí)行的JavaScript腳本

如何配置webpack-loader?

配置單個loader
    {
      test: /\.js$/
      use: [
        {
          loader: path.resolve(__dirname, 'src', 'loaders', 'first-loader.js'),
          options: {/*這里是給loader傳入的參數(shù)會有一個專門的庫來處理這個參數(shù),待會下面會講到*/}
        }
      ]
    }
配置多個loader

如果把我們自己寫的loader當(dāng)做一個模塊來加載的時候,webpack默認(rèn)的會去查找node_modules下面的模塊,但是我們自己寫的loader沒有在node_modules中 這時候我們怎么樣讓他也查找我們自己寫的loader呢?這時候我們就需要設(shè)置一個屬性 resolveLoader他是一個對象,里面有一個modules屬性 該屬性的值是一個數(shù)組 數(shù)組中 的每個值就是要查找的loader存放地

    resolveLoader: {
       modules: [
            path.resolve('node_modules'),
            path.resolve(__dirname, 'src', 'loaders')
        ]
    },

再配置loader

    {
        test: /\.less$/,
        use: ['style-loader','less-loader']
    }

loader的用法準(zhǔn)則

單一職責(zé),一個loader只做一件事情,這樣設(shè)計(jì)的原因是因?yàn)?,職?zé)越單一,組合性就強(qiáng),可配置性就好。

從右到左,鏈?zhǔn)綀?zhí)行,上一個loader的處理結(jié)果傳給下一個loader接著處理

模塊化,一個loader就是一個模塊,單獨(dú)存在不依賴其他的任何模塊
無狀態(tài),就類似于純函數(shù)。

loader有實(shí)用工具loader-utils解析loader參數(shù)的和schema-utils校驗(yàn)格式的

loader的依賴

loader相關(guān)API

1、緩存
webpack充分地利用緩存來提高編譯效率

    this.cacheable();

2、異步
當(dāng)一個 Loader 無依賴,可異步的時候我想都應(yīng)該讓它不再阻塞地去異步

    // 讓 Loader 緩存
    module.exports = function(source) {
        var callback = this.async();
        // 做異步的事
        doSomeAsyncOperation(content, function(err, result) {
            if(err) return callback(err);
            callback(null, result);
        });
    };

3、傳入的源文件以buffer的形式

默認(rèn)的情況源文件是以 UTF-8 字符串的形式傳入給 Loader,設(shè)置module.exports.raw = true可使用 buffer 的形式進(jìn)行處理

    module.exports.raw = true

4、獲取loader的options

    const loaderUtils = require('loader-utils');
    module.exports = function (source) {
        // 獲取當(dāng)前用戶給當(dāng)前l(fā)oader傳入的參數(shù)對象options
        const options = loaderUtils.getOptions(this);
        return source;
    }

5、校驗(yàn)傳入的options的值是不是符合要求

    const loaderUtils = require('loader-utils');
    const validate = require('schema-utils');
    let json = {
        "type": "object",
        "properties": {
            "content": {
                "type": "string",
            }
        }
    }
    module.exports = function (source) {
        const options = loaderUtils.getOptions(this);
        // 第一個參數(shù)是校驗(yàn)的json 第二個參數(shù)是loader傳入的options 第三個參數(shù)是當(dāng)前l(fā)oader的名稱
        validate(json, options, 'first-loader');
        console.log(options.content)
    }
    // 當(dāng)前規(guī)定 傳入的options必須是一個對象 他里面的content的值必須是一個字符串 如果不滿足要求則會有好的報(bào)錯  xxx應(yīng)該是一個什么類型

6、loader的返回其他結(jié)果

Loader有些場景下還需要返回除了內(nèi)容之外的東西。

    module.exports = function(source) {
      // 通過 this.callback 告訴 Webpack 返回的結(jié)果
      this.callback(null, source, sourceMaps);
      // 當(dāng)你使用 this.callback 返回內(nèi)容時,該 Loader 必須返回 undefined,
      // 以讓 Webpack 知道該 Loader 返回的結(jié)果在 this.callback 中,而不是 return 中 
      return;
    };
    this.callback(
        // 當(dāng)無法轉(zhuǎn)換原內(nèi)容時,給 Webpack 返回一個 Error
        err: Error | null,
        // 原內(nèi)容轉(zhuǎn)換后的內(nèi)容
        content: string | Buffer,
        // 用于把轉(zhuǎn)換后的內(nèi)容得出原內(nèi)容的 Source Map,方便調(diào)試
        sourceMap?: SourceMap,
        // 如果本次轉(zhuǎn)換為原內(nèi)容生成了 AST 語法樹,可以把這個 AST 返回,
        // 以方便之后需要 AST 的 Loader 復(fù)用該 AST,以避免重復(fù)生成 AST,提升性能
        abstractSyntaxTree?: AST
    );

7、同步與異步

Loader 有同步和異步之分,上面介紹的 Loader 都是同步的 Loader,因?yàn)樗鼈兊霓D(zhuǎn)換流程都是同步的,轉(zhuǎn)換完成后再返回結(jié)果。 但在有些場景下轉(zhuǎn)換的步驟只能是異步完成的,例如你需要通過網(wǎng)絡(luò)請求才能得出結(jié)果,如果采用同步的方式網(wǎng)絡(luò)請求就會阻塞整個構(gòu)建,導(dǎo)致構(gòu)建非常緩慢。

    module.exports = function(source) {
        // 告訴 Webpack 本次轉(zhuǎn)換是異步的,Loader 會在 callback 中回調(diào)結(jié)果
        var callback = this.async();
        someAsyncOperation(source, function(err, result, sourceMaps, ast) {
            // 通過 callback 返回異步執(zhí)行后的結(jié)果
            callback(err, result, sourceMaps, ast);
        });
    };

8、處理二進(jìn)制數(shù)據(jù)

在默認(rèn)的情況下,Webpack 傳給 Loader 的原內(nèi)容都是 UTF-8 格式編碼的字符串。 但有些場景下 Loader 不是處理文本文件,而是處理二進(jìn)制文件,例如 file-loader,就需要 Webpack 給 Loader 傳入二進(jìn)制格式的數(shù)據(jù)。 為此,你需要這樣編寫 Loader:

    module.exports = function(source) {
        // 在 exports.raw === true 時,Webpack 傳給 Loader 的 source 是 Buffer 類型的
        source instanceof Buffer === true;
        // Loader 返回的類型也可以是 Buffer 類型的
        // 在 exports.raw !== true 時,Loader 也可以返回 Buffer 類型的結(jié)果
        return source;
    };
    // 通過 exports.raw 屬性告訴 Webpack 該 Loader 是否需要二進(jìn)制數(shù)據(jù) 
    module.exports.raw = true;

9、緩存

在有些情況下,有些轉(zhuǎn)換操作需要大量計(jì)算非常耗時,如果每次構(gòu)建都重新執(zhí)行重復(fù)的轉(zhuǎn)換操作,構(gòu)建將會變得非常緩慢。 為此,Webpack 會默認(rèn)緩存所有 Loader 的處理結(jié)果,也就是說在需要被處理的文件或者其依賴的文件沒有發(fā)生變化時, 是不會重新調(diào)用對應(yīng)的 Loader 去執(zhí)行轉(zhuǎn)換操作的。

    module.exports = function(source) {
     // 關(guān)閉該 Loader 的緩存功能
       this.cacheable(false);
       return source;
    };

10、其它 Loader API

this.context:當(dāng)前處理文件的所在目錄,假如當(dāng)前 Loader 處理的文件是 /src/main.js,則 this.context 就等于 /src。

this.resource:當(dāng)前處理文件的完整請求路徑,包括 querystring,例如 /src/main.js?name=1。

this.resourcePath:當(dāng)前處理文件的路徑,例如 /src/main.js。

this.resourceQuery:當(dāng)前處理文件的 querystring。

this.target:等于 Webpack 配置中的 Target

this.loadModule:但 Loader 在處理一個文件時,如果依賴其它文件的處理結(jié)果才能得出當(dāng)前文件的結(jié)果時, 就可以通過 this.loadModule(request: string, callback: function(err, source, sourceMap, module)) 去獲得 request 對應(yīng)文件的處理結(jié)果。

this.resolve:像 require 語句一樣獲得指定文件的完整路徑,使用方法為 resolve(context: string, request: string, callback: function(err, result: string))。

this.addDependency:給當(dāng)前處理文件添加其依賴的文件,以便再其依賴的文件發(fā)生變化時,會重新調(diào)用 Loader 處理該文件。使用方法為 addDependency(file: string)。

this.addContextDependency:和 addDependency 類似,但 addContextDependency 是把整個目錄加入到當(dāng)前正在處理文件的依賴中。使用方法為 addContextDependency(directory: string)。

this.clearDependencies:清除當(dāng)前正在處理文件的所有依賴,使用方法為 clearDependencies()。

this.emitFile:輸出一個文件,使用方法為 emitFile(name: string, content: Buffer|string, sourceMap: {...})。
完整API

手寫簡單的style-loader 和less-loader

    // 創(chuàng)建一個style標(biāo)簽里面放上源文件的樣式字符串 插到頁面上
    module.exports = function (source) {
        let script = (`
            let style = document.createElement("style");
            style.innerText = ${JSON.stringify(source)};
            document.head.appendChild(style);
        `);
        return script;
    }
    let less = require('less');
    module.exports = function (source) {
        this.cacheable();
        less.render(source, (err,result)=>{
            console.log(result.css);
            this.callback(err,result.css)
        })
    }

不要厭煩熟悉的事物,每天都進(jìn)步一點(diǎn);不要畏懼陌生的事物,每天都學(xué)習(xí)一點(diǎn);

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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