webpack源碼剖析之三:loader

媒介

在webpack特徵內里,它能夠支撐將非javaScript文件打包,但前面寫到webpack的模塊化打包只能應用於含有特定範例的JavaScript文件。本次引見的loader則是用來處理這類題目的。本文章loader的完成基於code-splitting

功能剖析

舉個例子:

webpack.config.js中的設置loader

    module: {
            rules: [
                {
                    test: /\.js$/,
                    loader: "test-loader!test-loader2"
                }
            ]
        }

營業代碼中的內聯loader

require('d!c');

剖析:

我們須要將這些loader剖析成可運轉的函數,並在parse模塊剖析語法樹之前運轉掉這些loader函數

所以我們須要:

  1. resolve模塊:剖析出module對應的loader字符串,並剖析出各個loader的絕對途徑
  2. buildDeps模塊:經由過程文件途徑獵取須要運轉的loader函數,將其壓入行列,以後再順次順次遞歸出loader函數運轉,如果是異步loader,則要經由過程回調函數來遞歸下一個loader函數。

完成

resolve 模塊

完成思緒:

  1. 將設置內的loaders,shell敕令的loaders,require/import的內聯loader夙昔至后構成一個數組。設置內的loaders須要正則婚配test屬性,來獵取設置內的loader字符串。一切loader字符串內部又能夠截取’!’,獵取完全的loader。
  2. 剖析並剖析該數組內的字符串,獵取各個loader的絕對途徑。並返回剖析好的字符串。這塊的完成和文件打包相似。

終究require內的字符串以下

/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/d.js!
/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/test-loader/index.js!
/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/test-loader2/index.js!
/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/c.js

buildDeps模塊

完成思緒:

  1. 剖析文件途徑,並獵取須要運轉的loader函數,存入數組
  2. 數組在經由過程pop函數一個個遞歸,考慮到存在異步loader函數的狀況,須要為運轉函數供應async,以及callback的上下文。詳細的上下文可參考Loader API

loader遞歸邏輯以下:

    nextLoader.apply(null, content);
    function nextLoader() {
        const args = Array.prototype.slice.apply(arguments);
        if (loaderFunctions.length > 0) {
            const loaderFunction = loaderFunctions.pop();
            let async = false;
            const context = {
                fileName,
                options,
                debug: options.debug,
                async: function () {
                    async = true;
                    return nextLoader;
                },
                callback: function () {
                    async = true;
                    nextLoader.apply(null, arguments)
                }
            };
            const resVal = loaderFunction.apply(context, args);
            if (!async) {
                nextLoader(resVal);
            }
        } else {
            callback(null, args[0])
        }
    }

測試

將以上3個loader,test-loader,test-loader2,異步loader d.js,打包c.js

test-loader

module.exports = function(content) {
    return content+"\nexports.answer = 42;\n"
}

test-loader2

module.exports = function(content) {
    return content+"\nexports.test2 = test2;\n"
}

異步loader d.js

module.exports = function (content) {
    const d = 'd';
    this.async();
    const b = content + "\nexports.d = 2000;\n";
    setTimeout(this.callback.bind(this, b), 2000);
}

c.js

const c = 'c';

module.exports = c;

終究打包出來的c.js的代碼以下

....
/* 1 */
/***/(function(module, exports,__webpack_require__) {
const c = 'c';

module.exports = c;

exports.test2 = test2;

exports.answer = 42;

exports.d = 2000;

/***/}
....

代碼完成

本人的簡易版webpack完成simple-webpack

(完)

    原文作者:朱建
    原文地址: https://segmentfault.com/a/1190000015219662
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞