madj2k / t3-accelerator
加速您的TYPO3安装:将关键CSS(视口以上)内联,压缩您的网站HTML,使用子域作为CDN以减少页面加载时间,通过页面属性管理代理缓存(例如使用Varnish),在将持久化对象存储到数据库时减少数据库大小
Requires
- php: >=7.4
- ext-json: *
- typo3/cms-core: ~10.4.0 || ~11.5.0 || ~12.4.0
Requires (Dev)
- typo3/testing-framework: ~6.16.9
Suggests
- sopsone-ch/varnish: ~2.4
- dev-master
- v12.4.15-stable
- v12.4.14-stable
- v12.4.12-stable
- v12.4.11-stable
- v12.4.10-stable
- v12.4.9-stable
- v12.4.8-stable
- v12.4.7-stable
- v12.4.6-stable
- v12.4.5-stable
- v12.4.4-stable
- v12.4.3-stable
- v12.4.2-stable
- v12.4.1-stable
- v12.4.0-stable
- v10.4.0-stable
- v9.5.1004-stable
- v9.5.1003-stable
- v9.5.1002-stable
- v9.5.1001-stable
- v9.5.1000-stable
- v9.5.13-stable
- v9.5.12-stable
- v9.5.11-stable
- v9.5.10-stable
- v9.5.9-stable
- v9.5.8-stable
- v9.5.7-stable
- v9.5.6-stable
- v9.5.5-stable
- v9.5.4-stable
- v9.5.3-stable
- v9.5.2-stable
- v9.5.1-stable
- v9.5.0-stable
- dev-development
- dev-master9.5
This package is auto-updated.
Last update: 2024-09-20 08:20:34 UTC
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”,因为它不使用外部服务器。它使用给定域的子域来提供静态内容,因此需要
- 相应的DNS配置
- 一个通配符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-accelerator
与opsone-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
- 在
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',
],
];
- 创建一个扩展
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
{
}
- 在您的控制器中使用它。以下示例为多个域构建一个网站地图,并为每个域独立缓存。
/**
* 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;
}
}