使用Webpack往往离不开loader的安装配置,手写一个loader其实非常简单,类似手写一个功能函数,下面我们来实现一个替换字符串的loader
初始化项目
创建一个根目录mack-loader,此目录下 npm init -y生成默认的package.json文件 ,在文件中配置打包命令
"scripts": {
"build": "webpack"
}
之后npm i -D webpack webpack-cli,安装完webpack,在根目录 创建配置文件webpack.config.js
const path = require('path')
module.exports = {
mode: 'development', // 先设置为development,不压缩代码,方便调试
entry: {
main: './src/index.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
在根目录创建一个src目录,里面创建index.js,输入console.log('hello, world')
之后运行npm run build即可打包项目,初始化项目完成
编写 replace-loader
根目录创建loaders文件夹,里面创建replaceLoader.js
const loaderUtils = require('loader-utils');
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
const result = source.replace('world', options.name);
return result;
}
这里我们采用官方推荐的loader-utils读取options配置,也可用this.query获取配置对象,name是我们在loader配置项输入的字段名,source是源文件内容,最后需要返回,注意这里不能使用箭头函数,否则this指向会有错误,之后便可在webpack.config.js配置文件使用这个loader
module: {
rules: [{
test: /\.js$/,
use: {
loader: path.resolve(__dirname, './loaders/replaceLoader.js'),
options: {
name: 'echo'
}
}
}]
}
效果是会把world替换为name中得字符串,npm run build 后在main.js里面则可以看到此效果
loader 返回更多内容
官方文档配置的 API 中有loader的许多API ,除了this.query,常用的还有this.callback
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap?: SourceMap, // 可选参数,返回source-map
meta?: any // 可选参数,返回meta
);
可使用此API替代return
const loaderUtils = require('loader-utils');
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
const result = source.replace('world', options.name);
// return result;
this.callback(null, result);
}
loader 中编写异步代码
在loader中编写异步代码需要用this.async,我们可以再实现一个异步loader,创建replaceLoderAsync.js
const loaderUtils = require('loader-utils');
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
const callback = this.async()
setTimeout(() => {
const result = source.replace('world', options.name);
callback(null, result)
}, 1000)
}
其中this.async返回的是this.callback,因此可以当做return来使用,将replaceLoder.js中的代码改为
module.exports = function (source) {
const result = source.replace('echo', 'world');
this.callback(null, result)
}
我们实现先调用异步loader,将world改为echo,之后再调用同步loader将echo改为world,在配置文件的相应配置为
const path = require('path')
module.exports = {
mode: 'development', // 先设置为development,不压缩代码,方便调试
entry: {
main: './src/index.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
resolveLoader: {
// 会依次在node_modules、loaders文件夹中查找是否存在对应loader
modules: ['node_modules', './loaders']
},
module: {
rules: [{
test: /\.js$/,
use: [{
loader: 'replaceLoader.js'
},{
loader: 'replaceLoaderAsync.js',
options: {
name: 'echo'
}
}]
}]
}
}
之后运行npm run build即可在dist的main.js验证效果
编写loader的应用场景
- 监控前端方法错误:可以自己编写
loader检测业务代码中含有function关键字时自动用try...catch...包含代码块捕获错误,可以避免自己手写try...catch...导致的业务代码的臃肿 - 实现网站的中英文替换:可以将文字用占位符包裹,检测到占位符则根据环境变量替换为中英文,伪代码如下
module.exports = function (source) {
if(Node全局变量 === '中文') {
source.replace('{{title}}', '中文标题')
} else {
source.replace('{{title}}', 'english title')
}
const result = source.replace('echo', 'world');
this.callback(null, result)
}
总结
完整的代码可以参考我的github项目,欢迎star:https://github.com/zyqq/make-loader