Drupal网站前端性能优化:第1部分(Drupal缓存)

作者:成都长风云Drupal开发

Drupal网站前端性能优化:第1部分

在过去的一段时间里,我们(成都长风云Drupal开发团队)对运维的部分网站在配置、代码和主题化方面的提供了明显的性能优化。

我们总结一些Drupal网站优化的基本理念,在这些理念的基础上,我们运用一些技巧,对Drupal网站的性能进行优化:

1、如果没有必要,请不要启动不必要的服务。

2、是Drupal网站访客获得最佳体验所需的最小、最优化的媒体资源,这一点从Drupal8开始变得容易了。

3、只传输Drupal网站访客当时需要的东西。

当您必须包含一些复杂的内容时,请先将其排除在传递最重要内容的方式之外

我将讨论本文的第一个项目。其余部分将在未来几周内公布。

在优化了大量网站上的体验后,我们可以预见地发现,使用这个列表从上到下的性能,会产生可以预见的惊人结果。

一、如果没有必要,请不要启动服务

我们在这里讨论的是静态页面缓存(或一般的缓存)。互联网上有很多关于缓存的文章,尤其是在Drupal中,但最重要的是,动态网站总是比静态网站慢,而且在相同的服务器资源下,提供大量流量的能力也要差得多。

对于非交互式网络体验(向读者提供纯内容,而不是请求用户输入并更新网络内容以适应),您应该尽可能多地提供完全静态呈现的页面。

常见问题:

像Honeypot和CAPTCHA这样的机器人对抗模块的配置方式可以破坏任何使用它们的页面的网站缓存。

个性化内容如果做得不正确(例如使用会话风格的cookie),可能会破坏访问者的缓存。

随机化的内容可能会破坏缓存(取决于配置)。

您可以使用开发工具的“网络”选项卡在Chrome中确认内容的可缓存性。打开它们(在Mac上为cmd+opt+i,然后选择网络选项卡)并重新加载页面。左边的第一个结果应该是实际的页面本身,您需要在“headers”部分查看以下内容:

Drupal网站的前端性能优化

“Cache-Control”标头应该不是“must-revalidate, no-cache, private”。理想情况下,如果所有配置都正确,则每次重新加载页面时,“年龄”标头都应增加。如果没有发生这种情况,您应该首先查看上面的gotchas列表,找出您的内容不可缓存的原因。

一种有用的故障排除技术是检查模块并禁用它们,一次一个,然后按照上面的步骤重新检查页面缓存标题。在非生产环境中这样做可以让你非常自由地进行实验,而不必担心破坏网站。

如果禁用了某个模块并且缓存开始工作,则可以将调查范围缩小到有问题的模块。当您发现某个模块导致问题时,可以采取大约三类操作:

调查配置。以Honeypot模块为例:“时间限制”设置会导致页面缓存中断。将此值设置为0应允许具有保护表单的页面保持缓存状态。

如果通过Drupal中的配置不可实现Drupal缓存,那么寻找替代模块可能是一个不错的选择。在Drupal模块的世界里,通常有多种选项可用于实现任何目标。成都长风云Drupal开发团队最近开始使用Antibot模块来实现我们以前依靠CAPTCHA和Honeypot模块来实现的相同功能(减少垃圾邮件)。这个模块不会破坏页面缓存,对访问者来说,总体上比captcha有更好的用户体验。

当模块配置和模块选择不是解决页面缓存问题的可行解决方案时,您可能正在考虑需要重构的自定义代码,或者寻找成都长风云Drupal开发以获得帮助。

{% progress-meter.twig %}
{{ attach_library('ce_quick_action/progress_meter') }}
<div class="action-meter__progress-meter" data-progressMeter data-eac-id="{{ eac_id }}" data-goal="{{ goal }}">
  <p class="action-meter__progress-bar">
    <span class="action-meter__submissions js-progress-bar"></span>
  </p>
</div>

以及随附的Javascript:

(function (once) {
  Drupal.behaviors.quickActionProgressMeter = {
    attach(context, settings) {
      const elements = once('quick-action-progress-meter', '[data-progressMeter]', context);
      elements
        .forEach(function (element) {
          const eacId = element.getAttribute('data-eac-id');
          const goal = element.getAttribute('data-goal');
          fetch(`/conversion_engine/api/eac/count/${eacId}`)
            .then(response => response.json())
            .then(data => {
              const count = data.count;
              const progress = Math.round((count / goal) * 100);
              element.querySelector('.action-meter__progress-bar').style.width = `${progress}%`;
              element.querySelector('.action-meter__submissions').dataset.count = count;
              // set the inner html to a comma formatted number using Intl.NumberFormat e.g 1,000,000
              element.querySelector('.action-meter__submissions').innerHTML = new Intl.NumberFormat().format(count);
            });
        });
    }
  };
}(once));

在这个Javascript中,您可以看到我们正在向自定义API端点发出请求,然后在页面加载后修改dom中的计数。这意味着无论表单提交发生了什么,页面本身都可以从缓存中提供服务。如果我们看一下该api端点后面的控制器,我们可以看到它也缓存响应,但它自己的时间表是可控的,可以通过管理员配置表单来调整反馈周期以获得即时性或性能:

<?php
namespace Drupal\ce_email_acquisition\Controller;
use Drupal\ce_email_acquisition\EmailAcquisitionInterface;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Cache\CacheableJsonResponse;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\ce_email_acquisition\EmailAcquisitionCounterInterface;
/**
 * Controller for the eac submission counter.
 */
final class EmailAcquisitionCounterController extends ControllerBase {
  /**
   * The eac counter service.
   *
   * @var \Drupal\ce_email_acquisition\EmailAcquisitionCounterInterface
   */
  private EmailAcquisitionCounterInterface $eacCounter;
  /**
   * The time service.
   *
   * @var \Drupal\Component\Datetime\TimeInterface
   */
  protected $time;
  /**
   * A controller to fetch the quick action submission count of a challenge.
   *
   * @param \Drupal\ce_email_acquisition\EmailAcquisitionCounterInterface $eacCounter
   *   The signature_counter service.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   The config factory.
   * @param \Drupal\Component\Datetime\TimeInterface $time
   *   The time stuff.
   */
  public function __construct(
    EmailAcquisitionCounterInterface $eacCounter,
    ConfigFactoryInterface $configFactory,
    TimeInterface $time
  ) {
    $this->eacCounter = $eacCounter;
    $this->configFactory = $configFactory;
    $this->time = $time;
  }
  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new self(
      $container->get('ce_email_acquisition.eac_counter'),
      $container->get('config.factory'),
      $container->get('datetime.time')
    );
  }
  /**
   * Callback for the quick action submission counter.
   *
   * @param \Drupal\ce_email_acquisition\EmailAcquisitionInterface $challenge
   *   The challenge entity.
   */
  public function __invoke(EmailAcquisitionInterface $challenge) {
    $count = $this->eacCounter
                  ->getCountForChallenge(
                    $challenge->field_email_acquisition_id->value
                  );
    $data = [
      'count' => $count,
    ];
    $response = new CacheableJsonResponse($data);
    $response->setMaxAge($this->getCachedMaxAge());
    // Do this because
    // https://drupal.stackexchange.com/questions/255579/unable-to-set-cache-max-age-on-resourceresponse
    $cache_meta_data = new CacheableMetadata();
    $cache_meta_data->setCacheMaxAge($this->getCachedMaxAge());
    $response->addCacheableDependency($cache_meta_data);
    // Set expires header for Drupal's cache.
    $date = new \DateTime($this->getCachedMaxAge() . 'sec');
    $response->setExpires($date);
    // Allow downstream proxies to cache (e.g. Varnish).
    $response->setPublic();
    // If the challenge changes, we want to invalidate the cache.
    $response->addCacheableDependency(CacheableMetadata::createFromObject($challenge));
    return $response;
  }
  /**
   * Get the cache max age.
   *
   * @return int
   *   The cache max age in seconds, defaulting to 300 (5 minutes).
   */
  private function getCachedMaxAge() : int {
    return $this->configFactory
                ->getEditable('ce_cp.controlpanel')
                ->get('eac_count_cache_max_age') ?? 300;
  }
}

这种方法使我们能够微调这个流量很大的页面的缓存行为,以找到性能和参与度之间的最佳点,从而使客户能够提供尽可能大的活动,同时通过帮助访问者更即时地看到其影响来提高参与度。

这是一个平衡自定义站点功能的例子,该功能的初始规范是抗缓存的,但通过仔细使用现有技术,我们可以通过显著提高可扩展性和性能来提供大部分相同的好处。

六、专业的Drupal开发团队 - 成都长风云Drupal开发团队

在2024年,我们已经专注于Drupal开发16年(始于2008年)。无论您计划从Drupal7升级到Drupal10、从旧版本迁移到Drupal9、基于Drupal开发新的系统、企业官网、电商网站,维护基于Drupal开发的系统等,我们都能依靠我们的专业技术为您完成。免费咨询Drupal升级方案,手机号:13795726015 或 微信号:changfengqj 扫微信二维码:

联系我们

提供基于Drupal的门户网站、电子商务网站、移动应用开发及托管服务

联系电话
137-9572-6015
长按加微信
长风云微信
长按关注公众号
长风云公众号