web.dev 工程博客 #1:我们如何构建网站和使用 Web 组件

这是 web.dev 工程博客的第一篇文章。 在未来几个月内,我们希望分享我们工作中可行的洞见,敬请关注带有工程博客标记的帖子! 在本课程中,我们将介绍静态网站的构建流程,以及(可选!Web 组件背后的 JavaScript。

web.dev 提供有关打造现代 Web 体验的内容,并可让您衡量网站的性能。精明的用户可能已经意识到,“衡量”页面只是 Lighthouse 的界面,后者也可在 Chrome 的开发者工具中使用。登录 web.dev 后,您可以定期对自己的网站运行 Lighthouse 审核,以便了解其得分随时间的变化情况。 我稍后会再次介绍“衡量”页面,因为我们认为它非常重要。🎊?

从本质上讲,web.dev 是一个由一系列 Markdown 文件生成的静态网站。我们之所以选择使用 Eleventy,是因为它是一款经过精心打磨的可扩展工具,可轻松将 Markdown 转换为 HTML。

我们还使用现代 JavaScript 软件包,仅向支持 type="module"(包括 asyncawait)的浏览器提供。我们也乐于使用永续更新型浏览器支持但少数旧版不支持的功能。由于我们是静态网站,因此无需 JavaScript 即可阅读我们的内容。

构建流程(包括生成静态 HTML 并使用 Rollup 捆绑 JavaScript)完成后,您就可以使用简单的静态服务器托管 web.dev 以进行测试。该网站几乎完全是静态的,但我们仍有几项特殊需求,需要使用自定义 Node.js 服务器。 其中包括针对无效网域的重定向,以及为即将推出的国际化功能解析用户首选语言的代码。

静态生成

web.dev 上的每个网页都是用 Markdown 编写的。所有页面都包含前言,其中描述了与每篇帖子相关的元数据。这些元数据会提取到每个网页的布局中,以创建标题、标记等。 示例如下:

---
layout: post
title: What is network reliability and how do you measure it?
authors:
  - jeffposnick
date: 2018-11-05
description: |
  The modern web is enjoyed by a wide swath of people…
---

The modern web is enjoyed by a wide swath of [people](https://www.youtube.com/watch?v=dQw4w9WgXcQ), using a range of different devices and types of network connections.

Your creations can reach users all across the world...

借助这些前言,我们可以定义作者、发布日期和标记等任意属性。Eleventy 会在几乎所有插件、模板或其他我们希望执行智能操作的上下文中,以数据的形式方便地公开前言。数据对象还包含 Eleventy 所说的数据级联,即从每个网页、网页使用的布局以及分层文件夹结构中提取的各种数据。

每种独特的布局都描述了不同类型的内容,并且可以从其他布局继承。在 web.dev 上,我们使用此功能正确构建不同类型的内容(例如帖子和 Codelab),同时仍然共享一个顶级 HTML 布局。

集合

Eleventy 提供了一种以编程方式构建任意内容集合的方法。这样,我们就可以为博文作者构建分页支持,并生成虚拟页面(磁盘上没有匹配的 Markdown 文件的页面)。 例如,我们使用包含其永久链接的表达式(以便为每位作者重新渲染模板)和后备集合的模板构建作者页面。

例如,这样就可以创建一个包含 Addy 的所有帖子的简单页面!

限制

目前,我们无法轻松钩入 Eleventy 的构建流程,因为它是声明式而非命令式:您描述您想要什么,而不是您想要如何实现。 很难将 Eleventy 作为大型构建工具的一部分来运行,因为它只能通过其命令行界面调用。

模板

web.dev 使用最初由 Mozilla 开发的 Nunjucks 模板系统。Nunjucks 具有循环和条件等典型模板功能,但还允许我们定义用于生成更多 HTML 或调用其他逻辑的短代码

与大多数构建静态内容网站的团队一样,我们从小处着手,并随着时间的推移添加了短代码(目前约有 20 个)。 其中大多数仅生成其他 HTML(包括我们的自定义 Web 组件)。示例如下:

{% Aside %}
See how Asides work in the web.dev codebase
{% endAside %}

最终结果将如下所示:

但它实际上会创建如下所示的 HTML:

<div class="aside color-state-info-text">
<p>See how Asides work in the web.dev codebase</p>
</div>

虽然不在本文的讨论范围内,但 web.dev 还将短代码用作一种元编程语言。短代码接受实参,其中一个实参就是包含的内容。短代码不需要返回任何内容,因此可用于构建状态或触发其他行为。🤔?💭?

设计脚本

如前所述,由于 web.dev 是一个静态网站,因此无需 JavaScript 即可提供和使用,并且不支持 type="module" 或我们的其他现代代码的旧版浏览器也可以访问。这是我们让所有人都能访问 web.dev 的方法中非常重要的一部分。

不过,我们面向现代浏览器的代码由两个主要部分组成:

  1. 引导加载程序代码,其中包含全局状态、Google Analytics 和 SPA 路由的代码
  2. 用于逐步增强网站的 Web 组件的代码和 CSS

引导代码非常简单:web.dev 可以将新页面作为单页应用 (SPA) 加载,因此我们会安装一个全局监听器,用于监听对本地 <a href="..."> 元素的点击。SPA 模型可帮助我们维护用户当前会话的全局状态,否则每次新页面加载都会触发 Firebase 调用来访问用户的登录状态。

我们还根据您点击的网址指定了几个不同的进入网站入口点,并使用动态 import() 加载正确的入口点。 这样可以减少用户在网站通过代码进行增强之前需要的字节数。

Web 组件

Web 组件是自定义元素,用于封装 JavaScript 中提供的运行时功能,由自定义名称(如 <web-codelab>)标识。这种设计非常适合大部分静态网站(例如 web.dev):当网站的 HTML 更新时,浏览器会管理元素的生命周期,并在元素附加到页面或从页面分离时正确通知这些元素。 而旧版浏览器则会完全忽略 Web 组件,并呈现 DOM 中剩余的所有内容。

每个 Web 组件都是一个类,其中包含 connectedCallback()disconnectedCallback()attributeChangedCallback() 等方法。web.dev 的自定义元素大多继承自 LitElement,后者为复杂组件提供了简单的基础。

虽然 web.dev 在许多页面上都使用 Web 组件,但最需要使用 Web 组件的还是衡量页面。您在此页面上看到的大部分功能由以下两个元素提供:

<web-url-chooser-container></web-url-chooser-container>
<web-lighthouse-scores-container></web-lighthouse-scores-container>

这些元素可创建其他元素,从而提供更多功能。请务必注意,这些元素只是常规 Markdown 源代码的一部分,我们的内容团队可以为任何页面(而不仅仅是“衡量”节点)添加扩展功能。

我们的 Web 组件最常使用由 React 推广的容器组件模型,不过这种模型现在已经过时。每个 -container 元素都会连接到我们的全局状态(由 unistore 提供),然后渲染视觉元素,后者反过来会渲染具有样式或其他内置功能的实际 DOM 节点。

显示全局状态与使用它的 HTML 元素之间的关系的图表。
全局状态与 Web 组件

我们最复杂的 Web 组件旨在直观呈现全局操作和状态。 例如,借助 web.dev,您可以审核自己喜爱的网站,然后离开“Measure”(测量)页面。 如果您返回,会发现任务仍在进行中。

我们的简单组件仅用于增强原本静态的内容或创建出色的可视化效果(例如,每个折线图都是自己的 <web-sparkline-chart>),与全局状态没有任何关系。

让我们来聊聊

web.dev 工程团队(RobEwaMichaelSam)很快就会推出更多技术深度解析。

希望了解我们的工作方式能为您自己的项目提供一些灵感。 如果您对本博客有任何疑问或主题建议,欢迎通过 Twitter 与我们联系!