webpack4自定义loader

660 阅读2分钟

loader是什么?

Loader是打包方案,webpack不能识别非js结尾的模块,告知webpack某些特定文件如何打包,loader其实是在webpack编译期间执行的一个函数

替换一个字符串

这里我想使用loader把word替换个xxx,发现没有我想用的loader,只能自己写一个了

console.log('hello word');
// loader函数
/**
 * source 代码片段
 * map surcemap 可选
 * meta 模块原数据 可选
 */
module.exports = function (source, map, meta) {
    return source.replace('word', 'xxx');
}
// webpack.config.js
const path = require('path');

module.exports = {
	mode: 'development',
	entry: {
		main: './src/index.js'
	},
	module: {
		rules: [{
			test: /.js/,
			use: [
				{
					loader: path.resolve(__dirname, './loaders/replaceLoader.js'),
				},
			]
		}]
	},
	output: {
		path: path.resolve(__dirname, 'dist'),
		filename: '[name].js'
	}
}

build结果,自定义loader实现了我们的效果

能不能把替换的内容变成参数传递的?

通过loader函数内部this.query获取loader配置的options

// loader
module.exports = function (source, map, meta) {
    const options = this.query;
    return source.replace('word', options.name);
}

通过loader-utils获取

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

module.exports = function(source) {
	const options = loaderUtils.getOptions(this);
	return source.replace('dell', options.name);
}
const path = require('path');

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    module: {
        rules: [
            {
                test: /.js/,
                use: [
                    {
                        loader: path.resolve(__dirname, './loaders/replaceLoader.js'),
                        // 相当于为loader函数传递参数
                      	options: {
                            name: '123123123'
                        }
                    }
                ]
            }
        ]
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    },
};

可以发现上诉我们都是通过path路径引入loader,能不能类似与less-loader直接写loader名称就可以,答案显示当然可以,通过resolveLoader配置loader文件夹

const path = require('path');

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
  	// 自定义loader文件夹
    resolveLoader: {
      modules: ['node_modules', './loaders']
    },
    module: {
        rules: [
            {
                test: /.js/,
                use: [
                    {
                      	// 同级目录node_modules或者loaders找replaceLoader.js
                        loader: 'replaceLoader',
                        options: {
                            name: '123123123'
                        }
                    }
                ]
            }
        ]
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    },
};

this.callback

除了通过return返回loader编译结果的值还可以通过this.callback

this.callback(
  err: Error | null,
  content: string | Buffer,
  sourceMap?: SourceMap,
  meta?: any
);
const loaderUtils = require('loader-utils');

module.exports = function(source) {
	const options = loaderUtils.getOptions(this);
	callback(null, source.replace('word', options.name));
}

异步loader(会阻塞)

异步loader,使用this.async(),返回值通过this.async()的返回值callback输出

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

module.exports = function(source) {
	const options = loaderUtils.getOptions(this);
	const callback = this.async();

	setTimeout(() => {
		const result = source.replace('word', options.name);
		callback(null, result);
	}, 1000);
}

定义loader需要注意什么

  1. commonjs风格
  2. 导出的是一个函数
  3. 不可以是箭头函数this上有很多参数可以看webpack官方文档,箭头函数无this指向

查看更多相关loader配置

webpack无疑是个庞大的体系,文章仅仅介绍了如何完整定义一个loader的全过程,如果想了解的更多可以查看webpack loader-api