3.一步步了解 Webpack Loader 开发

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战node

Webpack Loader

loader 用于对模块的源代码进行转换webpack

配置

  • 单个 loader,你能够简单经过在 rule 对象设置 path.resolve 指向这个本地文件
  • 多个 loaders,你可使用 resolveLoader.modules 配置
  • 测试的时候可使用 npm link
  • 当链式调用多个 loader 的时候,请记住它们会以相反的顺序执行。取决于数组写法格式,从右向左或者从下向上执行
    • 最后的 loader 最先调用,将会传入原始资源内容。
    • 第一个 loader 最后调用,指望值是传出 JavaScript 和 source map(可选)。
    • 中间的 loader 执行时,会传入前一个 loader 传出的结果。

demo: 将 var 关键词替换为 const

# loader.js
module.exports = function (source) {
    return source.replace(/var/g, 'const')
}

# index.js
function test() {
    var a = 1;
    var b = 2;
    var c = 3;
    console.log(a, b, c);
}

test()

# wepback.config.js
const path = require('path')

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /.js$/,
                use: [
                    {
                        loader: path.resolve('./src/loader1.js'),
                    },
                ]
            }
        ]
    },
}
# `npm run build`,获得打包文件 `bundle.js`
复制代码

用法准则

  • 简单易用
  • 使用链式传递。
  • 模块化的输出。
  • 确保无状态
  • 使用 loader utilities
  • 记录 loader 的依赖
  • 解析模块依赖关系
  • 提取通用代码
  • 避免绝对路径
  • 使用 peer dependencies

简单

loaders 应该只作单一任务。这不只使每一个 loader 易维护,也能够在更多场景链式调用。git

链式

利用 loader 能够链式调用的优点。写五个简单的 loader 实现五项任务,而不是一个 loader 实现五项任务;github

loader 能够被链式调用意味着不必定要输出 JavaScript。只要下一个 loader 能够处理这个输出,这个 loader 就能够返回任意类型的模块web

无状态

确保 loader 在不一样模块转换之间不保存状态。每次运行都应该独立于其余编译模块以及相同模块以前的编译结果。npm

loader 工具库

充分利用 loader-utils 包。它提供了许多有用的工具,但最经常使用的一种工具是获取传递给 loader 的选项。schema-utils 包配合 loader-utils,用于保证 loader 选项,进行与 JSON Schema 结构一致的校验。这里有一个简单使用二者的例子:json

loader.jsapi

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

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

export default function(source) {
  const options = getOptions(this);

  validateOptions(schema, options, 'Example Loader');

  // 对资源应用一些转换……

  return `export default ${ JSON.stringify(source) }`;
};
复制代码

loader 依赖

若是一个 loader 使用外部资源(例如,从文件系统读取),必须声明它。这些信息用于使缓存 loaders 无效,以及在观察模式(watch mode)下重编译。下面是一个简单示例,说明如何使用 addDependency 方法实现上述声明:数组

loader.js缓存

import path from 'path';

export default function(source) {
  var callback = this.async();
  var headerPath = path.resolve('header.js');

  this.addDependency(headerPath);

  fs.readFile(headerPath, 'utf-8', function(err, header) {
    if(err) return callback(err);
    callback(null, header + "\n" + source);
  });
};
复制代码

模块依赖

根据模块类型,可能会有不一样的模式指定依赖关系。例如在 CSS 中,使用 @import 和 url(...) 语句来声明依赖。这些依赖关系应该由模块系统解析。

能够经过如下两种方式中的一种来实现:

  • 经过把它们转化成 require 语句。
  • 使用 this.resolve 函数解析路径

通用代码

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

绝对路径

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

同等依赖(Peer Dependencies)

若是你的 loader 简单包裹另一个包,你应该把这个包做为一个 peerDependency 引入。这种方式容许应用程序开发者在必要状况下,在 package.json 中指定所需的肯定版本

测试

当你遵循上面的用法准则编写了一个 loader,而且能够在本地运行。下一步该作什么呢?让咱们用一个简单的单元测试,来保证 loader 可以按照咱们预期的方式正确运行。咱们将使用 Jest 框架。而后还须要安装 babel-jest 和容许咱们使用 import / export 和 async / await 的一些预设环境(presets)。让咱们开始安装,而且将这些依赖保存为 devDependencies

npm install --save-dev jest babel-jest babel-preset-env
复制代码

.babelrc

{
  "presets": [[
    "env",
    {
      "targets": {
        "node": "4"
      }
    }
  ]]
复制代码

仅供学习参考

参考

相关文章
相关标签/搜索