当我们使用 console.log(…) 输出一些信息的时候, 浏览器会按照一定的格式输出这些信息到控制台。
例如:
我们可以看出浏览器对于不同的数据是做了不同的格式处理的,对于简单的字符串是直接输出 “xxx”,但是对于一些对象或数组类型,会先输出一个简单信息然后有一个展开的箭头可以查看详细的信息。
例如上图的 friend 会在第一行先展示 Array(1) 代表这是数组并且长度为 1,点击 friend 左边的箭头又可以查看数组的具体元素。
那么如果我们调用 console.log 的时候不希望浏览器安装这种格式去输出我们的信息,或者这样输出对我们开发调试查看问题不友好的时候,我们就可以使用 devtoolsFormatters 自定义我们自己的数据的输出了。
在自定义 devtoolsFormatters 之前, 你需要先打开浏览器的 Enable custom formatters 设置。
打开 chrome 控制台, 右上角有个设置的图标点进去
在 Preferences 的 Console 中有一个 Enable custom formatters,勾选上就可以了
那么现在就可以开始进入主题使用 devtoolsFormatters 自定义输出了
devtoolsFormatters 挂载在 window 对象上,如果你从未对其赋值,那么它的值是 undefined
,期望的值是一个 formatter 数组,formatter 是一个包含 header,hasBody,body 三个函数的对象。
const formatter = {
header(){...},
hasBody(){...},
body(){...},
}
if (window.devtoolsFormatters instanceof Array) {
window.devtoolsFormatters.push(formatter);
} else {
window.devtoolsFormatters = [formatter];
}
formatter 包含了 header,hasBody,body 三个函数,且三个函数都会接收 console.log 中传的数据,例如
console.log({ name: 'eno'; });
那么三个函数都会接收到参数 {name: 'eno'}
。hasBody 函数需要返回一个 bool 函数,而 header 和 body 函数返回一个 jsonMl 格式的数据,
jsonML
jsonML 就是使用 json 格式描述 dom(xml) 节点的一种方式,其格式为 [tag-name: string, attributes: object, element-list: string | jsonMl]
,不懂可以自行查阅相关资料。
header
header 用来格式化输出的第一行信息,比如我们第一个例子:console.log({ name: 'eno', friend: [{ name: 'zeng'}]})
时候第一行会输出一个简单信息
{name: 'eno', friend: Array(1)}
,我们的 header 函数将会决定这一部分输出的内容是什么。如果输出的是 null,浏览器将会使用自己的方式格式化信息输出。
body
body 就是我们输出负责数据时展开的详细信息部分,formatter 的 body 函数会决定这一部分输出什么。body 函数和 header 是一样的,同样返回一个 jsonML 格式的数据。
hasBody
顾名思义就是有没有body,该函数需要返回一个 boolean 值,这将决定我们的输出中有没有箭头,就是点击展开查看 body 的那个箭头。如果返回的是 true,那么将会显示一个箭头,如果是 false 那么久没有。
写一个具体的示例来体现 devtoolsFormatters 用法会更加直接。
背景:假设我们有一个类叫 CustomerError 使我们自定义的一个报错类型,现在 console.log 对我们的报错输出不够理想,我们需要格式化输出更好的去调试代码。
CustomerError
首先先来个 CustomerError,用 message 记录报错信息,fileName 和 lineNumber 分别记录报错的文件和行号,重写一下 toString 方法;
顺便来一个 isCustomerError 的判断函数,判断一个数据是否是 CustomerError 的数据
class CustomerError {
constructor(message, fileName, lineNumber) {
this.message = message;
if (fileName && lineNumber) {
this.target = {
fileName,
lineNumber,
}
}
}
toString() {
return `CustomerError: ${this.message}`;
}
}
const isCustomerError = (val) => val instanceof CustomerError;
formatter
接下来就是 formatter,我们判断数据是否是我们的 CustomerError,如果不是交个浏览器模式处理即可。如果是则返回一个 jsonML 的数据。
const formatter = {
header(obj) {
if (!isCustomerError(obj)) return null;
const style = 'color:red;' // 使用 style 可以设置输出的样式,例如颜色,字体等,这里用红色输出 header 部分
return [
'div',
{ style },
obj.toString(),
]
},
hasBody(obj) {
return isCustomerError(obj);
},
body(obj) {
if (!isCustomerError(obj)) return null;
const style = 'text-indent: 2em;';
const jsonMl = [
'div',
{ style },
[
'div',
{},
[
'div',
{},
`message: "${obj.message}"`,
],
],
];
if (obj.target) {
jsonMl.push([
'div',
{},
[
'div',
{},
`target: "${obj.target.fileName}:${obj.target.lineNumber}"`,
],
]);
}
return jsonMl;
}
}
测试一下
声明一个 CustomerError 实例,然后 console.log 一下看看控制台会发生什么
const customerError = new CustomerError('This is my customer error!', 'index.html', 93);
console.log(customerError);
jsonML 的第一个元素是 tag-name,目前仅试过返回 div
和 span
成功,尝试返回 p
和 ul
都失败了。
div
和 span
两者的却别就是换行,使用 div 的话内容独占一行,如果使用 span,可以让多个 jsonML 的内容在同一行显示。
jsonML 的第二个元素 attributes,目前仅使用 style,不清楚 devtoolsFormatters 还可以支持那些属性去格式输出,因为没有找到有官方文档对这个函数的说明。style 的话参考 css 的语法就可以了。
上面的例子可以看出,其实 jsonML 不一定是传入三个元素,同意也可以放回四个、五个甚至更多元素,只不过后面的元素都会被当做渲染内容就是 element-list
以上就是对 devtoolsFormatters 的用法,如果进一步了解实战中的用法,也可以参考 vue 源码中的 initCustomFormatter 函数