Yii 2 片段缓存(Fragment Caching)完全指南:从 beginCache 到动态内容的源码级剖析

发布时间:2026/9/24 11:20:00
Yii 2 片段缓存(Fragment Caching)完全指南:从 beginCache 到动态内容的源码级剖析
后端Web框架【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址https://gitcode.com/gh_mirrors/yi/yii2点击查看免费下载片段缓存Fragment Caching是 Yii 2 在数据缓存之上提供的一项页面级优化能力用于把页面中一段相对静态的 HTML 输出缓存起来从而消除每次请求都要重新生成这段内容的开销。本文将以docs/guide-pt-BR/caching-fragment.md与英文版docs/guide/caching-fragment.md同源为核心骨架结合 Yii 2 框架源码与单元测试系统讲解片段缓存的五种配置选项、嵌套缓存、动态内容机制及其底层实现帮助你在真实项目里正确、高效地使用这一功能。什么是片段缓存片段缓存指的是缓存 Web 页面中的一个片段。例如某页面用一个表格展示年度销售汇总你可以把这个表格整体缓存起来从而省去每次请求重新生成该表格的时间。正如文档所说片段缓存构建在数据缓存Data Caching之上——它的本质仍然是向缓存组件写入、读取数据只是数据的内容是一段 HTML 输出。在 Yii 2 中片段缓存的使用场景集中在 视图View中。其基本用法如下if ($this-beginCache($id)) { // ... 在这里生成内容 ... $this-endCache(); }即在[[yii\base\View::beginCache()|beginCache()]]与[[yii\base\View::endCache()|endCache()]]之间包裹内容生成逻辑如果缓存命中beginCache()会直接渲染缓存中的内容并返回false从而跳过内容生成逻辑如果缓存未命中内容生成逻辑会被执行当调用endCache()时生成好的输出会被捕获并存入缓存。与数据缓存一样每个片段都需要一个唯一的$id来标识缓存条目。$id可以是字符串也可以是数组数组会作为缓存键的组成部分参与计算详见后文缓存键的计算一节。删除片段缓存英文版原文档补充了删除片段缓存的方法由于片段缓存最终存储在数据缓存组件中且缓存键由类名与$id共同构成因此可以通过Yii::$app-cache直接删除Yii::$app-cache-delete([yii\widgets\FragmentCache, $id]);这一写法与源码中FragmentCache::calculateKey()的键构造逻辑完全对应见后文。缓存选项Caching OptionsbeginCache()的第二个参数可以传入一个选项数组用于对片段缓存做精细化配置。从源码看这个选项数组在底层会被用来配置一个实现了片段缓存实际功能的 widget——[[yii\widgets\FragmentCache]]。让我们先看 FragmentCache 的源码 中定义的公共属性它们正是所有可用选项属性默认值作用cachecache缓存对象或缓存对象的应用组件 ID自 2.0.2 起也支持配置数组duration60内容在缓存中保持有效的秒数0表示永不过期dependencynull缓存内容所依赖的Dependency对象或创建依赖对象的配置数组variationsnull导致缓存内容变化的因素列表如语言、GET 参数enabledtrue是否启用片段缓存可用于按条件开关缓存接下来逐一展开文档重点讲解的四个高频选项。时长duration[[yii\widgets\FragmentCache::duration|duration]]是最常用的选项它指定内容在缓存中保持有效的秒数。下面的代码将片段内容最多缓存一个小时if ($this-beginCache($id, [duration 3600])) { // ... 生成内容 ... $this-endCache(); }如果该选项未设置默认值为60秒对应源码 FragmentCache.php 中的public $duration 60;。另外根据源码注释设置为0时表示缓存的数据永不过期。这一选项最终会传递给底层缓存组件的set()调用$this-cache-set($this-calculateKey(), $data, $this-duration, $this-dependency);见 FragmentCache.php依赖dependencies与数据缓存的依赖机制类似被缓存的片段内容也可以挂载依赖当依赖发生变化时缓存内容自动失效。例如一篇 post 的展示内容依赖于它是否被修改过。要指定依赖设置[[yii\widgets\FragmentCache::dependency|dependency]]选项即可它可以是[[yii\caching\Dependency]]对象也可以是用于创建依赖对象的配置数组。下面的代码指定片段内容依赖post表中updated_at列的最大值即依赖最近一次帖子修改时间$dependency [ class yii\caching\DbDependency, sql SELECT MAX(updated_at) FROM post, ]; if ($this-beginCache($id, [dependency $dependency])) { // ... 生成内容 ... $this-endCache(); }当有帖子被修改时MAX(updated_at)变化缓存随即失效下次请求会重新生成内容。从源码看配置数组会在写入缓存前被实例化为依赖对象if (is_array($this-dependency)) { $this-dependency Yii::createObject($this-dependency); }见 FragmentCache.php除了DbDependencyYii 2 的yii\caching命名空间下还提供了FileDependency依赖文件修改时间、ExpressionDependency依赖 PHP 表达式结果、CallbackDependency依赖回调返回值等多种依赖类型具体可参考数据缓存文档。变体variations缓存内容可能随某些参数而变化。例如对于支持多语言的 Web 应用同一段视图代码会为不同语言生成不同内容因此你可能希望缓存内容随当前应用语言而变化。设置[[yii\widgets\FragmentCache::variations|variations]]选项即可指定缓存变体它应是标量值的数组每个值代表一个特定的变化因子。下面的代码让缓存内容随语言变化if ($this-beginCache($id, [variations [Yii::$app-language]])) { // ... 生成内容 ... $this-endCache(); }值得注意的是源码中variations属性声明为string[]|string见 FragmentCache.php因此它也可以是单个字符串同时变体值会直接参与缓存键的计算详见下文缓存键的计算。单元测试testVariations见 FragmentCacheTest.php验证了variations为[ru]与[en]时会产生两个互不干扰的缓存条目且无变体与有变体的条目彼此独立。开关缓存Toggling Caching某些场景下你可能只想在特定条件满足时才启用片段缓存。例如一个展示表单的页面只希望在首次请求GET 请求时缓存表单之后的展示POST 请求表单可能包含用户提交的数据不应被缓存。此时可以设置[[yii\widgets\FragmentCache::enabled|enabled]]选项if ($this-beginCache($id, [enabled Yii::$app-request-isGet])) { // ... 生成内容 ... $this-endCache(); }从源码看enabled直接决定了缓存组件的初始化当enabled为false时FragmentCache::init()中$this-cache会被置为null从而跳过所有缓存读写逻辑见 FragmentCache.php内容每次都实时生成也不会产生输出缓冲开销之外的副作用。缓存键的计算与删除原理理解缓存键有助于正确管理缓存。在 FragmentCache.php 中protected function calculateKey() { return array_merge([__CLASS__, $this-getId()], (array)$this-variations); }即缓存键由三部分组成片段缓存类名yii\widgets\FragmentCache片段的id即beginCache()传入的$id所有variations变体值。这正是前文删除片段缓存写法Yii::$app-cache-delete([yii\widgets\FragmentCache, $id]);的依据——只要$id相同、没有额外变体删除时构造同样的数组键即可命中。反过来也说明如果写入时带了variations删除时必须带上相同的变体值否则无法定位到对应缓存条目。嵌套缓存Nested Caching片段缓存可以嵌套使用即一个已缓存的片段可以包含在另一个同样被缓存的片段中。例如评论被缓存在内层片段里而评论连同 post 内容一起被缓存在外层片段中。下面的代码展示了两个片段缓存如何嵌套if ($this-beginCache($id1)) { // ...内容生成逻辑... if ($this-beginCache($id2, $options2)) { // ...内容生成逻辑... $this-endCache(); } // ...内容生成逻辑... $this-endCache(); }嵌套缓存可以为内层与外层设置不同的缓存选项例如不同的过期时长。即使外层缓存的数据失效内层缓存仍然可能提供有效的内层片段反之则不成立——如果外层缓存被判定为有效即使内层缓存内容已失效外层仍会继续提供同一份缓存副本。因此文档特别提醒设置嵌套缓存的时长或依赖时必须小心否则过时的内层片段可能被保留在外层片段中。从实现角度看外层片段的run()方法会捕获整段输出包括内层缓存输出的内容一并存入缓存只有当外层缓存失效重新生成时内层缓存才会被重新评估。动态内容Dynamic Content使用片段缓存时你可能会遇到这样的场景一大段内容相对静态只有个别位置是变化的。例如页面头部除了主导航栏还要显示当前登录用户的用户名。另一个典型问题是被缓存的内容中可能包含每次请求都必须执行的 PHP 代码例如注册 asset bundle 的代码。这两种问题都可以用 Yii 2 的动态内容Dynamic Content特性解决。动态内容是指即使被包裹在片段缓存中也不应被缓存的一段输出。要让内容始终保持动态它必须通过执行 PHP 代码在每次请求时重新生成哪怕外层内容正从缓存中提供。在片段缓存内部调用[[yii\base\View::renderDynamic()]]即可在指定位置插入动态内容if ($this-beginCache($id1)) { // ...内容生成逻辑... echo $this-renderDynamic(return Yii::$app-user-identity-name;); // ...内容生成逻辑... $this-endCache(); }renderDynamic()接受一段 PHP 代码作为参数该 PHP 代码的返回值被视为动态内容。无论外层片段是否命中缓存这段 PHP 代码都会在每次请求时执行。动态内容的底层实现从 View::renderDynamic() 的源码可以看到其工作原理public function renderDynamic($statements) { if (!empty($this-cacheStack)) { $n count($this-dynamicPlaceholders); $placeholder ![CDATA[YII-DYNAMIC-$n]]; $this-addDynamicPlaceholder($placeholder, $statements); return $placeholder; } return $this-evaluateDynamicContent($statements); }关键流程如下当存在活动的缓存栈cacheStack时renderDynamic()并不立即执行 PHP 代码而是生成一个形如![CDATA[YII-DYNAMIC-0]]的占位符并把占位符 → PHP 代码的映射登记到动态内容占位符列表中返回占位符本身内容生成完毕后占位符连同静态 HTML 一起被存入缓存见 FragmentCache::run() 中$data [$content, $this-getDynamicPlaceholders()];在输出阶段DynamicContentAwareTrait::updateDynamicContent()见 DynamicContentAwareTrait.php会通过evaluateDynamicContent()内部即eval执行每段 PHP 代码并用执行结果替换占位符当内容从缓存恢复时getCachedContent()命中占位符同样会被重新求值替换从而保证动态部分每次请求都新鲜。2.0.14 之后的动态内容 API英文版原文档补充说明自2.0.14起动态内容 API 通过[[yii\base\DynamicContentAwareInterface]]接口和[[yii\base\DynamicContentAwareTrait]]trait 暴露FragmentCache类即为参考实现。接口定义了三个方法getDynamicPlaceholders()、setDynamicPlaceholders()、addDynamicPlaceholder()见 DynamicContentAwareInterface.phptrait 则提供了默认实现以及占位符替换逻辑updateDynamicContent()见 DynamicContentAwareTrait.php。View通过pushDynamicContent()/popDynamicContent()维护一个动态内容感知对象的缓存栈见 View.phpFragmentCache在init()中入栈、在run()中出栈从而支持多层嵌套缓存中动态内容的正确归属与替换。通过单元测试验证行为仓库中的 FragmentCacheTest.php 使用ArrayCache作为缓存组件覆盖了上述绝大多数特性可以作为理解行为的最佳参考testCacheEnabled第一次beginCache(test)返回true未命中生成并缓存内容第二次beginCache(test)返回false命中直接输出缓存内容testCacheDisabled1/testCacheDisabled2enabled false时始终实时生成、不写入缓存且已存在的缓存条目不会被禁用状态下的读取所影响testSingleDynamicFragment循环 42 次首次未命中其余全部命中但renderDynamic()返回的计数器每次请求都递增证明动态部分未被缓存testMultipleDynamicFragments同一片段内多个renderDynamic()调用都能在缓存恢复后正确求值替换testNestedDynamicFragments外层缓存命中时内层缓存与外层动态内容均能正确渲染testVariations验证不同variations产生独立缓存条目字符串形式与数组形式均可。使用建议与注意事项结合原文档与源码在使用片段缓存时有几点值得注意缓存组件先行片段缓存依赖cache应用组件可通过FragmentCache::$cache选项替换为其他缓存对象或组件 ID。关于缓存组件的配置方式如 memcached、文件缓存、数据库缓存等参见数据缓存文档与应用组件文档。合理设置时长与依赖静态性强的片段如年度报表表格适合较长duration依赖数据库变化的片段应优先使用DbDependency等依赖而非单纯依赖过期时间。变体键要一致读写以及删除时$id与variations必须保持一致否则会产生缓存键错位。嵌套缓存注意失效传播外层缓存有效时内层失效内容仍会被外层保留嵌套层级的时长/依赖需要通盘考虑。动态内容只放必要部分renderDynamic()中的代码每次请求都会执行滥用会削弱缓存收益只把真正需要实时刷新的部分如当前用户名、CSRF token、asset 注册放入动态内容。片段缓存是 Yii 2 在数据缓存之上提供的高性价比优化手段在视图层用一对beginCache()/endCache()调用即可获得整段 HTML 的缓存能力配合duration、dependency、variations、enabled四个选项与动态内容机制可以精细地平衡缓存命中率与内容新鲜度是构建高性能 Yii 2 应用时值得优先掌握的技能之一。赞分享后端Web框架【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址https://gitcode.com/gh_mirrors/yi/yii2点击查看免费下载相关推荐Yii 2 片段缓存Fragment Caching实战指南从 beginCache 到动态内容Yii 2 片段缓存Fragment Caching实战指南从 beginCache 到动态内容 片段缓存Fragment Caching是 Yii后端Web框架Yii 2 片段缓存Fragment Caching实战指南从 beginCache 到动态内容的全链路解析Yii 2 片段缓存Fragment Caching实战指南从 beginCache 到动态内容的全链路解析 片段缓存Fragment Caching后端Web框架Yii 2 片段缓存Fragment Caching完全指南从视图片段缓存到动态内容Yii 2 片段缓存Fragment Caching完全指南从视图片段缓存到动态内容 片段缓存Fragment Caching是 Yii 2 提供的页后端Web框架创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考