在 DevTools 中现代化 CSS 基础设施

193 阅读6分钟

在 DevTools 中现代化 CSS 基础设施

原文链接:developer.chrome.com/blog/modern…

这篇文章是一系列博客文章的一部分,描述了我们对 DevTools 架构所做的更改及其构建方式。我们将解释 CSS 在历史上如何在 DevTools 中工作,以及我们如何在 DevTools 中现代化我们的 CSS,以准备(最终)迁移到用于在 JavaScript 文件中加载 CSS 的 Web 标准解决方案。

# DevTools 中 CSS 的先前状态

DevTools 以两种不同的方式实现 CSS:一种用于DevTools遗留部分中使用的 CSS 文件,另一种用于 DevTools 中使用的现代 Web 组件

DevTools 中的 CSS 实现是多年前定义的,现在已经过时了。DevTools 一直坚持使用该module.json模式,并且在删除这些文件方面付出了巨大的努力。删除这些文件的最后一个拦截器resources是用于加载 CSS 文件的部分。

我们想花时间探索不同的潜在解决方案,这些解决方案最终可能会变成CSS 模块脚本。目的是消除遗留系统造成的技术债务,同时使向 CSS 模块脚本的迁移过程更容易。

DevTools 中的任何 CSS 文件都被视为“遗留”module.json文件,因为它们是使用正在被删除的文件加载的。把所有CSS文件下所列resourcesmodule.json文件在同一目录下的CSS文件。

剩余文件的示例module.json

{“资源”:[“serviceWorkersView.css”,“serviceWorkerUpdateCycleView.css”]}

然后,这些 CSS 文件将填充一个全局对象映射,称为Root.Runtime.cachedResources从路径到其内容的映射。为了将样式添加到 DevTools,您需要使用要加载的文件的确切路径进行调用。registerRequiredCSS

示例调用registerRequiredCSS

构造函数() { ... this.registerRequiredCSS('ui/legacy/components/quick_open/filteredListWidget.css'); …}

这将检索 CSS 文件的内容,并<style>使用以下函数将其作为元素插入到页面中appendStyle:。

appendStyle使用内联样式元素添加 CSS 的函数

const content = Root.Runtime.cachedResources.get(cssFile) || '';if (!content) { console.error(cssFile + ' not preloaded. Check module.json');} const styleElement = document.createElement('style');styleElement.textContent = content;node.appendChild(styleElement) );

当我们引入现代 Web 组件(使用自定义元素)时,我们最初决定通过<style>组件文件本身中的内联标签****使用 CSS。这提出了自己的挑战:

  • **缺乏语法高亮支持。**为内联 CSS 提供语法高亮显示的插件往往不如在文件中编写的 CSS 的语法高亮显示和自动完成功能那么好.css
  • **构建性能开销。**内联 CSS 还意味着需要两次通过 linting:一次用于 CSS 文件,另一次用于内联 CSS。如果所有 CSS 都写在独立的 CSS 文件中,我们可以消除这种性能开销。
  • **挑战缩小。**内联 CSS 不容易缩小,因此没有任何 CSS 被缩小。DevTools 发布版本的文件大小也因同一 Web 组件的多个实例引入的重复 CSS 而增加。

我的实习项目的目标是为 CSS 基础架构找到一个解决方案,该解决方案适用于遗留基础架构和 DevTools 中使用的新 Web 组件。

#研究潜在的解决方案

问题可以分为两个不同的部分:

  • 弄清楚构建系统如何处理 CSS 文件。
  • 弄清楚 DevTools 如何导入和使用 CSS 文件。

我们为每个部分研究了不同的潜在解决方案,下面概述了这些解决方案。

#导入 CSS 文件

在 TypeScript 文件中导入和使用 CSS 的目标是尽可能接近 Web 标准,在整个 DevTools 中强制执行一致性并避免在我们的 HTML 中重复 CSS。我们还希望能够选择一种解决方案,将我们的更改迁移到新的 Web 平台标准,例如 CSS 模块脚本。

由于这些原因,@ import语句和标签似乎不适合 DevTools。它们不会与整个 DevTools 其余部分的导入保持一致,并导致Flash Of Unstyled Content (FOUC)。迁移到 CSS 模块脚本会更难,因为导入必须明确添加和处理,而不是使用<link>标签。

const output = LitHtml.html`<style> @import "css/styles.css"; </style><button> 你好世界</button>`
const output = LitHtml.html`<link rel="stylesheet" href="styles.css"><button> Hello World </button>`

使用@import或 的潜在解决方案<link>

相反,我们选择找到一种方法将 CSS 文件作为对象导入,以便我们可以使用其属性将其添加到Shadow Dom(DevTools 现在使用 Shadow DOM 已有几年了)。CSSStyleSheetadoptedStyleSheets

#捆绑选项

我们需要一种将 CSS 文件转换为CSSStyleSheet对象的方法,以便我们可以轻松地在 TypeScript 文件中对其进行操作。我们认为Rollupwebpack都是潜在的打包器来为我们进行这种转换。DevTools 已经在其生产构建中使用了 Rollup,但是在使用我们当前的构建系统时,将任何一个 bundler 添加到生产构建中都可能存在潜在的性能问题。我们与ChromiumGN 构建系统的集成使得打包更加困难,因此打包器往往无法与当前的 Chromium 构建系统很好地集成。

相反,我们探索了使用当前 GN 构建系统为我们进行这种转换的选项。

# DevTools 中使用 CSS 的新基础架构

新的解决方案包括使用adoptedStyleSheetsGN 构建系统向特定的 Shadow DOM 添加样式,同时生成 CSSStyleSheet 对象,这些对象可以被 adocumentShadowRoot.

// CustomButton.ts// 从CSSimport customButtonStyles from './customButton.css.js'生成的JS文件中导入CSS样式表内容;从'./otherStyles.css.js'导入otherStyles;导出类CustomButton extends HTMLElement { ... connectedCallback(): void { // 将样式添加到阴影根范围 this.shadow.adoptedStyleSheets = [customButtonStyles, otherStyles]; }}

使用adoptedStyleSheets有多种好处,包括:

  • 它正在成为现代网络标准
  • 防止重复的 CSS
  • 仅将样式应用于 Shadow DOM,这避免了由 CSS 文件中的重复类名或 ID 选择器引起的任何问题
  • 易于迁移到未来的 Web 标准,例如 CSS 模块脚本和导入断言

该解决方案的唯一警告是import语句需要.css.js导入文件。为了让 GN 在构建过程中生成 CSS 文件,我们编写了generate_css_js_files.js脚本。构建系统现在处理每个 CSS 文件并将其转换为默认导出对象的 JavaScript 文件CSSStyleSheet。这很棒,因为我们可以导入 CSS 文件并轻松采用它。此外,我们现在还可以轻松地缩小生产版本,从而节省文件大小:

const style = new CSSStyleSheet();styles.replaceSync( // 在生产中,我们也缩小了 CSS 样式 /`${isDebug ? output : cleanCSS.minify(output).styles} /*# sourceURL=${fileName} * /`/);导出默认样式;

从脚本生成的示例iconButton.css.js

#使用 ESLint 规则迁移遗留代码

虽然可以轻松地手动迁移 Web 组件,但迁移遗留使用的registerRequiredCSS过程更加复杂。注册遗留样式的两个主要函数是registerRequiredCSScreateShadowRootWithCoreStyles。我们决定,由于迁移这些调用的步骤相当机械,我们可以使用 ESLint 规则来应用修复并自动迁移遗留代码。DevTools 已经使用了许多特定于 DevTools 代码库的自定义规则。这很有帮助,因为ESLint已经将代码解析为抽象语法树(简称 AST),我们可以查询特定的调用节点,这些调用节点是注册 CSS 的调用。

我们在编写迁移 ESLint 规则时面临的最大问题是捕获边缘情况。我们希望确保在了解哪些边缘案例值得捕获和哪些应该手动迁移之间取得适当的平衡。我们还希望能够确保我们可以在构建系统未自动生成导入文件时告诉用户,.css.js因为这可以防止在运行时出现任何文件未找到错误。

使用 ESLint 规则进行迁移的一个缺点是我们无法更改系统中所需的 GN 构建文件。这些更改必须由用户在每个目录中手动完成。虽然这需要更多的工作,但这是确认每个导入的文件实际上是由构建系统生成的好方法.css.js

总的来说,使用 ESLint 规则进行这种迁移真的很有帮助,因为我们能够将遗留代码快速迁移到新的基础架构,并且 AST 随时可用意味着我们还可以处理规则中的多个边缘情况,并使用ESLint 的修复程序可靠地自动修复它们应用程序接口

#接下来怎么办?

到目前为止,Chromium DevTools 中的所有 Web 组件都已迁移到使用新的 CSS 基础架构,而不是使用内联样式。大多数遗留用法registerRequiredCSS也已迁移到使用新系统。剩下的就是删除尽可能多的module.json文件,然后迁移当前的基础架构以在未来实现 CSS 模块脚本!

#下载预览频道

考虑使用 Chrome CanaryDevBeta作为您的默认开发浏览器。通过这些预览频道,您可以访问最新的 DevTools 功能、测试尖端的 Web 平台 API,并在您的用户之前发现您网站上的问题!

使用以下选项讨论帖子中的新功能和更改,或与 DevTools 相关的任何其他内容。

订阅Chrome DevTools 博客,及时了解 DevTools 新闻