美文网首页
如何编写自定义webpack loader?

如何编写自定义webpack loader?

作者: 努力学习的小丸子 | 来源:发表于2021-06-22 09:23 被阅读0次

    https://webpack.docschina.org/contribute/writing-a-loader/

    书写规范

    loader 需导出一个函数

    监听文件实时编译
    
    const path = require("path");
    const fs = require("fs");
    export default function (source) {
      var callback = this.async();
      var headerPath = path.resolve("header.js");
      // 当header.js文件发生改变时会触发编译
      this.addDependency(headerPath);
    
      fs.readFile(headerPath, "utf-8", function (err, header) {
        if (err) return callback(err);
        callback(null, header + "\n" + source);
      });
    }
    
    webpack 5 新特性

    webpack 5 发布后,在 loader 的上下文中,会带有内置的 this.getOptions 方法。这对于那些使用之前推荐 schema-utils 中的 getOptions 方法的 loader 而言,这是一个重大更新。

    模块依赖

    根据模块的类型,可以使用不同的模式来指定依赖项。例如在CSS中,使用@import和url(…)语句。这些依赖关系应该由模块系统来解决。
    这可以通过两种方式之一来实现:

    • 通过将它们转换为require语句。
    • 使用 this.resolve 解析路径函数。

    对于 less-loader ,它不能将每个@import转换为require,因为所有的.less文件必须一次编译一次,用于变量和mixin跟踪。因此,less loader使用自定义路径解析逻辑扩展了less compiler。然后它利用了第二种方法this.resolve来解析依赖。

    绝对路径

    不要在模块代码中插入绝对路径,因为当项目的根被移动时,它们会破坏散列。loader-utils中有一个stringifyRequest方法,可以用来将绝对路径转换为相对路径。

    jtest 测试

    使用webpack和memfs来避免输出文件到磁盘

    // compiler.js
    import path from 'path';
    import webpack from 'webpack';
    import { createFsFromVolume, Volume } from 'memfs';
    export default (fixture, options = {}) => {
        const compiler = webpack({
            context: __dirname,
            entry: `./${fixture}`,
            output: {
                path: path.resolve(__dirname),
                filename: 'bundle.js',
            },
            module: {
                rules: [
                    {
                        test: /\.js$/,
                        use: {
                            loader: 'replace-loader',
                            options,
                        },
                    },
                ],
            },
            resolveLoader: {
                modules: ["../loader"], // 配置loader的查找目录
            },
        });
        compiler.outputFileSystem = createFsFromVolume(new Volume());
        compiler.outputFileSystem.join = path.join.bind(path);
        return new Promise((resolve, reject) => {
            compiler.run((err, stats) => {
                if (err) reject(err);
                if (stats.hasErrors()) reject(stats.toJson().errors);
                resolve(stats);
            });
        });
    };
    
    // loader.test.js
    import compiler from './compiler.js';
    
    test('Inserts name and outputs JavaScript', async () => {
        const stats = await compiler('example.js', { name: 'Alice' });
        const output = stats.toJson({ source: true }).modules[0].source;
        console.log(output);
        expect(output).toBe("console.log('hello Alice');");
    });
    
    // example.js
    console.log('hello world');
    
    // package.json
    "test": "jest"
    

    其他代码:

    // loader/replace-loader.js
    // 注意导出的函数不能是箭头函数
    // webpack 5 发布后,在 loader 的上下文中,会带有内置的 this.getOptions 方法。
    // 这对于那些使用之前推荐 schema-utils 中的 getOptions 方法的 loader 而言,这是一个重大更新:
    import { getOptions } from 'loader-utils';
    import { validate } from 'schema-utils';
    const schema = {
        type: 'object',
        properties: {
            test: {
                type: 'string'
            }
        }
    };
    export default function(source) {
        const options = getOptions(this);
        validate(schema, options, {
            name: 'Example Loader',
            baseDataPath: 'options'
        });
        return source.replace(/world/g, options.text);
    }
    
    // vue.config.js
    configureWebpack: {
        module: {
          rules: [
            {
              test: /\.js$/i,
              use: ["replace-loader"],
            },
          ],
        },
        resolveLoader: {
          modules: ["./node_modules", "./loader"], // 配置loader的查找目录
        },
      }
    

    相关文章

      网友评论

          本文标题:如何编写自定义webpack loader?

          本文链接:https://www.haomeiwen.com/subject/xaxeeltx.html