loader

  1. webpack 做的事情,仅仅是分析出各种模块的依赖关系,然后形成资源列表,最终打包生成到指定的文件中;更多的功能需要借助 webpack loaderswebpack plugins 完成;
  2. loader 本质上是一个函数,它的作用是将某个源码字符串转换成另一个源码字符串返回loader 函数将在模块解析的过程中被调用,从而得到最终的源码;

构建打包全流程

chunk 中解析模块的流程

处理 loaders 流程

解析 loader 的时候,从上到下匹配,命中匹配后将 loader 依次放入到一个数组中,最后再从后向前依次调用 loader

loader 配置

完整配置

module.exports = {
    module: { // 针对模块的配置,目前版本只有两个配置,rules、noParse
        rules: [ // 模块匹配规则,可以存在多个规则
            { // 每个规则是一个对象
                test: /\.js$/, // 匹配的模块正则
                use: [ // 匹配到后应用的规则模块
                    {  // 其中一个规则
                        loader: "模块路径", // loader模块的路径,该字符串会被放置到 require 中
                        options: { // 向对应 loader 传递的额外参数

                        }
                    }
                ]
            }
        ]
    }
}

简化配置

module.exports = {
    module: { // 针对模块的配置,目前版本只有两个配置,rules、noParse
        rules: [ // 模块匹配规则,可以存在多个规则
            { // 每个规则是一个对象
                test: /\.js$/, // 匹配的模块正则
                use: ["模块路径1", "模块路径2"] // loader 模块的路径,该字符串会被放置到 require 中
            }
        ]
    }
}

loader 练习

手写 style-loader

JavaScript
CSS
JavaScript
JavaScript
// index.js
var content = require("./assets/index.css");

console.log(content); // css 的源码字符串
/* assets/index.css */
body {
    background: #333;
    color     : #fff;
}
// loaders/style-loader.js
module.exports = function (sourceCode) {
    var code = `
        var style = document.createElement("style");
        style.innerHTML = \`${sourceCode}\`;
        document.head.appendChild(style);
        module.exports = \`${sourceCode}\`;
    `;
    return code;
}
// webpack.config.js
module.exports = {
    mode: "development",
    devtool: "source-map",
    module: {
        rules: [{
            test: /\.css$/,
            use: ["./loaders/style-loader"]
        }]
    }
}

手写 img-loader

JavaScript
JavaScript
JavaScript
// index.js
var src = require("./assets/webpack.png");
console.log(src);
var img = document.createElement("img");
img.src = src;
document.body.appendChild(img);
// loaders/img-loader.js
var loaderUtil = require("loader-utils"); // 用于获取 webpack 中配置的 options 中的参数

/***
 * 将 buffer 数组转成base64 字符串
 * @param {*} buffer 
 * @returns 
 */
function getBase64(buffer) {
    return "data:image/png;base64," + buffer.toString("base64");
}

/***
 * 获取文件名字
 * @param {*} buffer 
 * @param {*} name [contenthash].[ext]
 * @returns 
 */
function getFilePath(buffer, name) {
    // 根据 [单文件 buffer] 生成 hash
    var filename = loaderUtil.interpolateName(this, name, { content: buffer });
    this.emitFile(filename, buffer); // 向最终的文件列表中增加文件
    return filename;
}

function loader(buffer) {
    // 获取原始数据 buffer
    console.log("文件数据大小:(字节)", buffer.byteLength);

    var { limit = 1000, filename = "[contenthash].[ext]" } = loaderUtil.getOptions(this);
    if (buffer.byteLength >= limit) {
        var content = getFilePath.call(this, buffer, filename);
    } else {
        var content = getBase64(buffer);
    }

    return `module.exports = \`${content}\``;
}


// 设置为 true 后,loader 返回的就是原始数据,而不是字符串了
loader.raw = true;
module.exports = loader;
// webpack.config.js
module.exports = {
    mode: "development",
    devtool: "source-map",
    module: {
        rules: [
            {
                test: /\.(png)|(jpg)|(gif)$/, 
                use: [
                    {
                        loader: "./loaders/img-loader.js",
                        options: {
                            limit: 3000, // 3000 字节以上使用图片,3000 字节以内使用 base64
                            filename: "img-[contenthash:5].[ext]"
                    }
                }]
            }
        ]
    }
}

面试题

最后的输出结果

  1. 题目代码:

    JavaScript
    JavaScript
    JavaScript
    JavaScript
    JavaScript
    JavaScript
    // index.js
    require("./a.js"); // a.js 中内容为空
    
    // loaders/loader1.js
    module.exports = function (sourceCode) {
        console.log("loader1");
        return sourceCode;
    }
    
    // loaders/loader2.js
    module.exports = function (sourceCode) {
        console.log("loader2");
        return sourceCode;
    }
    
    // loaders/loader3.js
    module.exports = function (sourceCode) {
        console.log("loader3");
        return sourceCode;
    }
    
    // loaders/loader4.js
    module.exports = function (sourceCode) {
        console.log("loader4");
        return sourceCode;
    }
    
    // webpack.config.js
    module.exports = {
        mode: "development",
        module: {
            rules: [
                {
                    test: /index\.js$/, //正则表达式,匹配模块的路径
                    use: ["./loaders/loader1", "./loaders/loader2"] //匹配到了之后,使用哪些加载器
                }, //规则1
                {
                    test: /\.js$/, //正则表达式,匹配模块的路径
                    use: ["./loaders/loader3", "./loaders/loader4"] //匹配到了之后,使用哪些加载器
                } //规则2
            ], //模块的匹配规则
        }
    }
    
  2. 输出:看 chunk 中解析模块的更详细流程

    // index.js
    loader4
    loader3
    loader2
    loader1
    
    // a.js
    loader4
    loader3
    
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. loader
    1. 1.1. 构建打包全流程
    2. 1.2. chunk 中解析模块的流程
    3. 1.3. 处理 loaders 流程
  2. 2. loader 配置
    1. 2.1. 完整配置
    2. 2.2. 简化配置
  3. 3. loader 练习
    1. 3.1. 手写 style-loader
    2. 3.2. 手写 img-loader
  4. 4. 面试题
    1. 4.1. 最后的输出结果