编写一个 loader

2020-12-30  本文已影响0人  jluemmmm

loader 是一个导出为一个函数的 node 模块,该函数在 loader 转换资源的时候调用。给定的函数将调用 loader模块,并通过 this 上下文访问。

单个 loader 调用

当一个loader 在资源中使用,这个loader只能传入一个参数,这个参数是一个包含资源文件内容的字符串。同步 loader可以返回一个代表模块转化后的值,loader也可以通过使用this.callback(err, value)函数,返回任意数量的值。错误传递给 this.callback函数,或者放入同步 loader中。

多个loader

链式调用多个 loader 时,会以相反顺序执行。

编写 loader 注意事项

1. 简单易用

loader 应该只做单一任务,使得loader易于维护,可以在更多场景链式调用

2. 链式调用

使用loader可以链式调用的优势,写五个简单的loader实现五项任务,而不是一个loader实现五项任务。

3. 模块化

保证输出模块化,loader生成的模块于普通模块遵循相同的设计原则

4. 无状态

确保loader 在不同模块转换之间不保存状态,每次运行都独立于其他编译模块以及相同模块之前的编译结果

5. loader utilities

利用 loader-utils 包,提供非常多的有用工具,常用的工具是获取传递给 loader 的选项。schema-utils 包配合 loader-utils,用于保证 loader 选项,进行与 JSON schema结构一致性的校验。

import { getOptions } from 'loader-utils'
import validateOptions from 'schema-utils'

const schema = {
  type: 'object',
  properties: {
    test: {
      type: 'sting'
    }
  }
}

export default function(source) {
  const options = getOptions(this)
  validateOptions(schema, options, 'Example loader')
  /* 对资源应用进行一些转换 */
  return `export default ${ JSON.stringify(source) }`
}

6. loader 依赖

如果一个loader使用外部资源(例如,从文件系统读取),必须声明它。这些信息用于使缓存 loaders 无效,以及在观察者模式下重新编译。

import path from 'path'
export default function(source) {
  var callback = this.async()
  var headPath = path.resolve('header.js')

  this.addDependency(headPath)

  fs.readFile(headPath, 'utf-8', function(err, header) {
    if (err) return callback(err)
    callback(null, header + "\n" + source)
  })
}

7. 模块依赖

根据模块类型,可能会有不同的模块指定依赖关系。如在 css 中,使用 @importurl(...) 语句来声明依赖。这些模块应由模块系统解析。可以通过以下两种方式的一种来实现:

css-loader中,将 @import 语句转化为 require 其他样式文件,将 url(...) 替换为 require 引用文件,实现将依赖关系转化为 require 声明

less-loader,无法将每个 @import 转化为 require,因为所有 .less文件中的变量和混合跟踪必须经历一次编译。因此,less-loader 将 less 编译器进行扩展,自定义路径解析逻辑,然后通过 webpack 的 this.resolve解析依赖。

如果语言只支持相对 url,如 url(file)总是指向 ./file,通过 ~来指定已安装的模块(如 node_modules中的模块)。所以对于url,相当于 url('~some-library/img.jpg')

8. 通用代码

避免在 loader 处理的每个模块中生成通用代码。应该在 loader 中创建一个运行时文件,并生成 require 语句以引用该共享模块。

9. 绝对路径

不要在模块代码中插入绝对路径,当项目根路径变化时,文件绝对路径也会变化。loader-utils 中的stringifyRequest方法,可以将绝对路径转化为相对路径。

10. 同等依赖

如果你的 loader 简单包裹另外一个包,应该把这个包作为 peerDependency 引入。这种方式允许应用程序开发者在必要的情况下,在package.json中指定所需的确定版本。

编写一个最最简单的 loader

const _loaderUtils = require('loader-utils')

module.exports = (source) => {
  const options = _loaderUtils.getOptions(this);
  console.log(this)
  source = source.replace(/\[name\]/g, ''); 
  return `export default ${ JSON.stringify(source) }`;
};


rules 中通过指向具体的路径使用

{
  test: /\.txt$/,
  use: {
    loader: path.resolve(__dirname, './test/loader.js'),
    options: {
      name: 'Alice'
    }
  }
}
上一篇 下一篇

猜你喜欢

热点阅读