1. 创建一个简单的loader
所谓 loader 只是一个导出为函数的 JavaScript 模块。loader runner 会调用这个函数,然后把上一个 loader 产生的结果或者资源文件(resource file)传入进去。函数的 this 上下文将由 webpack 填充。
假设我们现在有一个
index.js:
//index.js
console.log("hello world")
在不对js文件进行额外处理时对其进行打包,得到
/dist/main.js
//main.js
...
/***/ (function(module, exports) {
eval("console.log(\"hello world\")\n\n//# sourceURL=webpack:///./src/index.js?");
/***/ })
...
我们希望借助loader使js文件中要输出的
hello world
转换为
hello js
,像上文所说,一个loader就是一个导出为函数的JavaScript模块。
创建
replaceLoader.js
,现在的文件目录结构:
//replaceLoader.js
//使用声明式函数而不是箭头函数,因为在该函数中我们需要使用this
module.exports = function (source){
return source.replace('world','js')
}
然后我们使用
replaceLoader
对js文件进行处理。
修改
webpack.config.js:
...
module: {
rules: [{
test: /\.js/,
use: [
path.resolve(__dirname,'./loader/replaceLoader')
]
}]
}
...
重新打包,查看此时生成的
main.js
:
...
/***/ (function(module, exports) {
eval("console.log(\"hello js\")\n\n//# sourceURL=webpack:///./src/index.js?");
/***/ })
...
不难发现,此时运行
main.js
,控制台的输出就从
hello world
转换成了
hello js
。
这个
replaceLoader
就是一个简单的loader,它的作用就是在打包js文件时,将js文件中的
world
替换成
js
。
2. 设置resolveLoader简化自定义loader的加载路径
修改
webpack.config.js:
...
resolveLoader: {
modules: ['node_modules', './loader']
}
...
这样设置
resolveLoader
,webpack在打包文件时,如果需要使用loader,它将首先在
node_modules
中查找该loader,如果找不到,就会在
./loader
文件夹下面查找。
3. 向Loader传入参数
我们在使用Loader时,可以向其传递一些参数,比如我们在使用
url-loader
时:
{
test:/\.(png|jpg|gif)$/,
use:{
loader: 'url-loader',
options: {
name: '[name].[ext]',//placeholder占位符
outputPath:'image/',
limit: 2048 //2kb
}
}
}
我们为
url-loader
设置了
options
,这里面的配置是如何被
url-loader
获取并使用呢?
查看webpack官网上
Loader API
对
options
的使用,我们发现:
this.query
如果这个
loader
配置了
options
对象的话,this.query 就指向这个 option 对象。
如果 loader 中没有 options,而是以 query 字符串作为参数调用时,this.query 就是一个以 ? 开头的字符串。
使用
loader-utils
中提供的
getOptions
方法 来提取给定 loader 的 option。
修改
webpack.config.js
:
...
{
test: /\.js/,
use: [
{
loader: 'replaceLoader',
options: {
from: 'world',
to: 'js'
}
}
]
}
...
我们现在修改
replaceLoader
,查看一下
this.query
的内容:
//使用声明式函数而不是箭头函数,因为在该函数中我们需要使用this
module.exports = function (source){
console.log(this.query)
return source.replace('world','js')
}
打包时的输出:
{ from: 'world', to: 'js' }
...
所以
this.query
就是一个对象,里面包含了我们出入的options信息。
修改
replaceLoader
,使用
options
传入的配置信息。
//使用声明式函数而不是箭头函数,因为在该函数中我们需要使用this
module.exports = function (source){
return source.replace(this.query.from,this.query.to)
}
但是我们注意到,webpack官网建议我们使用
loader-utils
中提供的
getOptions
方法 来提取给定 loader 的 option。
npm i loader-util -s
修改
replaceLoader.js
:
const loaderUtils = require('loader-utils')
module.exports = function (source){
const options = loaderUtils.getOptions(this)
return source.replace(options.from, options.to)
}
在返回转换后的
content
时,可以使用
this.callback()
,
this.callback
方法更加灵活,因为它允许传递多个参数,而不仅仅是
content
。
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap?: SourceMap,
meta?: any
);
第一个参数必须是 Error 或者 null
第二个参数是一个 string 或者 Buffer。
可选的:第三个参数必须是一个可以被这个模块解析的 source map。
可选的:第四个选项,会被 webpack 忽略,可以是任何东西(例如一些元数据)。
使用
this.callback
返回处理结果,修改
replaceLoader.js
:
const loaderUtils = require('loader-utils')
module.exports = function (source){
const options = loaderUtils.getOptions(this)
this.callback(null,source.replace(options.from, options.to))
return
}
4. 在Loader中使用异步处理
异步处理需要使用
this.async
告诉 loader-runner 这个 loader 将会异步地回调。返回 this.callback。
新创建一个
replaceLoaderAsync.js
:
//replaceLoaderAsync和replaceLoader的异步版本
const loaderUtils = require('loader-utils')
module.exports = function (source){
const options = loaderUtils.getOptions(this)
const callBack = this.async()
setTimeout(()=>{
const result = source.replace(options.from, options.to)
callBack(null, result)
},5000)
}
我们先不使用这个异步loader,查看打包信息:
...
Time: 124ms
...
打包时间为124ms。
现在我们更改
webpack.config.js
,使用
replaceLoaderAsync
{
test: /\.js/,
use: [
{
loader: 'replaceLoader',
options: {
from: 'world',
to: 'js'
}
},{
loader: 'replaceLoaderAsync',
options: {
from: 'hello',
to: 'hi'
}
}
]
}
现在再次打包:
...
Time: 5254ms
...
打包时间增加到了5254ms。
同时查看打包后生成的
main.js
:
...
/***/ (function(module, exports) {
eval("console.log(\"hi js\")\n\n//# sourceURL=webpack:///./src/index.js?");
/***/ })
...
两个loader都生效了。
结语:
loader中还有很多配置,更多配置可以前往
webpack官网
查看。