Drupal 8站点股票行情块单独排除缓存的实现问题求助
解决Drupal 8中单独排除股票行情块缓存的问题
你遇到的问题很典型:整页缓存(Drupal自带的页面缓存+Varnish)会把所有内容包括你的股票行情块一起缓存,单独在预处理钩子中设置max-age根本不会生效——因为整页缓存优先级更高,块的缓存规则被忽略了。下面给你两种可行的解决方案,按需选择:
方案一:使用ESI(Edge Side Includes)推荐,适合服务器端渲染+SEO友好
ESI是Varnish支持的一种技术,可以让整页缓存时,单独请求指定块的内容,绕过整页缓存规则。这样你的股票行情块就能独立控制缓存时长,不影响页面其他静态内容。
步骤1:开启Drupal的ESI支持
- 进入Drupal后台,依次点击 配置 → 性能
- 找到「缓存高级设置」,勾选「允许ESI」,保存配置
步骤2:修正你的预处理钩子代码
之前的代码缺少ESI开启的关键配置,同时要确保块的标识判断正确建议临时用kint($vars)打印变量,确认derivative_plugin_id的准确值:
function mytheme_preprocess_block(&$vars) { // 确认块的标识是否正确,可临时用kint($vars)查看 if($vars['derivative_plugin_id'] == 'stocktickerblock') { // 开启ESI,让Varnish单独处理这个块的请求 $vars['#esi'] = TRUE; // 设置块的缓存时长为5分钟(300秒) $vars['#cache']['max-age'] = 300; // 添加缓存标签,数据库更新时可主动清除块缓存 $vars['#cache']['tags'] = ['stock_ticker_data']; // 该块不依赖用户/语言等上下文,清空上下文列表 $vars['#cache']['contexts'] = []; } }
步骤3:配置Varnish支持ESI
修改你的Varnish配置文件(通常是default.vcl),添加以下规则:
- 在
vcl_recv函数中:
if (req.http.X-ESI == '1') { set req.http.Surrogate-Capability = "abc=ESI/1.0"; }
- 在
vcl_backend_response函数中:
if (beresp.http.Surrogate-Control ~ "ESI/1.0") { unset beresp.http.Surrogate-Control; set beresp.do_esi = true; }
修改后重启Varnish服务。
步骤4:数据库更新时主动清缓存
在你的数据库更新脚本中,添加一行代码清除股票行情块的缓存标签,这样数据更新后能立即刷新块内容:
\Drupal\Core\Cache\Cache::invalidateTags(['stock_ticker_data']);
方案二:AJAX动态加载块内容适合无法修改Varnish配置的场景
如果没法调整Varnish配置,你可以用前端AJAX定时请求最新的行情数据,完全绕过整页缓存。
步骤1:修改块的模板文件
找到股票行情块的模板(比如block--stocktickerblock.html.twig),替换内容为:
<div id="stock-ticker-container"> {# 初始加载的静态内容 #} {{ content }} </div> <script> // 每隔5分钟(300000毫秒)刷新一次行情 setInterval(function() { fetch('/stock-ticker/ajax') .then(response => response.text()) .then(data => { document.getElementById('stock-ticker-container').innerHTML = data; }); }, 300000); </script>
步骤2:创建AJAX路由和控制器
- 在你的自定义模块的
routing.yml中添加路由:
stock_ticker.ajax: path: '/stock-ticker/ajax' defaults: _controller: '\Drupal\mymodule\Controller\StockTickerController::ajaxRender' _title: 'Stock Ticker AJAX' requirements: _access: 'TRUE'
- 创建控制器类,返回最新的行情数据:
namespace Drupal\mymodule\Controller; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Cache\CacheableResponse; class StockTickerController extends ControllerBase { public function ajaxRender() { // 从数据库获取最新行情数据 $stock_data = $this->fetchStockData(); $build = [ '#theme' => 'stock_ticker', // 自定义模板渲染数据 '#data' => $stock_data, '#cache' => [ 'max-age' => 0, // 禁止缓存AJAX请求结果 'tags' => ['stock_ticker_data'], ], ]; $response = new CacheableResponse(\Drupal::service('renderer')->renderRoot($build)); $response->getCacheableMetadata()->addCacheTags(['stock_ticker_data']); return $response; } private function fetchStockData() { // 这里写你的数据库查询逻辑 $database = \Drupal::database(); return $database->query('SELECT * FROM stock_data ORDER BY updated_at DESC LIMIT 1')->fetchAssoc(); } }
两种方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| ESI | 服务器端渲染,SEO友好;不依赖前端JS | 需要修改Varnish配置;对服务器配置有要求 |
| AJAX | 无需调整Varnish;实现简单 | 初始加载可能有延迟;JS禁用时无法更新内容 |
内容的提问来源于stack exchange,提问作者pburgh




