webpack为loader提供了一系列的配置属性,同时也为提取这些配置属性提供了一系列的工具方法,这些东西原先并不是webpack本身就包含的,只是用的地方多了, 他就放进去了。
使用 schema-utils
schema-utils是验证一个对象中是否包含这个这个属性,并且这个属性是否符合预期值,正好webpack中的loader、plugin都会有大量的配置,那就正好可以使用这个工具包来验证配置是否符合预期,用法如下:
- 安装依赖:
npm i -D schema-utils - 编写代码:
// 引入 schema-utils ,里面有一个 validate 函数 const { validate } = require('schema-utils'); // 定义预期配置值 const schema = { "type": "object", "properties": { // 有一个 name 属性,属性值为 boolean "name": { "type": "boolean" } }, // name 属性值必须要有 "required": ["name"], // 不能使用其他没有被声明的属性值 "additionalProperties": false } validate(schema, {name: ''}); // name 属性值不是 boolean 报错 validate(schema, {name: true, a: ''}); // 有其他没有被声明的属性值报错
一般在loader中使用的时候,配置量比较大可以定义为外部.json文件:
const { validate } = require('schema-utils'); // 定义外部json文件 const schema = require('./schema.json'); function loader(source, sourceMap, data) { // 获取loader配置 const options = this.getOptions(); validate(schema, options); this.callback( null, source, sourceMap, data ); } module.exports = loader
在webpack5之后,可以简化上面的流程:
const schema = require('./schema.json'); function loader(source, sourceMap, data) { this.getOptions(schema); return source; } module.exports = loader
如果验证不合规,schema-utils会抛出异常,如下
抛出异常自然就会阻断后续的任务,除非你捕获异常。
schema-utils 的校验能力很强,其实了解这个工具后,不仅可以在webpack的loader或plugin中去验证配置,还可以在其他你任何需要验证的地方使用。
schema-utils 内部使用 ajv 的 JSON-Schema模式实现参数校验,而 JSON-Schema 是一种以 JSON 格式描述数据结构的 公共规范,使用时至少需要提供 type 参数,如:
{ "type": "number" }
ajv默认支持下面这些基础属性(不想做搬运工,下面的就简写了,感兴趣的自行查看文档):
- number:数值型,支持整数、浮点数,支持如下校验规则:
maximum、minimum、exclusiveMaximum、exclusiveMinimum、multipleOf
interger:整数型,与number类似,也支持上面介绍的maximum等校验规则;- string:字符串型,支持如下校验规则:
maxLength、minLength、pattern、format
boolean:bool 值;- array:数组型,支持如下校验属性:
maxItems、minItems、uniqueItems、items
null:空值,常用于复合type类型,如type = ['object', 'null']支持传入对象结构或null值;- object:对象结构,这是一个比较负责的结构,支持如下校验属性:
maxProperties/minProperties、required、properties
patternProperties:同样用于定义对象属性的 Schema,但属性名支持正则表达式形式,例如:
{ type: "object", patternProperties: { "^el-.*$": {type: "string"}, "^a-.*$": {type: "string"} } }
additionalProperties:限定对象是否可以提供除properties、patternProperties之外的属性;
上面的这些是基本的配置属性解释,除了上面这些还有其他的通用规则字段: enum:枚举数组; const:静态数值,属性值必须完全等于 const 定义;
还有复合指令:
not:数值必须不符合该条件,例如:{type: "number", not: {minimum: 3}}时,传入数值必须小于 3;anyof:数值必须满足anyof条件之一;oneof:数值必须满足且只能满足oneof条件之一;allof:数值必须满足allof指定的所有条件;if/then/else:这个有点绕,所以我翻译一下:
{ "type": "object", "if": {"properties": {"foo": {"minimum": 10}}}, "then": {"required": ["bar"]}, "else": {"required": ["baz"]} }
配置长上面这样,直接用代码翻译:
if (option.foo < 10) { if (option.bar == null) { throw new Error() } } else { if (option.baz == null) { throw new Error() } }
口语瞎 J er 翻译:
if-> 如果传进来的配置满足:有一个foo的属性,它的值必须小于 10
then-> 如果通过了if老哥的考验,那么传进来的属性中必须有一个 bar 属性
else-> 如果没有通过if老哥的考验,那么传进来的属性中必须有一个 baz 属性
大白话翻译:
满足
if的条件就需要进入then,然后再满足then的条件,不满足if的条件就需要进入else,然后满足else的条件。
这些基础数据类型与校验规则让我们可以非常完善的属性规则定义,再也不必写a != null || typeof a === 'string' ...这样的验证代码了,我们使用 schema-utils 时大部分时间都需要构建这些配置体系,所以简写就是让你多看文档。
使用 loader-utils
loader-utils见名知意,就是webpack开发loader的工具类,据说在webpack5之前这玩意很重要,别问为什么是据说,因为我在webpack5之前就没开发过loader,然后webpack5看这玩意这么多人用,还用的这么频繁,就给里面的一些功能,做到了loader注入的上下文中了。
被裁减后的 loader-utils 仅保留了四个接口:
urlToRequest:用于将模块路径转换为文件路径的工具函数;isUrlRequest:用于判定字符串是否为模块请求路径;getHashDigest:用于计算内容 Hash 值;interpolateName:用于拼接文件名的模板工具;
课程中只讲了
interpolateName,说上面的三个没多少地方使用,就没讲了。
在配置webpack的时候,output.filename属性支持[path]/[name].[ext]这样的占位符,用interpolateName这个方法就可以做到这样的效果,使用方法如下:
const {interpolateName} = require('loader-utils'); function loader(source, sourceMap, data) { const url = interpolateName(this, "js/[hash].script.[ext]", { content: source }); this.emitFile(url, source); return source; } loader.pitch = function (remainingRequest, previousRequest, data) { } module.exports = loader
interpolateName支持如下占位符,:
[ext]:原始资源文件的扩展名;[name]:原始文件名;[path]:原始文件相对context参数的路径;[folder]:原始文件所在的文件夹;[query]:查询参数,即?foo=bar[contenthash]:文件内容hash
上面这些占位符在官网里面都有详细的解释,之前有提到过,而且这里的占位符没有Template String全。
总结
了解schema-utils和loader-utils能更高效的开发loader,loader-utils是为loader服务的,但是schema-utils并不是只为loader服务,了解这些工具,可以让你的开发更高效和稳定。
