使用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