自定义格式化程序

自定义格式化程序允许网站控制 Web 控制台调试器 中变量的显示方式。

此功能对于处理复杂对象结构的 Web 应用程序、为原生变量定义对象的 JavaScript 框架或像 ClojureScript 这样编译成 JavaScript 的框架特别有用。

自定义格式化通过显示其变量更直观和信息丰富的表示形式来增强调试过程。

启用自定义格式化

要启用自定义格式化,请切换到 设置 面板,并在“高级设置”下选中名为“启用自定义格式化程序”的选项。此设置将在您下次打开 DevTools 时生效。

../../_images/enable-custom-formatters-setting.png

API

启用自定义格式化程序后,网站可以自定义 Web 控制台和调试器中某些变量的显示方式。这是通过使用名为 devtoolsFormatters 的全局数组定义自定义格式化程序来实现的。此数组中的每个条目都表示一个特定的格式化程序,它可能处理特定类型的变量。如果未为变量定义格式化程序,则使用其默认格式进行显示。

自定义格式化程序结构

每个格式化程序至少必须包含一个 header 函数。此函数必须返回一个 JsonML 数组或 null。如果返回 null,则将调用 devtoolsFormatters 中下一个条目的 header 函数。如果所有 header 函数都返回 null,则将使用 DevTools 的默认渲染方式显示对象。

除了 header 函数外,格式化程序还可以包含 body 函数。 hasBody 函数指示 body 是否存在。如果 hasBody 返回 true,则可以展开对象以显示更多详细信息。然后, body 函数返回实际主体。与 header 函数一样,它可以返回 JsonML 对象或 null

所有三个函数都将对象作为第一个参数,并将可选的配置对象作为第二个参数,这允许传递其他信息。

以下是这些函数的详细说明

header(object, config)

返回 JsonML 数组或 null。如果返回 null,则使用默认格式显示对象。 config 参数是可选的。可以使用特殊的 object 模板传递此参数(请参阅 生成子元素)。

hasBody(object, config)

返回一个布尔值,指示对象是否可以展开以显示更多详细信息。 config 参数是可选的。可以使用特殊的 object 模板传递此参数(请参阅 生成子元素)。

body(object, config)

返回 JsonML 数组或 null。如果返回 null,则使用默认格式显示对象。 config 参数是可选的。可以使用特殊的 object 模板传递此参数(请参阅 生成子元素)。

HTML 模板格式

每个 HTML 模板都以基于 JsonML 标准的格式进行编码。每个元素都以以下格式表示为列表

[tagName, {"style": "name: value; ..."}, child1, ]

允许使用以下 HTML 标签

<span><div><ol><ul><li><table><tr><td>

可选的 style 属性可以包含 CSS 声明字符串。可以应用的 CSS 属性为

  • align*

  • background*background-image 仅允许 data: URL)

  • border*

  • box*

  • clear

  • color

  • cursor

  • display

  • float

  • font*

  • justify*

  • line*

  • margin*

  • padding*

  • position(仅接受 staticrelative 值)

  • text*

  • transition*

  • outline*

  • vertical-align

  • white-space

  • word*

  • writing*

  • width

  • min-width

  • max-width

  • height

  • min-height

  • max-height

子元素可以是另一个元素、字符串或对象引用。

生成子元素

可以通过定义特殊的 object 模板来创建子元素。此模板的格式为

["object", {"object": objectToInspect, "config": configObject}]

示例

简单示例

让我们来看一个简单的示例来说明自定义格式化程序的概念

window.devtoolsFormatters = [
  {
    header: variable => {
      if (variable.hasOwnProperty('foo')) {
        return [
          'span', {
            'style': `
              font-family: "Comic Sans MS", fantasy;
              font-size: 3rem;
              color: green;
            `
          },
          'foo'
        ];
      }
      return null;
    }
  }
];

在上面的示例中,为变量定义了一个自定义格式化程序。格式化程序的 header 属性是一个函数,用于确定变量的显示方式。在这种情况下,如果变量具有名为 foo 的属性,它将呈现为具有特定样式的 <span> 元素。它将像这样记录到 Web 控制台

../../_images/simple-custom-formatter-example.png

完整示例

对于更复杂的示例,让我们考虑显示 Date 对象

<!DOCTYPE html>
<html>
  <head>
    <title>Custom formatter for dates</title>
  </head>
  <body>
    <script>
    window.devtoolsFormatters = [
      {
        header: obj => {
          if (obj instanceof Date) {
            return ['div', {'style': 'font-weight: bold;'},
              `Date: ${obj.toLocaleDateString()} ${obj.toLocaleTimeString()}`
            ];
          }
          return null;
        },
        hasBody: obj => obj instanceof Date,
        body: obj => {
          if (obj instanceof Date) {
            return ['div', {},
              ['div', {}, `Year: ${obj.getFullYear()}`],
              ['div', {}, `Month: ${obj.getMonth() + 1}`],
              ['div', {}, `Day: ${obj.getDate()}`],
              ['div', {}, `Hour: ${obj.getHours()}`],
              ['div', {}, `Minutes: ${obj.getMinutes()}`],
              ['div', {}, `Seconds: ${obj.getSeconds()}`]
            ];
          }
          return null;
        }
      }
    ];
    </script>
  </body>
</html>

使用此自定义格式化程序,记录到控制台的 Date 对象将以格式化的方式显示日期和时间,以及其各个组件的单独细分。在控制台中,对象将像这样记录

../../_images/date-custom-formatter-example.png

带有对象引用的示例

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <title>Menu custom formatter</title>
    <script>
      const menu = [
        {
          url: '/',
          text: 'Home',
        },
        {
          url: '/nested',
          text: 'Nested',
          subitems: [
            {
              url: '/nested/1',
              text: 'Nested 1'
            },
            {
              url: '/nested/2',
              text: 'Nested 2',
              subitems: [
                {
                  url: '/nested/2/1',
                  text: 'Nested 2.1'
                },
                {
                  url: '/nested/2/2',
                  text: 'Nested 2.2'
                }
              ]
            },
            {
              url: '/nested/3',
              text: 'Nested 3'
            }
          ]
        },
        {
          url: '/about',
          text: 'About'
        },
        {
          url: '/contact',
          text: 'Contact'
        }
      ];

      window.devtoolsFormatters = [
        {
          header: (obj, config) => {
            if (obj instanceof Array && obj.every(item => item.hasOwnProperty('url') && item.hasOwnProperty('text'))) {
              return ['div', {'style': 'font-weight: bold;'}, `Menu: ${obj.length} entries`];
            }
            return null;
          },
          hasBody: obj => obj instanceof Array && obj.every(item => item.hasOwnProperty('url') && item.hasOwnProperty('text')),
          body: (obj, config) => {
            const levelColors = ['red', 'blue', 'green'];
            if (config === undefined) {
              config = { level: 0 };
            } else {
              config.level++;
            }

            return ['div', {'style': `margin-left: 15px; color: ${levelColors[config.level % levelColors.length]}`}, ...obj.map(item => {
              const subitem = ['div', ['div', `${item.text}: ${item.url}`]];
              if (item.hasOwnProperty('subitems')) {
                subitem.push(['object', {'object': item.subitems, config: {level: config.level}}]);
              }
              return subitem;
            })];
          }
        }
      ];
      console.log(menu);
    </script>
  </head>
  <body>
  </body>
</html>

此示例显示了一个带有嵌套子项的菜单对象。自定义格式化程序是递归的,因此它也将显示所有子项。此示例的输出如下所示

../../_images/menu.png

调试自定义格式化程序

如果自定义格式化程序包含错误,则会将错误消息记录到控制台,解释该问题。只要可能,错误消息还将包含指向代码中错误确切位置的源链接。

../../_images/custom-formatter-error.png

更多提示

  • 分析要格式化的变量的结构和行为,了解区分它们的键属性或类层次结构。

  • 在测试对象类型时,如果对象是特定类的实例,请使用 instanceof。如果对象是普通对象,请使用 hasOwnProperty 检查特定属性。

  • 使用不同类型的变量测试您的格式化程序,以确保它们按预期工作并准确地处理各种情况。

  • 通过 生成子元素 嵌套格式化程序,以更易读的方式显示复杂对象。

  • 使用 config 参数将其他信息传递给格式化程序,例如当前递归级别。

  • 如果您有多个格式化程序,请记住在每个格式化程序中检查对象的类型,并在对象不是预期类型时返回 null。否则,格式化程序将应用于所有对象,这可能会导致意外行为。

  • 明智地选择您的格式。对于大型对象,最好仅显示对象的摘要,并在需要时允许用户展开它。

  • 每个记录的对象都将调用格式化程序挂钩,这可能会影响性能。因此,您应该以小型且快速的挂钩为目标。

现有格式化程序

有现有的格式化程序可用于满足不同的需求。一些例子包括