drupal-cache-contexts
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDrupal Cache Contexts
Drupal缓存上下文
Cache contexts define request-dependent cache variations. Analogous to HTTP header.
Vary缓存上下文定义了依赖于请求的缓存变体,类似于HTTP的头。
VaryWhen to Use
使用场景
- Content varies by user role or permissions
- Content changes based on URL parameters
- Multi-language sites with localized content
- Theme-dependent rendering
- Preventing cache explosion from overly broad contexts
- 内容随用户角色或权限变化
- 内容根据URL参数改变
- 多语言站点的本地化内容
- 依赖主题的渲染
- 避免因上下文范围过宽导致的缓存膨胀
Available Contexts
可用上下文
| Context | Variations | Use Case |
|---|---|---|
| Per-user (AVOID) | Truly personalized content only |
| Per-role combination | Role-based visibility |
| Per-permission set | Permission-based content |
| Per-path | Path-dependent content |
| Per-parameter | Sort, filter, pagination |
| Per-language | Translated content |
| Per-theme | Theme-specific rendering |
| Per-session | Session data (triggers placeholder) |
| 上下文 | 变体类型 | 适用场景 |
|---|---|---|
| 按用户区分(需避免) | 仅用于真正的个性化内容 |
| 按角色组合区分 | 基于角色的可见性控制 |
| 按权限集合区分 | 基于权限的内容展示 |
| 按路径区分 | 依赖路径的内容 |
| 按参数区分 | 排序、筛选、分页功能 |
| 按语言区分 | 翻译后的内容 |
| 按主题区分 | 主题专属渲染 |
| 按会话区分 | 会话数据(触发占位符) |
Context Hierarchy
上下文层级
user (AVOID - per-user cache explosion)
├── user.is_super_user (2 variations only)
├── user.roles (per role combination)
│ └── user.roles:editor (has specific role?)
└── user.permissions (per permission set)Rule: Always use the most specific context possible.
user (AVOID - per-user cache explosion)
├── user.is_super_user (2 variations only)
├── user.roles (per role combination)
│ └── user.roles:editor (has specific role?)
└── user.permissions (per permission set)规则: 始终使用最具体的上下文。
Examples
示例
Example 1: Role-Based Content
示例1:基于角色的内容
Input: "I show different content to editors vs anonymous users"
Output:
php
$build = [
'#markup' => $this->getRoleBasedContent(),
'#cache' => [
'contexts' => ['user.roles'],
],
];输入: "我需要向编辑用户和匿名用户展示不同的内容"
输出:
php
$build = [
'#markup' => $this->getRoleBasedContent(),
'#cache' => [
'contexts' => ['user.roles'],
],
];Example 2: Per-User Content (Use Sparingly)
示例2:按用户区分的内容(谨慎使用)
Input: "I need to show the user's own profile info"
Output:
php
// WARNING: Creates cache entry per user - use lazy_builder instead
$build = [
'#markup' => $user->getDisplayName(),
'#cache' => [
'contexts' => ['user'], // High cardinality!
],
];
// BETTER: Use lazy builder for per-user content
$build = [
'#lazy_builder' => ['my_module.lazy:userName', []],
'#create_placeholder' => TRUE,
];输入: "我需要展示用户自己的个人资料信息"
输出:
php
// WARNING: Creates cache entry per user - use lazy_builder instead
$build = [
'#markup' => $user->getDisplayName(),
'#cache' => [
'contexts' => ['user'], // High cardinality!
],
];
// BETTER: Use lazy builder for per-user content
$build = [
'#lazy_builder' => ['my_module.lazy:userName', []],
'#create_placeholder' => TRUE,
];Example 3: URL Query Parameters
示例3:URL查询参数
Input: "My listing supports ?sort=date and ?sort=title parameters"
Output:
php
$sort = \Drupal::request()->query->get('sort', 'date');
$build = [
'#theme' => 'item_list',
'#items' => $this->getSortedItems($sort),
'#cache' => [
'contexts' => ['url.query_args:sort'],
],
];输入: "我的列表支持?sort=date和?sort=title参数"
输出:
php
$sort = \Drupal::request()->query->get('sort', 'date');
$build = [
'#theme' => 'item_list',
'#items' => $this->getSortedItems($sort),
'#cache' => [
'contexts' => ['url.query_args:sort'],
],
];Example 4: Language-Dependent Content
示例4:依赖语言的内容
Input: "I render translated labels in my block"
Output:
php
$build = [
'#markup' => $this->t('Welcome'),
'#cache' => [
'contexts' => ['languages:language_interface'],
],
];输入: "我在区块中渲染翻译后的标签"
输出:
php
$build = [
'#markup' => $this->t('Welcome'),
'#cache' => [
'contexts' => ['languages:language_interface'],
],
];Example 5: Combined Contexts
示例5:组合上下文
Input: "Content varies by role AND language"
Output:
php
$build = [
'#markup' => $this->getLocalizedRoleContent(),
'#cache' => [
'contexts' => [
'user.roles',
'languages:language_interface',
],
],
];输入: "内容随角色和语言变化"
输出:
php
$build = [
'#markup' => $this->getLocalizedRoleContent(),
'#cache' => [
'contexts' => [
'user.roles',
'languages:language_interface',
],
],
];Example 6: Custom Cache Context
示例6:自定义缓存上下文
Input: "I need to vary cache by a custom HTTP header"
Output:
yaml
undefined输入: "我需要根据自定义HTTP头来区分缓存"
输出:
yaml
undefinedmy_module.services.yml
my_module.services.yml
services:
cache_context.custom_header:
class: Drupal\my_module\Cache\CustomHeaderContext
arguments: ['@request_stack']
tags:
- { name: cache.context }
```php
namespace Drupal\my_module\Cache;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\Context\CacheContextInterface;
class CustomHeaderContext implements CacheContextInterface {
public static function getLabel() {
return t('Custom header');
}
public function getContext() {
$request = $this->requestStack->getCurrentRequest();
return $request->headers->get('X-Custom-Header', 'default');
}
public function getCacheableMetadata() {
return new CacheableMetadata();
}
}php
// Usage
$build['#cache']['contexts'][] = 'custom_header';services:
cache_context.custom_header:
class: Drupal\my_module\Cache\CustomHeaderContext
arguments: ['@request_stack']
tags:
- { name: cache.context }
```php
namespace Drupal\my_module\Cache;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\Context\CacheContextInterface;
class CustomHeaderContext implements CacheContextInterface {
public static function getLabel() {
return t('Custom header');
}
public function getContext() {
$request = $this->requestStack->getCurrentRequest();
return $request->headers->get('X-Custom-Header', 'default');
}
public function getCacheableMetadata() {
return new CacheableMetadata();
}
}php
// Usage
$build['#cache']['contexts'][] = 'custom_header';Example 7: Block with Cache Contexts
示例7:带缓存上下文的区块
Input: "My block shows different actions based on permissions"
Output:
php
class ActionBlock extends BlockBase {
public function build() {
$actions = [];
if (\Drupal::currentUser()->hasPermission('edit content')) {
$actions[] = 'Edit';
}
return ['#markup' => implode(', ', $actions)];
}
public function getCacheContexts() {
return Cache::mergeContexts(
parent::getCacheContexts(),
['user.permissions']
);
}
}输入: "我的区块根据权限展示不同操作"
输出:
php
class ActionBlock extends BlockBase {
public function build() {
$actions = [];
if (\Drupal::currentUser()->hasPermission('edit content')) {
$actions[] = 'Edit';
}
return ['#markup' => implode(', ', $actions)];
}
public function getCacheContexts() {
return Cache::mergeContexts(
parent::getCacheContexts(),
['user.permissions']
);
}
}Common Mistakes
常见错误
| Mistake | Impact | Solution |
|---|---|---|
Using | Cache explosion (1 entry per user) | Use |
Using | Triggers auto-placeholder | Use lazy builder |
| Missing context | Same cached content for all variations | Add appropriate context |
| Too broad context | Unnecessary cache variations | Use most specific context |
| 错误 | 影响 | 解决方案 |
|---|---|---|
使用 | 缓存膨胀(每个用户对应一个缓存条目) | 使用 |
直接使用 | 触发自动占位符 | 使用lazy builder |
| 遗漏上下文 | 所有变体共享相同缓存内容 | 添加对应的上下文 |
| 上下文范围过宽 | 不必要的缓存变体 | 使用最具体的上下文 |
Auto-Placeholdering
自动占位符机制
These contexts trigger automatic placeholdering in Dynamic Page Cache:
yaml
undefined以下上下文会在动态页面缓存中触发自动占位符:
yaml
undefinedservices.yml - default conditions
services.yml - default conditions
renderer.config:
auto_placeholder_conditions:
contexts:
- 'session'
- 'user'
Content with these contexts is replaced with a placeholder and rendered separately.renderer.config:
auto_placeholder_conditions:
contexts:
- 'session'
- 'user'
带有这些上下文的内容会被替换为占位符,单独进行渲染。Debugging
调试方法
bash
undefinedbash
undefinedEnable debug headers
启用调试头
$settings['http.response.debug_cacheability_headers'] = TRUE;
$settings['http.response.debug_cacheability_headers'] = TRUE;
Check applied contexts
检查已应用的上下文
curl -sI https://site.com/ | grep X-Drupal-Cache-Contexts
curl -sI https://site.com/ | grep X-Drupal-Cache-Contexts
Output: X-Drupal-Cache-Contexts: languages:language_interface theme url.path user.permissions
输出示例:X-Drupal-Cache-Contexts: languages:language_interface theme url.path user.permissions
undefinedundefined