美文网首页
编写一个 loader

编写一个 loader

作者: jluemmmm | 来源:发表于2020-12-30 21:10 被阅读0次

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

    单个 loader 调用

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

    多个loader

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

    • 最后的 loader 最早调用,将会传入原始资源内容。
    • 第一个 loader 最后调用,期望值传出 js 和 sourcemap(可选)
    • 中间的 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(...) 语句来声明依赖。这些模块应由模块系统解析。可以通过以下两种方式的一种来实现:

    • 通过将其转化成 require 语句
    • 使用this.resolve函数解析路径

    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'
        }
      }
    }
    

    相关文章

      网友评论

          本文标题:编写一个 loader

          本文链接:https://www.haomeiwen.com/subject/tcxkoktx.html