推迟javascript并在初始页面加载时显示中间内容

如何:延迟 Discourse 的 JavaScript

尽可能为所有 JavaScript 添加 defer 属性。延迟 JavaScript 加载和执行可让浏览器开始 HTML 解析、渲染和绘制。

因此,一些静态的中间内容可能会在 Discourse 启动过程的早期(甚至之前)显示。这应该有助于在首次加载页面时提高用户感知的页面加载速度。

静态中间内容的想法:

  • 带徽标和加载旋转器的启动屏幕
  • 来自后端的帖子的主题视图

POC 和 PR

有关最新的概念验证和 PR - 请查看此帖子


目前,供应商 JavaScript 和所有之前的 JavaScript 都未延迟加载。
@参见https://github.com/rr-it/discourse/commit/328efd5c055f5f2a4d93b5e52268cfe92913faf7

非常欢迎有关如何解决此问题的想法。


JavaScript async vs. defer vs. none

有关 JavaScript 加载选项的更多信息 - 包括 deferEfficiently load JavaScript with defer and async
(这与加快 Discourse 的实际启动速度无关。)


Fastboot/rehydration

我读了这篇文章:
那里的结论似乎是实现了 Fastboot/rehydration。
这有时间表吗?

4 个赞

这会导致 LCP 在 EmberJS 启动和重新渲染后仍然存在,未能解决有关 Google 新排名的主要问题。

这是我们目前为解决 Discourse 中的 LCP 而制定的中期计划。

2 个赞

从 Chrome 88 开始,幸运的是这种情况不再发生了! :rocket:
直到现在我也不知道这个。 :))

“在此更改之前,被移除的元素将不再被视为有效的 LCP 候选。……在此更改之后,被移除的元素仍然被视为有效的 LCP 候选。”

“包含稍后从 DOM 中移除的内容作为最大的内容绘制候选,将提高那些具有 图像 [对于 Discourse: 文本元素] 相同尺寸被多次插入的网站的最大内容绘制时间。这是轮播的常见模式,也是一些执行服务器端渲染的 JavaScript 框架的常见模式。”


LCP 更新日志

未来可能还会有更多好的变化:

1 个赞

以下是已实施 POC 的主题页面的模拟统计数据。

Lighthouse:“数值为估算值,可能会有差异。”

WebPageTest

webpagetest.org

Moto4G 模拟


注意:我们是顶部的黑色箭头。


说明:

  • 0 秒 - 2 秒 - 黑屏:
    WebPageTest 会忽略 JavaScript 的 defer 属性,并在首次绘制前下载所有 JavaScript - 这在真实设备上运行正常。
  • 2.5 秒 - LCP:来自服务器渲染的静态内容
  • 3.5 秒 - 可视化更改:加载了徽标
  • 6.5 秒 - 可视化更改:来自 EmberJs 渲染的内容
  • 7 秒 - 可视化完成

PageSpeed Insights

最大的内容绘制元素

PageSpeed 正确地将来自服务器渲染的静态文本节点识别为 FCP LCP 元素:
div.row > div.topic-body > div.post > p

EmberJs 渲染的文本节点:
div.row > div.topic-body > div.regular.contents > div.cooked > p

但 PageSpeed 似乎没有使用正确识别的静态文本节点来得出其模拟结果:模拟的 FCP 和 LCP 太大了。

真实用户数据

让我们再等待 14-28 天,以从 Chrome 用户体验报告 中获取已实施 POC 的“真实”数据。

未实施 POC 的测试主题页面的统计数据:
(数据来自此单个主题 URL - 而不是整个来源。)

4 个赞

太棒了!这是一个非常有趣的发现!干得好!

您在此扩展程序上得到了什么 https://chrome.google.com/webstore/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma?

3 个赞

通过 Web Vitals Chrome 扩展程序

  • 在桌面设备上
  • Chromium 版本 90.0.4430.212
  • 在新的无痕窗口中首次加载


关于首次输入延迟的说明:我等到页面完全加载完毕,然后点击了背景——也就是在 EmberJs 渲染完成后。


关于首次输入延迟的说明:在这里,我一看到静态内容就立即点击了背景。请将我的反应时间 :sloth: 加到这个 FID 上。

关于这些图表下方百分位数的附加说明:
百分位数不太相关,因为它们只将测量值与原始值进行比较。原始值是一个 TYPO3 网页,其中 Discourse 是子文件夹安装的。

2 个赞

很棒的主意!@rrit

我完全同意 Discourse 是一个非常缓慢、JS 密集型的 Web 应用程序,如果我们能推迟加载 CSS/JS 文件,将极大地帮助提高 LCP、FCP、FID、CLS 的速度。

我们以及许多其他人正面临此问题,因此非常希望看到此功能上线。所有 Discourse 站点在核心 Web 指标方面都表现不佳。如果我们首次向用户提供快速的静态 HTML 页面,或者在首次初始加载时推迟所有 JS/CSS 逻辑,这样我们就可以加快所有页面的速度并通过 CWV 分数!很高兴看到此功能在核心 Discourse 更新中上线。

由于网站未能通过核心 Web 指标的测试,所有 Discourse 网站的 Google 排名都在下降。

2 个赞

我们愿意在核心中尝试这一点。“闪烁”不同样式的内容可能会有点令人不安,因此我们希望从一个默认禁用的“实验性”站点设置开始。这样,站点管理员就可以选择启用它。

您能否在您的 PR @rrit 中尝试添加一个站点设置?添加一些 RSpec 测试以验证启用/禁用该设置的行为也将是很好的。

5 个赞

这是否意味着我们可以简单地在服务器渲染的页面上放置一个全屏加载指示器(宽度和高度均为 100%),并在 Ember 应用最终启动时将其替换,从而获得极低的 LCP?

我们可以将此加载指示器制作为模仿 Discourse UI 的 SVG,从而使过渡更平滑,减少 FOUC(闪烁)。

2 个赞

我认为关键部分是 LCP “候选

只有当它确实是最终渲染内容的最大的(或至少是相同大小的)内容时,它才会被视为最大的内容绘制?

那么使用爬虫视图效果很好,因为内容(即文本)在很大程度上是相同的?

(我主要是根据上面的截图猜测的——我还没有尝试过全屏加载器)

1 个赞

功能标志已实现。

我完全不是 Ruby 开发者——在这方面我肯定需要一些帮助。

也许我应该将我的 POC 推送到 discourse/discourse 仓库的一个新分支,然后再向 main 发送 PR?

这是我关于此功能的 PR:

@david 你能帮我开发这些更改的 Rspec 测试吗:

app/helpers/application_helper.rbspec/helpers/application_helper_spec.rb

我在这里看不到可行的单元测试。它似乎只能通过集成测试进行测试。
app/models/theme.rb
app/models/theme_field.rb

我不得不禁用 QUnit 测试运行器的 defer 标签:app/views/qunit/index.html.erb
之前 QUnit 测试在功能标志 "javascript defer" = false 时仍然运行。现在,即使在 "javascript defer" = true 的情况下,测试也能运行。

2 个赞

这可能已经被 Chrome Speed - Largest Contentful Paint Bug Fixes in M88 阻止了:

视口全图,在视觉上等同于背景图,不再被视为最大的内容绘制。


说得好:请参阅 Largest Contentful Paint (LCP)  |  Articles  |  web.dev

对于文本元素,只考虑其文本节点的大小(包围所有文本节点的最小矩形)。

对于所有元素,CSS 应用的任何外边距、内边距或边框均不予考虑。

  • 这就是为什么静态文本节点必须与 EmberJs 文本节点的大小完全相同。
  • 甚至可以通过增加 line-height 来使其稍大一些。
    例如,如果文本节点的宽度不匹配,由于不同的换行符会引入许多几何情况,导致静态文本节点比 EmberJs 的小。

请参阅:LCP 示例


我实际上使用了主题页面内帖子的 noscript 渲染。CSS 类与实际的类略有匹配——因此外观是相同的。

请参阅:app/views/layouts/application.html.erb 的更改

编辑:我的错,这实际上是爬虫视图:app/views/topics/show.html.erb

2 个赞

在 POC 中,有两个功能合并在一起——我们是否应将它们拆分为两个实验性功能标志?

  • 带有 defer 标签的 JavaScript(设置仪表板中的功能标志)
    (隐藏功能标志,因为这需要容器重建或主题缓存刷新)修复:带缓存的热切换
  • 在主题视图中显示静态内容(设置仪表板中的功能标志)

来了:功能标志


当然,对 LCP 的全部影响是通过同时使用两者来实现的:“FCP:静态内容”

可能有一些 Discourse 实例,其插件或主题组件在延迟 JavaScript 时会失败。通过拆分这些功能,它们可以在不延迟 JavaScript 的情况下获得静态内容的微小优势:“FCP:无 JavaScript 延迟的静态内容”

2 个赞

自 2022-01-30 应用 POC 后,Google Search Console 的初步印象:

桌面

桌面结果需要一些时间才能显示:


注意:旧的绿色基线代表同一域上的非 Discourse 网页。

移动设备


注意:旧的绿色基线代表同一域上的非 Discourse 网页。

让我们再等待 7-14 天,希望移动页面的改进能更多,因为这些值是过去 28 天的平均值——目前应用 POC 只计算了 12 天。

5 个赞

LCP 概念验证总结

概念验证自 2022 年 1 月 30 日起开始应用,经过 4 周多的时间才在 Google Search Console 的“核心网页指标”报告中(基于 CrUX 数据)影响到所有页面。

所有主题页面的 LCP 均处于绿色区域(由 CrUX 测量):

  • 桌面:LCP 1.7 秒
  • 移动设备:LCP 2.0 秒

LCP 数据:Google Search Console/CrUX

自 2022 年 1 月 30 日应用概念验证以来,Google Search Console 的展示情况:

桌面


注意:旧的绿色基线代表同一域上的非 Discourse 网页。

优质网址

移动设备


注意:旧的绿色基线代表同一域上的非 Discourse 网页。

优质网址

LCP 问题:超过 2.5 秒(移动设备)


注意:仅主题页面在 EmberJS 内容之前显示静态内容。


需要批准带有功能标志的 PR

@sam 您能否委托某人查看此 PR 并批准,谢谢。

3 个赞

我们一定会仔细审查,这是一个很大的改动,可能需要一些时间才能处理。

5 个赞

@rrit 感谢分享您网站的数据!我们内部已经讨论过,恐怕目前不会在 Discourse 核心中添加此功能。

虽然您分享的 Web Vital 指标非常出色,但“爬虫视图”内容的闪现会影响用户体验。您所做的样式更改确实有所帮助,但需要为每个具有自定义样式的 Discourse 网站进行调整。

我们的长期目标是使用类似 Ember FastBoot 的技术实现真正的服务器端渲染。理论上,这将提供与您测量的相同的统计改进,同时提供无缝的用户体验。我们希望将精力集中在这一目标上。


话虽如此,Discourse 的可扩展性非常强,我认为完全有可能在 Discourse 插件中实现您的想法,然后在 Plugin 中分享。

您在核心 PR 中最大的改动是在脚本标签中添加了 defer 属性。从插件中覆盖所有这些位置将非常困难。但是,我认为可以通过基于中间件的方法获得相同的结果。我找到了这篇博文,其中描述了一个类似的问题:

使用这种技术,您可以编写一个中间件,它检查 text/html 响应,对其进行解析,然后在必要时添加 defer 属性。

可以通过类似以下方式从插件添加中间件:

# name: my-plugin
# about: My plugin description
# version: 1.0
# url: https://example.org

require_relative "lib/script_defer_middleware"

on(:after_initializers) do
  Rails.configuration.middleware.use(ScriptDeferMiddleware)
end

如果您在基于插件的方法中遇到任何障碍,请随时在此处发帖,我们将很乐意为您指明方向。

10 个赞

如果我能抽出时间来做这件事,我可能会实现一个插件。

但现在,我尝试在 web_only.yml 中使用一种补丁方法来解决:

# 未经测试的伪代码!
hooks:
  after_code:
    - exec:
        cd: $home
        cmd:
          - curl https://patch-diff.githubusercontent.com/raw/discourse/discourse/pull/15858.diff | git apply
4 个赞

Ember FastBoot 看起来是一个完美的长期解决方案。与此同时,LCP 的话题依然热门:

2 个赞

感谢您为此付出的努力,@rrit :+1:

我有一个好消息,我们在 Discourse 中实现了一项新功能,这应该会有很大帮助。

5 个赞