madj2k/t3-accelerator

加速您的TYPO3安装:将关键CSS(视口以上)内联,压缩您的网站HTML,使用子域作为CDN以减少页面加载时间,通过页面属性管理代理缓存(例如使用Varnish),在将持久化对象存储到数据库时减少数据库大小

安装: 151

依赖项: 13

建议者: 0

安全: 0

星标: 1

关注者: 3

分支: 0

开放问题: 0

类型:typo3-cms-extension


README

特性

加速您的TYPO3安装

  • 内联关键CSS(视口以上)*
  • 压缩您的网站HTML
  • 使用子域作为您的静态内容(图像、文件等)的CDN以加快网站加载时间
  • 通过页面属性管理代理缓存(例如使用Varnish)
  • 在将持久化对象存储到数据库时减少JSON数组的大小。

1. HTML压缩器

1.1 描述

此功能从HTML代码中删除不必要的换行和空格。这显著减少了HTML代码的大小。

1.2 设置

重要提示:由于TYPO3 v10的更新,配置不再通过TypoScript进行,因为它现在作为中间件实现。现在可以通过您的站点配置(YAML)进行配置。

accelerator:
  htmlMinifier:
    enable: true
    excludePids: ''
    includePageTypes: '0'
  variants:
    -
      htmlMinifier:
        enable: false
      condition: 'applicationContext == "Development/Local"'
  • 启用激活HTML压缩
  • 排除PID排除此逗号分隔列表中定义的PID
  • 包含页面类型包含此逗号分隔列表中定义的页面类型

请注意:仅变体与启用属性一起工作

对于默认设置,只需设置

accelerator:
  htmlMinifier:
    enable: true

2. 伪CDN

2.1 描述

通过CDN功能,可以从网站的子域中加载静态内容,从而显着减少网站加载时间。这不是真正的CDN,而是一个“伪CDN”,因为它不使用外部服务器。它使用给定域的子域来提供静态内容,因此需要

  1. 相应的DNS配置
  2. 一个通配符TLS证书才能正常工作。如何做到这一点不属于本文档内容 - 但您可以随时问我 :-)

无伪CDN的示例

<picture >
    <source srcset="https://www.example.de/fileadmin/_processed_/e/e/csm_20191112-Unternehmensberatung-Desktop_20772b022d.jpg" media="(min-width: 1025px)">
    <source srcset="https://www.example.de/fileadmin/_processed_/a/2/csm_20191112-Unternehmensberatung-Tablet_e748abd11d.jpg" media="(min-width:769px)">
    <source srcset="https://www.example.de/fileadmin/_processed_/a/2/csm_20191112-Unternehmensberatung-Tablet_a7c14e847a.jpg" media="(min-width:481px)">
    <source srcset="https://www.rkwde/fileadmin/_processed_/4/9/csm_20191112-Unternehmensberatung-Mobile_3c8697c74b.jpg" media="(min-width:321px)">
    <source srcset="https://www.example.de/fileadmin/_processed_/4/9/csm_20191112-Unternehmensberatung-Mobile_f329b9da89.jpg" media="(min-width:0px)">
    <img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" alt="Ihre Unternehmensberatung ">
</picture>

有伪CDN的示例

<picture >
    <source srcset="https://static1.example.de/fileadmin/_processed_/e/e/csm_20191112-Unternehmensberatung-Desktop_20772b022d.jpg" media="(min-width: 1025px)">
    <source srcset="https://static1.example.de/fileadmin/_processed_/a/2/csm_20191112-Unternehmensberatung-Tablet_e748abd11d.jpg" media="(min-width:769px)">
    <source srcset="https://static1.example.de/fileadmin/_processed_/a/2/csm_20191112-Unternehmensberatung-Tablet_a7c14e847a.jpg" media="(min-width:481px)">
    <source srcset="https://static2.rkwde/fileadmin/_processed_/4/9/csm_20191112-Unternehmensberatung-Mobile_3c8697c74b.jpg" media="(min-width:321px)">
    <source srcset="https://static2.example.de/fileadmin/_processed_/4/9/csm_20191112-Unternehmensberatung-Mobile_f329b9da89.jpg" media="(min-width:0px)">
    <img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" alt="Ihre Unternehmensberatung ">
</picture>

2.2 设置

重要提示:由于TYPO3 v10的更新,配置不再通过TypoScript进行,因为它现在作为中间件实现。现在可以通过您的站点配置(YAML)进行配置。重要提示:DNS必须相应配置,并且必须在激活此功能之前安装通配符TLS证书

accelerator:
  pseudoCdn:
    enable: true
    maxConnectionsPerDomain: 4
    maxSubdomains: 100
    search: '/(href="|src="|srcset="|url\(\')\/?((uploads\/media|uploads\/pics|typo3temp\/compressor|typo3temp\/GB|typo3conf\/ext|fileadmin)([^"\']+))/i'
    ignoreIfContains: '/\.css|\.js|\.mp4|\.pdf|\?noCdn=1/'
  variants:
    -
      pseudoCdn:
        enable: false
      condition: 'applicationContext == "Development/Local"'
  • 启用激活伪CDN
  • 每个域的最大连接数定义从子域加载的资源数量。
  • 最大子域数定义应该有多少个子域。如果值设置为10,则使用子域static1.example.com到static10.example.com。
  • 搜索允许覆盖搜索/替换静态内容路径的正则表达式
  • 忽略包含允许指定伪CDN的排除标准。特别是JS文件应在此处排除(跨域问题)

请注意:仅变体与启用属性一起工作

对于默认设置,只需设置

accelerator:
  pseudoCdn:
    enable: true

3. 内联关键CSS(视口以上)

3.1 描述

为了提高您网站的加载速度,所谓的关键CSS(页面上方内容)可以存储在一个单独的文件中。然后,将这个关键CSS内联写入网站的HTML中,而其余的CSS(通过页面.includeCSS包含)以不会阻塞页面渲染的方式添加(这是通常的做法)。关键CSS可以针对每个前端布局进行指定。在这里,我们使用pages-table中的'backend_layout'和'backend_layout_next_level'字段。如果没有为布局指定关键CSS,CSS文件将按正常方式包含。

3.2 设置

重要提示:由于TYPO3 v10的更新,配置不再通过TypoScript进行,因为它现在作为中间件实现。现在可以通过您的站点配置(YAML)进行配置。

accelerator:
  criticalCss:
    enable: true
    layoutField: backend_layout
    layoutFieldNextLevel: backend_layout_next_level
    filesForLayout:
      home:
        -
          EXT:accelerator/Tests/Integration/ContentProcessing/CriticalCssTest/Fixtures/Frontend/Files/criticalOne.css
        -
          EXT:accelerator/Tests/Integration/ContentProcessing/CriticalCssTest/Fixtures/Frontend/Files/criticalTwo.css
    filesToRemoveWhenActive:
      -
        EXT:accelerator/Tests/Integration/ContentProcessing/CriticalCssTest/Fixtures/Frontend/Files/Global/removeOne.css
      -
        EXT:accelerator/Tests/Integration/ContentProcessing/CriticalCssTest/Fixtures/Frontend/Files/Global/removeTwo.css
  variants:
    -
      criticalCss:
        enable: false
      condition: 'applicationContext == "Development/Local"'
  • 启用激活关键CSS的包含
  • layoutField设置页面属性中的字段,用于确定当前页面的定义布局以包含正确的CSS文件。默认值是"backend_layout"。如果您不使用其他字段,则可以省略此设置。如果您使用自己的属性,请确保它被添加为rootline-field。
  • layoutFieldNextLevel设置页面属性中的字段,用于确定子页面的定义布局以包含正确的CSS文件。默认值是"backend_layout_next_level"。如果您不使用其他字段,则可以省略此设置。如果您使用自己的属性,请确保它被添加为rootline-field。
  • filesForLayout包含键和CSS文件,如果页面的布局与定义的键匹配,则包含这些文件。键是页面定义的layoutField-/layoutFieldNextLevel属性的值("pagets__"前缀被移除)。如果没有匹配,则不会包含任何文件。上面的例子会在将backendLayout属性设置为"pagets__home"的页面上包含criticalOne.css和criticalTwo.css文件。
  • filesToRemoveWhenActive定义了当关键CSS被激活并正在当前页面上工作时,将从page.includeCss中移除的文件

请注意:仅当使用enable属性时,这些变体才有效。

如果使用pageType 1715339215或GET参数no_critical_css=1,则禁用关键CSS。这有助于通过NPM critical等工具渲染关键CSS。

4. 代理缓存,例如使用Varnish

4.1 描述

此扩展允许使用代理缓存(例如Varnish)进行扩展设置。默认情况下,如果设置了前端cookie,页面将被排除在代理缓存之外。这是为了防止个人信息被缓存,从而对陌生人可见。

然而,这也意味着代理缓存对于登录的前端用户完全禁用,因此他们无法从代理缓存提供的整个页面的性能改进中受益。为了避免这种情况,此扩展在后台页面属性中提供了一个“允许代理缓存”字段。它有以下选项

  • 继承:从页面根路径继承设置
  • 禁用:完全禁用此页面的ProxyCache(及其子页面,如果适用)。此设置对于页面上的时间控制插件很有用
  • 激活:即使设置了前端cookie,也明确启用ProxyCache。这允许从Varnish缓存中提供页面,即使用户已登录。这应该仅用于不包含个人数据的页面。

字段的值会向下继承到页面树中,并导致添加HTTP标题

X-Typo3-Proxycaching: 1

除此之外,还添加了一个包含整个网站唯一标签(HMAC密钥)的第二个HTTP标题,另一个是针对当前页面的。它们可以用于基于标签清除代理缓存。

Xkey: 3ade06b8b96caba9c1717382f6dff9c7f049295e 2cbded6f9c51a25bc9f41b76e6834ea173066908

此设置仅在代理缓存配置中进行了适当设置的情况下才有效。由于代理缓存配置非常个性化,这里只列出了根据上述规范控制代理缓存行为的有关行。以下配置示例假设使用了madj2k/t3-acceleratoropsone-ch/varnish

4.1 与Varnish代理缓存一起使用的示例配置

#
# Varnish file by Steffen Kroggel (developer@steffenkroggel.de)
# Version 1.0.5
# Date 2020/11/05
#

# Marker to tell the VCL compiler that this VCL has been adapted to the
# new 4.0 format.
vcl 4.0;
import std;
import xkey;
[...]

#========================================================
# Sub-routine when request is received
#========================================================
sub vcl_recv {
    # Happens before we check if we have this in cache already.
    #
    # Typically you clean up the request here, removing cookies you don't need,
    # rewriting the request, etc.

    [...]


    # Set X-Forwarded-For Header
    if (req.restarts == 0) {

        if (req.http.X-Forwarded-For) {
            set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
        } else {
            set req.http.X-Forwarded-For = client.ip;
        }
    }

    # Catch BAN Command for TYPO3 extension "Varnish"
    # This bans specific cache objects from cache
    if (
        (req.method == "BAN")
        || (req.method == "PURGE")
    ) {

        # Check if  IP is allowed to BAN/Purge
        if (req.http.X-Forwarded-For ~ "^127.0.0.0") {
            return(synth(405,"Not allowed. IP: " + req.http.X-Forwarded-For));
            #===
        }

        # Check if one single page of an instance is to be invalidated
        if (req.http.Varnish-Ban-TYPO3-Pid && req.http.Varnish-Ban-TYPO3-Sitename) {
            set req.http.n-gone = xkey.softpurge(req.http.Varnish-Ban-TYPO3-Sitename + "_" + req.http.Varnish-Ban-TYPO3-Pid);
            return (synth(200, "Softpurge. Invalidated " + req.http.n-gone + " objects with " + req.http.Varnish-Ban-TYPO3-Sitename + "_" + req.http.Varnish-Ban-TYPO3-Pid));
            #====

        # Check if all pages of an instance are to be invalidated
        } else if (req.http.Varnish-Ban-TYPO3-Sitename) {
            set req.http.n-gone = xkey.softpurge(req.http.Varnish-Ban-TYPO3-Sitename);
            return (synth(200, "Softpurge. Invalidated " + req.http.n-gone + " objects with " + req.http.Varnish-Ban-TYPO3-Sitename));
            #===

        # Fallback with minimum impact
        } else {
            ban("req.http.host == " + req.http.host + " && req.url == " + req.url);
            return(synth(200,"Ban. Banned " + req.http.host + req.url));
            #===
        }
    }


    [...]

    # Do not cache authorized content (login via htaccess)
    if (req.http.Authorization) {
        return (pass);
        #===
    }

    # Force lookup if the request is a no-cache request from the client (STRG + F5)
    if (req.http.Cache-Control ~ "no-cache") {
        return (pass);
        #===
    }

    # Do not cache image files, pdfs, xls, docs, zips, etc. This fills up the cache to fast
    # and it keeps WebP-optimization on apache side from working
    if (req.url ~ "(?i)\.(jpeg|jpg|png|gif|ico|webp|txt|pdf|gz|zip|doc|docx|ppt|pptx|xls|xlsx)$") {
        return (pass);
	    #===
    }

    # Do not cache TYPO3 BE User requests
    if (req.http.Cookie ~ "be_typo_user" || req.url ~ "^/typo3/") {
        return (pass);
        #===
    }

    # Do not cache non-cached pages or specific page types and params
    # We also ignore some RealUrl-coded params from extensions
    if (
        (req.url ~ "^/nc/?")
        || (req.url ~ "$/gitpull.php")
        || (req.url ~ "(\?|&)type=")
        || (req.url ~ "(\?|&)typeNum=")
        || (req.url ~ "(\?|&)no_cache=1")
        || (req.url ~ "(\?|&)no_varnish=1")
        || (req.url ~ "(\?|&)eID=")
        || (req.url ~ "(\?|&)cHash=")
        || (req.url ~ "/tx-[a-z-]+/")
        || (req.url ~ "/pagetype-[a-z-]+/")
        || (req.url ~ "^/phpmyadmin/?")
    ) {
        return (pass);
        #===
    }


    # unset grace-header from request
    unset req.http.grace;

    # Removes all cookies named __utm? (utma, utmb...) and __unam - tracking thing
    # Otherwise we might run into problems with caching
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|(?<=; )) *__utm.=[^;]+;? *", "\1"); # Google Analytics
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|(?<=; )) *__unam=[^;]+;? *", "\1"); # Google Analytics
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|(?<=; )) *_et_coid=[^;]+;? *", "\1"); # eTracker
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|(?<=; )) *isSdEnabled=[^;]+;? *", "\1"); # perso-net shit
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|(?<=; )) *cookie_optin=[^;]+;? *", "\1"); # Cookie-Opt-In
    if (req.http.Cookie == "") {
        unset req.http.Cookie;
    }

    [...]
}

#========================================================
# Sub-routine after data from backend is received and before it is cached
#========================================================
sub vcl_backend_response {
    # Happens after we have read the response headers from the backend.
    #
    # Here you clean the response headers, removing silly Set-Cookie headers
    # and other mistakes your backend does.

    # Set TTL and grace
    set beresp.ttl = 1w;
    set beresp.grace = 3d;

    [...]

    # Only cache objects that are requested with frontend-cookies if ProxyCaching is set to 1
    if (
        (bereq.http.Cookie)
        && (! beresp.http.X-TYPO3-ProxyCaching == "1")
    ) {

        # Do not cache this object and do not keep decision
        set beresp.uncacheable = true;
        set beresp.ttl = 0s;
        set beresp.grace = 0s;
        return (deliver);
    }


    # Check for some things in the response-header that indicate that we should not cache
    # e.g. we do NOT cache contents that are about to set a cookie
    # or where ProxyCaching is set to 2
    if (
        (beresp.http.Set-Cookie)
        || (beresp.http.Vary == "*")
        || (beresp.http.Authorization)
        || (beresp.http.Pragma ~ "nocache")
        || (beresp.http.Cache-Control ~ "no-cache")
        || (beresp.http.X-TYPO3-ProxyCaching == "2")

        # TYPO3 uses "private" when INT-Scripts are used!
        # so we check for ProxyCaching variable in addition
        || (
            (beresp.http.Cache-Control ~ "private")
            && (! beresp.http.X-TYPO3-ProxyCaching == "1")
        )
    ) {

        # Do not cache this object and do not keep the decision
        set beresp.uncacheable = true;
        set beresp.ttl = 0s;
        set beresp.grace = 0s;
        return (deliver);
        #===
    }

    return (deliver);
    #===

}

#========================================================
# Sub-routine after object is loaded from cache
#========================================================
sub vcl_hit {

    [...]

    # Based on the already cached object we check if there is login sensitive data allowed on the cached pages
    # If so, we pass to backend if a cookie is set
    if (
        (! obj.http.X-TYPO3-ProxyCaching == "1")
        && (req.http.Cookie)
    ){
        return (pass);
        #===
    }

   [...]
}

#========================================================
# Sub-routine before delivering final data
#========================================================
sub vcl_deliver {

    # Happens when we have all the pieces we need, and are about to send the
    # response to the client.
    #
    # You can do accounting or modifying the final object here.

    [...]

    # Remove cache control if it isn't needed
    if (resp.http.X-TYPO3-ProxyCaching ~ "1") {
	    unset resp.http.cache-control;
    }

    [...]

    # Remove entries related to Varnish-Extension and Accelerator
    unset resp.http.xtag;
    unset resp.http.X-TYPO3-ProxyCaching;

    [...]
}

5. 减少数组和对象以提高序列化和存储效率

在某些使用场景中,您需要将数组或对象序列化以便存储,例如数据库。但尤其是对象可能非常大,将它们写入或从数据库中读取效率非常低。更不用说数据库大小的不断增长。

MarkerReducer 使用几种技术来减少您需要处理的数据量。它起源于存储用于稍后通过 crontab 发送的电子邮件模板中标记数组的需要(因此得名)。数组包含字符串,但也包含大对象,我不希望在减少之前序列化它们。

它包含两个静态函数

  • public static function implode(array $marker): array - 接收您的数组并返回用于存储的数组版本
  • public static function explode(array $marker): array - 接收数组的简化版本,并返回原始版本

在此特别感谢 Christian Dilger,他创建了一个更高级的 MarkerReducer 版本。

请注意:为了保持向后兼容性,默认情况下使用的是旧版本的 MarkerReducer。 要使用新的高级版本,请转到“设置”->“扩展配置”并切换到高级版本。

6. 为您的扩展提供缓存 API

  1. ext_localconf.php 中通过设置前端和后端缓存来激活它。
$cacheIdentifier = \Madj2k\CoreExtended\Utility\GeneralUtility::underscore($extKey);
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'][$cacheIdentifier] = [
    'frontend' => \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend::class,
    'backend' => \TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend::class,
    'groups' => [
        'all',
        'pages',
    ],
];
  1. 创建一个扩展 AbstractCache 类的缓存类
/**
* Class SitemapCache
*
* @author Steffen Kroggel <developer@steffenkroggel.de>
* @copyright Steffen Kroggel
* @package Madj2k_CoreExtended
* @license https://gnu.ac.cn/licenses/gpl.html GNU General Public License, version 3 or later
*/
class SitemapCache extends \Madj2k\Accelerator\Cache\CacheAbstract
{


}
  1. 在您的控制器中使用它。以下示例为多个域构建一个网站地图,并为每个域独立缓存。
/**
 * Class GoogleController
 *
 * @author Steffen Kroggel <developer@steffenkroggel.de>
 * @copyright Steffen Kroggel
 * @package Madj2k_CoreExtended
 * @license https://gnu.ac.cn/licenses/gpl.html GNU General Public License, version 3 or later
 */
class GoogleController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
{

    /**
     * pagesRepository
     *
     * @var \Madj2k\CoreExtended\Domain\Repository\PagesRepository|null
     */
    protected ?PagesRepository $pagesRepository = null;


    /**
     * action sitemap
     *
     * @return string
     * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException
     * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
     */
    public function sitemapAction(): string
    {

        $cache = $this->getCache()->setEntryIdentifier(GeneralUtility::getIndpEnv('HTTP_HOST'));
        if (!$sitemap = $cache->getContent()) {

            $currentPid = $GLOBALS['TSFE']->id;
            $treeList = explode(
                ',',
                \Madj2k\CoreExtended\Utility\QueryUtility::getTreeList($currentPid)
            );

            $pages = $this->pagesRepository->findByUidListAndDokTypes($treeList);
            $this->view->assign('pages', $pages);
            $sitemap = $this->view->render();

            // flush caches
            $cache->flushByTag(CacheAbstract::TAG_IDENTIFIER_PLUGIN);

            // save results in cache
            $cache->setContent($sitemap);

            $this->getLogger()->log(\TYPO3\CMS\Core\Log\LogLevel::INFO, sprintf('Successfully rebuilt Google sitemap feed.'));
        } else {
            $this->getLogger()->log(\TYPO3\CMS\Core\Log\LogLevel::INFO, sprintf('Successfully loaded Google sitemap from cache.'));
        }

        return $sitemap;

    }


    /**
     * Returns the cache object
     *
     * @return \Madj2k\CoreExtended\Cache\SitemapCache
     */
    protected function getCache(): SitemapCache
    {
        $cache = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(SitemapCache::class);
        $cache->setIdentifier('my_extension'); // may differ if you have several caches in your extension
        $cache->setRequest($this->request);
        return $cache;
    }
}