webpack系统学习(十四)如何编写一个loader

  • Post author:
  • Post category:其他




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官网

查看。



版权声明:本文为Niall_Tonshall原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。