ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Yii 2 片段缓存(Fragment Caching)完全指南:从基本用法到嵌套缓存与动态内容

Yii 2 片段缓存(Fragment Caching)完全指南:从基本用法到嵌套缓存与动态内容 Yii 2 片段缓存Fragment Caching完全指南从基本用法到嵌套缓存与动态内容【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2片段缓存Fragment Caching是 Yii 2 框架中用于缓存 Web 页面局部片段的高阶缓存能力它建立在数据缓存之上可在视图View层通过一对方法调用把耗时生成的页面区块如年度销售汇总表、文章正文缓存起来从而消除每次请求重复生成这些内容的时间开销。阅读本文后你将掌握片段缓存的标准用法、duration/dependency/variations/enabled四大选项的配置技巧、嵌套缓存与动态内容的正确姿势并通过源码级剖析理解其底层实现原理与失效机制。什么是片段缓存片段缓存指的是缓存 Web 页面中的一个片段。例如如果一个页面用表格展示年度销售汇总你可以把这张表格存入缓存省去每次请求重新生成表格的时间。片段缓存构建在数据缓存之上本质上是「数据缓存的视图层封装」它以数据缓存为存储后端由视图组件提供专用的调用入口。与数据缓存一样片段缓存同样需要一个唯一的$id来标识一段被缓存的内容。基本用法beginCache 与 endCache要使用片段缓存只需在视图中使用如下构造if ($this-beginCache($id)) { // ...在这里生成内容... $this-endCache(); }也就是说把内容生成逻辑包在beginCache()与endCache()这对调用之间。执行语义如下如果内容在缓存中命中beginCache()会直接渲染出缓存内容并返回false从而跳过内容生成逻辑否则内容生成逻辑会被执行当调用endCache()时生成的内容会被捕获并存入缓存。这两个方法都定义在yii\base\View中。从源码看beginCache($id, $properties [])会把$id和当前视图对象注入属性数组然后以yii\widgets\FragmentCache组件开启一个小部件若缓存内容可用则立即调用endCache()并返回false否则返回true表示「请生成内容」。删除片段缓存当需要主动清除某个片段的缓存时例如后台编辑文章后可以直接操作底层缓存组件Yii::$app-cache-delete([yii\widgets\FragmentCache, $id]);这里的删除键[yii\widgets\FragmentCache, $id]并非随意编写而是与源码中FragmentCache::calculateKey()的键构造逻辑严格对应——该方法生成array_merge([__CLASS__, $this-getId()], (array)$this-variations)作为缓存键__CLASS__即yii\widgets\FragmentCache。因此若不使用variations选项按上述写法即可精确删除对应片段若使用了variations则需在删除键中补上相同的变体值。缓存选项除了$id你还可以把选项数组作为第二个参数传给yii\base\View::beginCache()。在后台这个选项数组会被用来配置一个真正实现片段缓存功能的yii\widgets\FragmentCache小部件。下表汇总了全部核心选项选项类型默认值作用durationint60缓存内容有效秒数设为 0 表示永不过期dependencyDependency对象或配置数组无声明缓存内容所依赖的依赖对象variations标量值数组无使缓存内容随不同因素语言、GET 参数等产生多个版本enabledbooltrue是否启用片段缓存cache缓存组件cache使用的缓存组件 ID源码属性见下文下面逐一说明各选项的用法与实现细节。duration缓存有效期duration是片段缓存最常用的选项它指定内容在缓存中最多可保持有效多少秒。下面的代码把片段内容最多缓存一小时if ($this-beginCache($id, [duration 3600])) { // ...在这里生成内容... $this-endCache(); }如果该选项未设置将使用默认值 60即缓存内容在 60 秒后过期。这一点在源码FragmentCache::$duration中有明确声明同时源码注释说明设为0表示缓存数据永不失效该语义与数据缓存Cache::set()的duration约定一致。dependency缓存依赖与数据缓存一样被缓存的片段内容也可以声明依赖。例如一篇文章显示的内容依赖于文章是否被修改过。指定依赖时设置dependency选项它可以是yii\caching\Dependency对象也可以是用于创建依赖对象的配置数组。下面的代码指定片段内容依赖updated_at列值的变化$dependency [ class yii\caching\DbDependency, sql SELECT MAX(updated_at) FROM post, ]; if ($this-beginCache($id, [dependency $dependency])) { // ...在这里生成内容... $this-endCache(); }当post表中任一文章的updated_at发生变化SELECT MAX(updated_at)的查询结果就会改变缓存随即失效。从源码看DbDependency在执行 SQL 时会临时关闭并恢复数据库的查询缓存enableQueryCache确保依赖检测读到的是最新数据并通过sqlparams绑定参数而Dependency基类提供了reusable静态缓存机制同一依赖在一次请求内可避免重复计算。依赖对象是在写入缓存时由配置数组经Yii::createObject()创建并随缓存数据一同存储的见FragmentCache::run()。variations缓存变体被缓存的内容可能随某些参数而发生变化。例如对于支持多语言的 Web 应用同一段视图代码可能生成不同语言的内容因此你可能希望缓存内容随当前应用语言而变化。设置缓存变体时使用variations选项它应是一个标量值数组每个值代表一个特定的变化因素。例如让缓存内容随语言变化if ($this-beginCache($id, [variations [Yii::$app-language]])) { // ...在这里生成内容... $this-endCache(); }从源码看FragmentCache::calculateKey()会把variations合并进缓存键array_merge([__CLASS__, $this-getId()], (array)$this-variations)。也就是说variations并不是对同一份内容做覆盖而是通过不同的缓存键天然生成多份独立缓存不同语言或不同 GET 参数、不同用户分组等互不干扰。数组型缓存键最终会由Cache::buildKey()序列化并 MD5 哈希后使用若键为 32 字符以内的纯字母数字字符串则直接使用因此可以放心把语言代码等值传入。enabled按条件开关缓存有时你只想在满足特定条件时启用片段缓存。例如对于展示表单的页面你只希望在表单初次请求GET 请求时缓存它而后续的 POST 请求显示不应被缓存因为表单中可能包含用户输入。为此可设置enabled选项if ($this-beginCache($id, [enabled Yii::$app-request-isGet])) { // ...在这里生成内容... $this-endCache(); }当enabled为false时内容生成逻辑每次都执行既不读缓存也不写缓存。源码中这一行为的实现是FragmentCache::init()中cache属性会在enabled为假时被置为null从而跳过缓存的读取与写入见FragmentCache::init()。嵌套缓存片段缓存可以嵌套即一个被缓存的片段可以包含在另一个同样被缓存的片段之中。例如评论被缓存在内层片段缓存中同时评论与文章内容一起被缓存在外层片段缓存中。下面展示两个片段缓存如何嵌套if ($this-beginCache($id1)) { // ...内容生成逻辑... if ($this-beginCache($id2, $options2)) { // ...内容生成逻辑... $this-endCache(); } // ...内容生成逻辑... $this-endCache(); }不同的缓存选项可以为嵌套缓存分别设置。例如内层缓存与外层缓存可以使用不同的duration值。嵌套缓存的失效行为有一个重要规律即使外层缓存中的数据失效内层缓存仍可能提供有效的内部片段反过来则不成立——如果外层缓存被判定为有效即使内层缓存内容已失效外层仍会继续提供同一份缓存副本。因此设置嵌套缓存的duration或dependency时必须谨慎否则过期的内部片段可能被长期保留在外层片段中。动态内容缓存中永不失效的部分使用片段缓存时你可能会遇到这种情况一个很大的片段内容大部分相对静态只有一两处是动态的。例如页面头部可能同时显示主菜单栏和当前用户名。另一个常见问题是被缓存的内容里可能包含必须每次请求都执行的 PHP 代码例如注册资源包的代码。这两个问题都可以通过所谓的动态内容功能解决。动态内容指的是输出中即使被包含在片段缓存内也永不缓存的部分。为了让它始终保持动态即使外层内容正从缓存中提供服务这部分也必须在每次请求时通过执行 PHP 代码生成。你可以在被缓存的片段内调用yii\base\View::renderDynamic()在期望的位置插入动态内容如下所示if ($this-beginCache($id1)) { // ...内容生成逻辑... echo $this-renderDynamic(return Yii::$app-user-identity-name;); // ...内容生成逻辑... $this-endCache(); }renderDynamic()方法接受一段 PHP 代码作为参数PHP 代码的返回值被当作动态内容。无论外层片段是否从缓存中提供这段 PHP 代码都会在每次请求时执行。动态内容的底层原理动态内容并非把 PHP 代码存进缓存、每次重新执行那么简单而是采用「占位符 回填」机制可以从源码中清晰看到写入阶段在缓存未命中、开始捕获输出时renderDynamic()检测到当前存在激活的内容缓存栈cacheStack于是生成形如![CDATA[YII-DYNAMIC-0]]的占位符替换动态内容并把「占位符 → PHP 代码」的映射记录到DynamicContentAwareTrait::addDynamicPlaceholder()中随后FragmentCache::run()把[$content, $placeholders]二元组存入缓存。读取阶段命中缓存后FragmentCache::getCachedContent()从缓存恢复内容与占位符映射再调用updateDynamicContent()——由于此时缓存栈已空处于最外层缓存占位符会被eval执行 PHP 代码后的真实结果通过strtr()替换见DynamicContentAwareTrait::updateDynamicContent()。多层嵌套场景updateDynamicContent()会检查getView()-getDynamicContents()是否为空只有最外层缓存才执行占位符替换从而保证嵌套缓存中的占位符能被正确逐层保留与回填。自 2.0.14 起动态内容 API 通过yii\base\DynamicContentAwareInterface接口及其yii\base\DynamicContentAwareTraittrait 对外暴露yii\widgets\FragmentCache类即实现了该接口并使用该 trait见FragmentCache.php页面缓存等其他缓存形式同样可以复用这套机制。源码视角FragmentCache 组件的工作流程综合以上分析一次完整的片段缓存生命周期如下对应 FragmentCache 源码View::beginCache($id, $options)以FragmentCache小部件形式启动init()中将cache属性解析为缓存组件实例若enabled为假则置null直接跳过后续步骤。若缓存中不存在有效内容getCachedContent()返回falseinit()会把该片段压入视图的cacheStack并开启输出缓冲ob_start()返回true让视图继续生成内容。视图内容生成完毕调用View::endCache()→FragmentCache::run()取出输出缓冲内容把依赖配置数组实例化为依赖对象将[$content, $placeholders]连同duration、dependency写入缓存组件最后回填动态内容并输出。后续请求再次命中缓存时getCachedContent()直接返回替换好动态内容的缓存副本beginCache()立即返回false。其中缓存键由calculateKey()统一生成删除缓存时按「片段 ID 变体」即可精确操作缓存组件本身支持前缀keyPrefix、序列化serializer等配置见Cache基类。测试验证仓库中 tests/framework/widgets/FragmentCacheTest.php 对片段缓存的核心行为提供了完整验证可作为理解本文内容的最佳对照testCacheEnabled第一次beginCache(test)返回true输出内容写入缓存第二次同一 ID 返回false且直接输出缓存的cached fragment验证了命中与未命中两种分支testCacheDisabled1/testCacheDisabled2验证enabled false时始终不读写缓存、内容每次重新生成testSingleDynamicFragment等动态内容用例验证renderDynamic()中的 PHP 代码在缓存命中后依然每次执行且计数自增结果正确回填到缓存内容中用例中Yii::$app-params[counter]在 42 次循环中逐次递增。这些测试同时确认了输出缓冲在启用/禁用缓存两种路径下都能被正确闭合不会产生缓冲区泄漏。使用建议综合文档与源码使用片段缓存时有几点值得注意优先缓存「重」片段适合把生成开销大、更新频率低的内容汇总报表、文章详情、菜单列表放入片段缓存避免为轻量输出引入额外的缓存读写开销合理组合依赖与有效期数据变更频繁的内容优先用dependency保证一致性变更缓慢的内容用duration控制过期即可多语言/多参数站点务必配置variations否则不同语言或不同筛选条件下的用户会共享同一份缓存内容嵌套缓存要警惕内层失效问题如文档所述外层缓存有效时内层已失效的片段仍会被整体输出需要统一协调内外层的duration与dependency动态内容用renderDynamic()把「必须每次执行」的代码当前用户信息、资源包注册放入renderDynamic()其余静态部分照常缓存。至此从基本用法、四大缓存选项、嵌套缓存到动态内容的完整片段缓存体系已经梳理完毕。你可以结合 数据缓存、页面缓存 与视图文档在项目中搭建起从数据层到视图层的完整缓存方案。【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表