37Кэширование фрагментов, страниц и HTTP
beginCache()/endCache() с duration, dependency, variations и enabled; вложенные фрагменты и динамическое содержимое renderDynamic(); фильтр PageCache; HTTP-кэш через HttpCache — Last-Modified, ETag, Cache-Control, а также session cache limiter и SEO-эффект.
Дальше данных: можно кэшировать готовый HTML — кусок представления, всю страницу, — и даже не отдавать страницу вовсе, если браузер уже имеет свежую копию (HTTP 304). Всё это те же зависимости и то же хранилище, что и в кэшировании данных.
Фрагменты#
<?php if ($this->beginCache($id, $options)) { ?>
...содержимое, которое дорого генерировать...
<?php $this->endCache(); } ?>beginCache() вернул true — кэша нет, содержимое генерируется и сохраняется; false — кэш есть, он уже выведен, тело пропускается.
Опции#
$options = [
'duration' => 3600, // по умолчанию 60 секунд; 0 — навсегда
'dependency' => [
'class' => 'yii\caching\DbDependency',
'sql' => 'SELECT COUNT(*) FROM post',
],
'variations' => [Yii::$app->language, Yii::$app->user->id], // отдельный кэш для каждой комбинации
'enabled' => !YII_ENV_DEV, // включать по условию
'cache' => 'cache', // компонент
];durationdependencyvariationsenabledfalse — просто выводить без кэша (удобно для отладки)cacheВложенные фрагменты#
<?php if ($this->beginCache($id1)) { ?>
...внешний фрагмент...
<?php if ($this->beginCache($id2, $options2)) { ?>
...внутренний...
<?php $this->endCache(); } ?>
...
<?php $this->endCache(); } ?>Внутренний может иметь короче срок, чем внешний, но пока жив внешний — внутренний не пересоберётся (он часть внешнего HTML).
Динамическое содержимое внутри кэша#
<?php if ($this->beginCache($id)) { ?>
...статичная часть...
<?= $this->renderDynamic('return Yii::$app->user->identity->name;') ?>
<?= $this->renderDynamic(function ($view, $params) { return date('H:i:s'); }) ?>
<?php $this->endCache(); } ?>renderDynamic() вставляет плейсхолдер, который выполняется при каждом показе — даже когда фрагмент из кэша. Код передаётся строкой PHP или замыканием (замыкание — с 2.0.14).
Кэширование страниц#
Фильтр PageCache в контроллере — кэш целого ответа, включая заголовки:
public function behaviors()
{
return [
[
'class' => 'yii\filters\PageCache',
'only' => ['index'],
'duration' => 60,
'variations' => [Yii::$app->language],
'dependency' => ['class' => 'yii\caching\DbDependency', 'sql' => 'SELECT COUNT(*) FROM post'],
// 'enabled' => …, 'cache' => 'cache', 'cacheCookies' => false, 'cacheHeaders' => true
],
];
}Работает как фрагмент, но на уровне beforeAction(): при попадании действие не выполняется вовсе. Кэшируется только успешный ответ (statusCode == 200); cookie и заголовки — настраиваемо. variations — обязательно добавьте всё, что меняет страницу (пользователь, язык), иначе гость увидит чужие данные.
HTTP-кэширование#
Не генерировать ответ, если у клиента уже свежая копия. Фильтр HttpCache:
public function behaviors()
{
return [
[
'class' => 'yii\filters\HttpCache',
'only' => ['index'],
'lastModified' => function ($action, $params) {
return (new \yii\db\Query())->from('post')->max('updated_at'); // timestamp
},
'etagSeed' => function ($action, $params) {
// что угодно, что меняется с содержимым
return serialize([$this->page->title, $this->page->content]);
},
'cacheControlHeader' => 'public, max-age=3600',
'sessionCacheLimiter' => 'public',
// 'params' => [...], 'enabled' => true
],
];
}lastModifiedLast-Modified; браузер шлёт If-Modified-SinceetagSeedETag (хэшируется); браузер шлёт If-None-Match. Точнее, чем время, но требует вычислить содержимоеcacheControlHeaderCache-Control (по умолчанию public, max-age=3600)sessionCacheLimitersession_cache_limiter(): PHP по умолчанию отправляет Cache-Control: no-store, из-за чего браузеры не кэшируют; '' — не менять, 'public'/'private'/'nocache'При совпадении фильтр отвечает 304 Not Modified без выполнения действия. Достаточно одного из lastModified/etagSeed; при обоих проверяются оба. Для ETag Yii ставит «weak» ETag, если weakEtag = true (2.0.8).
Что выбрать#
Дорогой кусок страницы (меню категорий, блок «популярное»), остальное динамическое. variations по языку/роли.
Публичные страницы без персонализации: лендинги, статьи для гостей. Осторожно с cookie и CSRF-токенами в формах.
Всё публичное, что можно отдать браузеру и CDN: лента, API-ответы, картинки. Экономит трафик и время генерации.
Уровни комбинируются: страница закэширована PageCache, а HttpCache перед ним вообще не отдаёт тело, если ETag совпал.
Кэша нет — содержимое нужно сгенерировать; при false оно уже выведено из кэша.
Через $this->renderDynamic() — этот код выполняется при каждом показе.
Чтобы для каждого языка/пользователя/устройства был свой экземпляр кэша, иначе один вариант получат все.
Ответ 304 Not Modified без тела; действие контроллера не выполняется.
PHP ставит Cache-Control: no-store через session cache limiter; задайте sessionCacheLimiter => 'public' или ''.