webpack笔记(9)-如何编写loader和插件

87 阅读1分钟

使用loader-runner调试

定义:loader-runner 允许你在不安装webpack的情 况下运行loaders

image.png

// run-loader.js
const {runLoaders} = require('loader-runner');
const fs = require('fs');
const path = require('path');

runLoaders({
    // 资源的绝对路径
    resource: path.join(__dirname, './src/demo.txt'),
    // oader 的绝对路径
    loaders: [
        // path.join(__dirname, './src/raw-loader.js')
        {
            loader: path.join(__dirname, './src/raw-loader.js'),
            options: {
                name: 'test'
            }
        }
    ],
    context: {
        minimize: true
    },
    // 使用fs来读取内容
    readResource: fs.readFile.bind(fs)
}, (err, result) => {
    err ? console.log(err) : console.log(result);
});
// raw-loader.js
module.exports = function(source) {
    // 通过loader-utils的 getOptions 方法获取参数
    // loader-utils 3.0.0 版本已经移除getOptions方法,通过this.query直接获取参数
    const {name} = loaderUtils.getOptions(this);
    console.log('name',name)

    const json = JSON.stringify(source)
        .replace(/\u2028/g, '\\u2028')
        .replace(/\u2029/g, '\\u2029');

    // 结果直接返回,如果想抛出异常:throw new Error('Error')
    // return `export default ${json}`;

    // 更多是使用callback返回-同步写法
    // this.callback(new Error('Error'), json);
    this.callback(null, json);
}

执行命令:node run-loader.js image.png 异步:

const loaderUtils = require('loader-utils');
const fs = require('fs');
const path = require('path');

module.exports = function(source) {
    // 可以使用 this.cacheable(false) 关掉缓存
    this.cacheable(false);
    
    const json = JSON.stringify(source)
        .replace(/\u2028/g, '\\u2028')
        .replace(/\u2029/g, '\\u2029');


    // 异步处理
    const callback = this.async();
    fs.readFile(path.join(__dirname, './async.txt'),'utf-8',(err, data) => {
        callback(null, data);
    });
}

执行结果: image.png

通过this.emitFile进行文件写入

const loaderUtils = require("loader-utils");

module.exports = function(content) {
    const url = loaderUtils.interpolateName(this, "[hash].[ext]", {
content});

// 将文件内容放到指定的位置
this.emitFile(url, content);

const path = `__webpack_public_path__ + ${JSON.stringify(url)};`;

return `export default ${path}`; };

插件编写

插件没有像 loader 那样的独立运行环境,只能在 webpack 里面运行。
例如,写一个zip插件:

// webpack.config.js
const path = require('path');
const ZipPlugin = require('./plugins/zip-plugin');


module.exports = {
    entry: './src/index.js',
    output: {
        path: path.join(__dirname, 'dist'),
        filename: 'main.js'
    },
    mode: 'production',
    plugins: [
        new ZipPlugin({
            filename: 'offline'
        })
    ]
}
const JSZip = require('jszip');
const path = require('path');
const RawSource = require('webpack-sources').RawSource;
const { Compilation } = require('webpack');

const zip = new JSZip();

module.exports = class ZipPlugin {
    constructor(options) {
        this.options = options;
    }
    // 每个插件都有一个apply方法,接收一个compiler对象
    // 插件执行时会进入到apply方法
    apply(compiler) {
        // 使用异步钩子-文件生成
        compiler.hooks.emit.tapAsync('ZipPlugin', (compilation, callback) => {
            // 创建一个目录
            const folder = zip.folder(this.options.filename);
            // 处理compilation
            for (let filename in compilation.assets) {
                // console.log(compilation.assets[filename])
                const source = compilation.assets[filename].source();

                folder.file(filename, source);
                // console.log('source',source)
            }
            zip.generateAsync({
                type: 'nodebuffer'
            }).then((content) => {
                // console.log(content)

                // 可以获取到经过webpack处理的各种各样的信息
                // console.log(compilation.options)

                const outputPath = path.join(
                    compilation.options.output.path,
                    this.options.filename + '.zip'
                );
                // 绝对路径转换成相对路径
                const outputRelativePath = path.relative(
                    compilation.options.output.path,
                    outputPath
                );
                compilation.assets[outputRelativePath] = new RawSource(content);

                callback();
            });
        });
    }
}

执行命令:npm run build 后输出结果 image.png