延迟加载 Disqus 评论

Avatar of Osvaldas Valutis
Osvaldas Valutis

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

最近,我一直痴迷于通过延迟加载优化性能。最近,我写了关于如何 延迟加载 Google 地图 以及如何 延迟加载响应式 Google Adsense 的文章。现在该轮到 Disqus 了,这是一项在您的网站上嵌入评论的服务。这是一项很棒的服务。它消除了开发您自己的本地评论系统、处理垃圾邮件等的麻烦。最近,我一直在一个项目中实施这个小部件。

问题

从布局上看,评论通常扮演次要角色。在许多情况下,访客根本看不到评论,因为他们不会滚动到评论所在的位置。但猜猜看?默认情况下,无论如何它们都会加载到网站上。浪费的带宽是一个问题。

看看 Disqus 官方推荐的技术实现

<div id="disqus_thread"></div>
<script>
  (function() {
    var d = document, s = d.createElement('script');
    s.src = '//username.disqus.com/embed.js';
    s.setAttribute('data-timestamp', +new Date());
    (d.head || d.body).appendChild(s);
  })();
</script>

他们这么说:“将以下代码放在您希望 Disqus 加载的位置”。假设您是一位优秀的开发者,并且通常在结束标签 </body> 之前插入所有 <script src="..."></script> 片段。然后,有一天,您决定在您的网站上实现 Disqus 评论,并将上述代码放在文档中间的评论部分。

会发生什么?开始下载的第一个 JavaScript 文件是 username.disqus.com/embed.js。这并不一定意味着它将首先下载,但它是浏览器关注的 JavaScript 文件列表中的第一个。难道不应该将第一个文件保留给您网站的主要 JavaScript 文件吗?当您的主要 JavaScript 文件加载延迟时,可能会出现许多问题(例如“休眠”的 <button> 等),尤其是在您开发该网站时没有遵循优雅降级渐进增强原则的情况下。

这还会干扰您网站上的其他外部资源,例如图像和 CSS 文件。想象一下,您在 2G 网络条件下使用智能手机,并且正在等待评论小部件加载,因为您是为了看小猫的照片而来的。

我做了一个测试。事实证明,带有零条评论的 Disqus 小部件重达2.49 MB!一堆 JavaScript、CSS、图像和字体文件的网络请求,在许多情况下不合理地减慢了为您网站的其他可能至关重要的部分或功能提供服务的效率。

Disqus 评论:无延迟加载

解决方案:小型 JavaScript 插件

为了能够延迟加载 Disqus,我开发了一个小型 JavaScript 插件来完成这项工作。无论评论区域位于视口的上方还是下方,如果没有必要,它都不会加载。

Disqus 评论:延迟加载

插件本身是一小段 JavaScript 代码。我制作了两个版本:原生版本和 jQuery 版本。我称之为disqusLoader。您可以在这里获取这些文件

以下是设置方法。首先,您需要将一个元素插入到 HTML 中,您希望评论部分位于该元素中

<div class="disqus"></div>

然后,像这样初始化插件

// vanilla
disqusLoader( '.disqus', { scriptUrl: '//username.disqus.com/embed.js' });

// jQuery
$.disqusLoader( '.disqus', { scriptUrl: '//username.disqus.com/embed.js' });

“太好了,但是 Disqus 特定的配置怎么办?”您可能会问。当然,还有一个可用的参数可以接受 Disqus 原生值。还有一些其他与插件相关的选项

var options =
{
  scriptUrl: '//username.disqus.com/embed.js',
  /*
    @type: string (url)
    @default: none
    @required
    URL of Disqus' executive JS file. The value is memorized on the first function call
    and ignored otherwise because Disqus allows only one instance per page at the time.
  */

  laziness: 1,
  /*
    @type: int (>=0)
    @default: 1
    Sets the laziness of loading the widget: (viewport height) * laziness . For example:
    0 - widget load starts when at the least a tiny part of it gets in the viewport;
    1 - widget load starts when the distance between the widget zone and the viewport is no more than the height of the viewport;
    2 - 2x viewports, etc.
  */

  throttle: 250,
  /*
    @type: int (milliseconds)
    @default: 250
    Defines how often the plugin should make calculations during the
    processes such as resize of a browser's window or viewport scroll.
    250 = 4 times in a second.
  */

  /*
    @type: function
    @default: none
    Disqus-native options. Check Disqus' manual for more information.
  */
  disqusConfig: function()
  {
    this.page.title       = 'Page Title';
    this.page.url         = 'http://url.to/your-website';
    this.page.identifier  = 'unique-identifier';
  }
};

// vanilla
disqusLoader( '.disqus', options );

// jQuery
$.disqusLoader( '.disqus', options );

自己查看一下

查看演示

您也可以在 GitHub 上参与贡献或关注该项目。

Disqus 回调函数

回调函数很棒,因为您可以对用户的操作做出反应。Disqus 官方文档中只有一种回调函数。查看 `embed.js` 文件的源代码,您可以看到更多预定义类型的回调函数

Disqus 评论:embed.js 源代码中的回调函数列表

但是,看起来只有两个启用了:onNewCommentonReady。这足以实现微小但明显的改进:加载指示器。

补充加载指示

加载 Disqus 小部件通常包括两个部分

  1. 加载 `embed.js` 文件
  2. 加载内部资源并执行其他类型的网络请求

Disqus 本身负责第二步,他们使用动画图像来指示这一点。但是第一步呢?加载外部 JavaScript 文件可能需要数十秒,原因有很多。这里的问题是,无论网络状况如何,用户都会被告知您的网站上有一个评论功能。用户体验在于细节!

Disqus 评论:补充加载指示

技术方法很简单:一个新的 HTML 元素和一个 JavaScript 回调函数,用于帮助隐藏该元素

<div class="disqus-placeholder">Loading comments...</div>
<div class="disqus"></div>
// vanilla
disqusConfig: function()
{
  this.callbacks.onReady = [function()
  {
    var el = document.querySelector( '.disqus-placeholder' );
    if( el.classList )
      el.classList.add( 'is-hidden' ); // IE 10+
    else
      el.className += ' ' + 'is-hidden'; // IE 8-9
  }];
}

// jQuery
disqusConfig: function()
{
  this.callbacks.onReady = [function()
  {
    $( '.disqus-placeholder' ).addClass( 'is-hidden' );
  }];
}
.disqus-placeholder.is-hidden { display: none; }

您可以在演示页面上 查看其运行效果。但首先,清除浏览器的缓存并限制网络速度。

一次加载多个实例或站点?

在研究这项技术时,我发现了一些今天存在的重要的限制……

在一个页面上无法使用多个脚本源

一旦脚本文件(例如 //username.disqus.com/embed.js)加载,就会创建全局变量 window.DISQUS,但前提是它之前没有设置过(这是一个不好的迹象,但让我们深入了解一下)。所以我做了一个测试。我从源脚本 A 初始化了小部件。然后为将来的变量 window.DISQUS = undefined 释放了一些空间,并初始化了源 B 的小部件。但是,结果一团糟:回调函数被多次触发,评论被复制等。显然,Disqus 的当前代码库并非旨在支持多个变量并与每个小部件实例单独操作。

在一个页面上无法同时使用多个小部件

DISUQS 对象中有一个可用的公共 JavaScript 方法函数 reset() 可用。如果您有任何关于 Disqus 的技术经验,您可能知道小部件被插入到一个元素中,该元素的 id 值为 disqus_thread。我用两个元素做了一个测试:在第一个元素中加载小部件,删除 ID 属性,并将其附加到第二个元素。最后,我调用了 reset 函数,期望第二个实例出现在第一个实例旁边。但是,调用该函数来加载新实例也会破坏任何之前初始化的小部件。不幸的是,今天 Disqus 仅设计为在给定时间内使用单个实例。

可以实时重新加载小部件

有一个好消息!即使无法同时拥有多个小部件实例,您仍然可以销毁旧的实例并加载新的实例。让我们用最典型的情况——**选项卡**——来将这个理论付诸实践。您需要做的就是在每次激活新选项卡时调用插件。

<div class="tabcontent" data-disqus-id="venus" data-disqus-title="Venus"></div>
<div class="tabcontent" data-disqus-id="earth" data-disqus-title="Earth"></div>
<div class="tabcontent" data-disqus-id="mars" data-disqus-title="Mars"></div>
// call this function every time a tab is clicked:

var initDisqus = function( content )
{
  disqusLoader( content,
  {
    scriptUrl:    '//username.disqus.com/embed.js',
    disqusConfig: function()
    {
      this.page.identifier  = content.getAttribute( 'data-disqus-id' );
      this.page.title     = content.getAttribute( 'data-disqus-title' );
    }
  });
}

您可以查看实际效果或在演示页面上查看完整代码。

结束语

这篇文章并不是关于Disqus的缺陷。而是关于我们开发者犯的错误。我们的世界充满了工具,如何使用它们取决于我们自己。尽管这些工具解决了特定问题,但如果我们在实施时没有采取适当的措施,它们通常还会带来一些其他问题。每次选择最简单的路径都会导致用户流失、转化率下降和跳出率上升。您已经可以延迟加载Disqus、Google AdsenseGoogle Maps社交媒体按钮,您还可以开发和分享自定义技术。请负责任!