完全指南:從 beginCache 到動態(tài)內(nèi)容的源碼級剖析)
后端Web框架【免費下載鏈接】yii2Yii 2: The Fast, Secure and Professional PHP Framework項目地址https://gitcode.com/gh_mirrors/yi/yii2點擊查看免費下載片段緩存Fragment Caching是 Yii 2 在數(shù)據(jù)緩存之上提供的一項頁面級優(yōu)化能力用于把頁面中一段相對靜態(tài)的 HTML 輸出緩存起來從而消除每次請求都要重新生成這段內(nèi)容的開銷。本文將以docs/guide-pt-BR/caching-fragment.md與英文版docs/guide/caching-fragment.md同源為核心骨架結(jié)合 Yii 2 框架源碼與單元測試系統(tǒng)講解片段緩存的五種配置選項、嵌套緩存、動態(tài)內(nèi)容機制及其底層實現(xiàn)幫助你在真實項目里正確、高效地使用這一功能。什么是片段緩存片段緩存指的是緩存 Web 頁面中的一個片段。例如某頁面用一個表格展示年度銷售匯總你可以把這個表格整體緩存起來從而省去每次請求重新生成該表格的時間。正如文檔所說片段緩存構(gòu)建在數(shù)據(jù)緩存Data Caching之上——它的本質(zhì)仍然是向緩存組件寫入、讀取數(shù)據(jù)只是數(shù)據(jù)的內(nèi)容是一段 HTML 輸出。在 Yii 2 中片段緩存的使用場景集中在 視圖View中。其基本用法如下if ($this-beginCache($id)) { // ... 在這里生成內(nèi)容 ... $this-endCache(); }即在[[yii\base\View::beginCache()|beginCache()]]與[[yii\base\View::endCache()|endCache()]]之間包裹內(nèi)容生成邏輯如果緩存命中beginCache()會直接渲染緩存中的內(nèi)容并返回false從而跳過內(nèi)容生成邏輯如果緩存未命中內(nèi)容生成邏輯會被執(zhí)行當(dāng)調(diào)用endCache()時生成好的輸出會被捕獲并存入緩存。與數(shù)據(jù)緩存一樣每個片段都需要一個唯一的$id來標(biāo)識緩存條目。$id可以是字符串也可以是數(shù)組數(shù)組會作為緩存鍵的組成部分參與計算詳見后文緩存鍵的計算一節(jié)。刪除片段緩存英文版原文檔補充了刪除片段緩存的方法由于片段緩存最終存儲在數(shù)據(jù)緩存組件中且緩存鍵由類名與$id共同構(gòu)成因此可以通過Yii::$app-cache直接刪除Yii::$app-cache-delete([yii\widgets\FragmentCache, $id]);這一寫法與源碼中FragmentCache::calculateKey()的鍵構(gòu)造邏輯完全對應(yīng)見后文。緩存選項Caching OptionsbeginCache()的第二個參數(shù)可以傳入一個選項數(shù)組用于對片段緩存做精細(xì)化配置。從源碼看這個選項數(shù)組在底層會被用來配置一個實現(xiàn)了片段緩存實際功能的 widget——[[yii\widgets\FragmentCache]]。讓我們先看 FragmentCache 的源碼 中定義的公共屬性它們正是所有可用選項屬性默認(rèn)值作用cachecache緩存對象或緩存對象的應(yīng)用組件 ID自 2.0.2 起也支持配置數(shù)組duration60內(nèi)容在緩存中保持有效的秒數(shù)0表示永不過期dependencynull緩存內(nèi)容所依賴的Dependency對象或創(chuàng)建依賴對象的配置數(shù)組variationsnull導(dǎo)致緩存內(nèi)容變化的因素列表如語言、GET 參數(shù)enabledtrue是否啟用片段緩存可用于按條件開關(guān)緩存接下來逐一展開文檔重點講解的四個高頻選項。時長duration[[yii\widgets\FragmentCache::duration|duration]]是最常用的選項它指定內(nèi)容在緩存中保持有效的秒數(shù)。下面的代碼將片段內(nèi)容最多緩存一個小時if ($this-beginCache($id, [duration 3600])) { // ... 生成內(nèi)容 ... $this-endCache(); }如果該選項未設(shè)置默認(rèn)值為60秒對應(yīng)源碼 FragmentCache.php 中的public $duration 60;。另外根據(jù)源碼注釋設(shè)置為0時表示緩存的數(shù)據(jù)永不過期。這一選項最終會傳遞給底層緩存組件的set()調(diào)用$this-cache-set($this-calculateKey(), $data, $this-duration, $this-dependency);見 FragmentCache.php依賴dependencies與數(shù)據(jù)緩存的依賴機制類似被緩存的片段內(nèi)容也可以掛載依賴當(dāng)依賴發(fā)生變化時緩存內(nèi)容自動失效。例如一篇 post 的展示內(nèi)容依賴于它是否被修改過。要指定依賴設(shè)置[[yii\widgets\FragmentCache::dependency|dependency]]選項即可它可以是[[yii\caching\Dependency]]對象也可以是用于創(chuàng)建依賴對象的配置數(shù)組。下面的代碼指定片段內(nèi)容依賴post表中updated_at列的最大值即依賴最近一次帖子修改時間$dependency [ class yii\caching\DbDependency, sql SELECT MAX(updated_at) FROM post, ]; if ($this-beginCache($id, [dependency $dependency])) { // ... 生成內(nèi)容 ... $this-endCache(); }當(dāng)有帖子被修改時MAX(updated_at)變化緩存隨即失效下次請求會重新生成內(nèi)容。從源碼看配置數(shù)組會在寫入緩存前被實例化為依賴對象if (is_array($this-dependency)) { $this-dependency Yii::createObject($this-dependency); }見 FragmentCache.php除了DbDependencyYii 2 的yii\caching命名空間下還提供了FileDependency依賴文件修改時間、ExpressionDependency依賴 PHP 表達(dá)式結(jié)果、CallbackDependency依賴回調(diào)返回值等多種依賴類型具體可參考數(shù)據(jù)緩存文檔。變體variations緩存內(nèi)容可能隨某些參數(shù)而變化。例如對于支持多語言的 Web 應(yīng)用同一段視圖代碼會為不同語言生成不同內(nèi)容因此你可能希望緩存內(nèi)容隨當(dāng)前應(yīng)用語言而變化。設(shè)置[[yii\widgets\FragmentCache::variations|variations]]選項即可指定緩存變體它應(yīng)是標(biāo)量值的數(shù)組每個值代表一個特定的變化因子。下面的代碼讓緩存內(nèi)容隨語言變化if ($this-beginCache($id, [variations [Yii::$app-language]])) { // ... 生成內(nèi)容 ... $this-endCache(); }值得注意的是源碼中variations屬性聲明為string[]|string見 FragmentCache.php因此它也可以是單個字符串同時變體值會直接參與緩存鍵的計算詳見下文緩存鍵的計算。單元測試testVariations見 FragmentCacheTest.php驗證了variations為[ru]與[en]時會產(chǎn)生兩個互不干擾的緩存條目且無變體與有變體的條目彼此獨立。開關(guān)緩存Toggling Caching某些場景下你可能只想在特定條件滿足時才啟用片段緩存。例如一個展示表單的頁面只希望在首次請求GET 請求時緩存表單之后的展示POST 請求表單可能包含用戶提交的數(shù)據(jù)不應(yīng)被緩存。此時可以設(shè)置[[yii\widgets\FragmentCache::enabled|enabled]]選項if ($this-beginCache($id, [enabled Yii::$app-request-isGet])) { // ... 生成內(nèi)容 ... $this-endCache(); }從源碼看enabled直接決定了緩存組件的初始化當(dāng)enabled為false時FragmentCache::init()中$this-cache會被置為null從而跳過所有緩存讀寫邏輯見 FragmentCache.php內(nèi)容每次都實時生成也不會產(chǎn)生輸出緩沖開銷之外的副作用。緩存鍵的計算與刪除原理理解緩存鍵有助于正確管理緩存。在 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]);的依據(jù)——只要$id相同、沒有額外變體刪除時構(gòu)造同樣的數(shù)組鍵即可命中。反過來也說明如果寫入時帶了variations刪除時必須帶上相同的變體值否則無法定位到對應(yīng)緩存條目。嵌套緩存Nested Caching片段緩存可以嵌套使用即一個已緩存的片段可以包含在另一個同樣被緩存的片段中。例如評論被緩存在內(nèi)層片段里而評論連同 post 內(nèi)容一起被緩存在外層片段中。下面的代碼展示了兩個片段緩存如何嵌套if ($this-beginCache($id1)) { // ...內(nèi)容生成邏輯... if ($this-beginCache($id2, $options2)) { // ...內(nèi)容生成邏輯... $this-endCache(); } // ...內(nèi)容生成邏輯... $this-endCache(); }嵌套緩存可以為內(nèi)層與外層設(shè)置不同的緩存選項例如不同的過期時長。即使外層緩存的數(shù)據(jù)失效內(nèi)層緩存仍然可能提供有效的內(nèi)層片段反之則不成立——如果外層緩存被判定為有效即使內(nèi)層緩存內(nèi)容已失效外層仍會繼續(xù)提供同一份緩存副本。因此文檔特別提醒設(shè)置嵌套緩存的時長或依賴時必須小心否則過時的內(nèi)層片段可能被保留在外層片段中。從實現(xiàn)角度看外層片段的run()方法會捕獲整段輸出包括內(nèi)層緩存輸出的內(nèi)容一并存入緩存只有當(dāng)外層緩存失效重新生成時內(nèi)層緩存才會被重新評估。動態(tài)內(nèi)容Dynamic Content使用片段緩存時你可能會遇到這樣的場景一大段內(nèi)容相對靜態(tài)只有個別位置是變化的。例如頁面頭部除了主導(dǎo)航欄還要顯示當(dāng)前登錄用戶的用戶名。另一個典型問題是被緩存的內(nèi)容中可能包含每次請求都必須執(zhí)行的 PHP 代碼例如注冊 asset bundle 的代碼。這兩種問題都可以用 Yii 2 的動態(tài)內(nèi)容Dynamic Content特性解決。動態(tài)內(nèi)容是指即使被包裹在片段緩存中也不應(yīng)被緩存的一段輸出。要讓內(nèi)容始終保持動態(tài)它必須通過執(zhí)行 PHP 代碼在每次請求時重新生成哪怕外層內(nèi)容正從緩存中提供。在片段緩存內(nèi)部調(diào)用[[yii\base\View::renderDynamic()]]即可在指定位置插入動態(tài)內(nèi)容if ($this-beginCache($id1)) { // ...內(nèi)容生成邏輯... echo $this-renderDynamic(return Yii::$app-user-identity-name;); // ...內(nèi)容生成邏輯... $this-endCache(); }renderDynamic()接受一段 PHP 代碼作為參數(shù)該 PHP 代碼的返回值被視為動態(tài)內(nèi)容。無論外層片段是否命中緩存這段 PHP 代碼都會在每次請求時執(zhí)行。動態(tài)內(nèi)容的底層實現(xiàn)從 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); }關(guān)鍵流程如下當(dāng)存在活動的緩存棧cacheStack時renderDynamic()并不立即執(zhí)行 PHP 代碼而是生成一個形如![CDATA[YII-DYNAMIC-0]]的占位符并把占位符 → PHP 代碼的映射登記到動態(tài)內(nèi)容占位符列表中返回占位符本身內(nèi)容生成完畢后占位符連同靜態(tài) HTML 一起被存入緩存見 FragmentCache::run() 中$data [$content, $this-getDynamicPlaceholders()];在輸出階段DynamicContentAwareTrait::updateDynamicContent()見 DynamicContentAwareTrait.php會通過evaluateDynamicContent()內(nèi)部即eval執(zhí)行每段 PHP 代碼并用執(zhí)行結(jié)果替換占位符當(dāng)內(nèi)容從緩存恢復(fù)時getCachedContent()命中占位符同樣會被重新求值替換從而保證動態(tài)部分每次請求都新鮮。2.0.14 之后的動態(tài)內(nèi)容 API英文版原文檔補充說明自2.0.14起動態(tài)內(nèi)容 API 通過[[yii\base\DynamicContentAwareInterface]]接口和[[yii\base\DynamicContentAwareTrait]]trait 暴露FragmentCache類即為參考實現(xiàn)。接口定義了三個方法getDynamicPlaceholders()、setDynamicPlaceholders()、addDynamicPlaceholder()見 DynamicContentAwareInterface.phptrait 則提供了默認(rèn)實現(xiàn)以及占位符替換邏輯updateDynamicContent()見 DynamicContentAwareTrait.php。View通過pushDynamicContent()/popDynamicContent()維護(hù)一個動態(tài)內(nèi)容感知對象的緩存棧見 View.phpFragmentCache在init()中入棧、在run()中出棧從而支持多層嵌套緩存中動態(tài)內(nèi)容的正確歸屬與替換。通過單元測試驗證行為倉庫中的 FragmentCacheTest.php 使用ArrayCache作為緩存組件覆蓋了上述絕大多數(shù)特性可以作為理解行為的最佳參考testCacheEnabled第一次beginCache(test)返回true未命中生成并緩存內(nèi)容第二次beginCache(test)返回false命中直接輸出緩存內(nèi)容testCacheDisabled1/testCacheDisabled2enabled false時始終實時生成、不寫入緩存且已存在的緩存條目不會被禁用狀態(tài)下的讀取所影響testSingleDynamicFragment循環(huán) 42 次首次未命中其余全部命中但renderDynamic()返回的計數(shù)器每次請求都遞增證明動態(tài)部分未被緩存testMultipleDynamicFragments同一片段內(nèi)多個renderDynamic()調(diào)用都能在緩存恢復(fù)后正確求值替換testNestedDynamicFragments外層緩存命中時內(nèi)層緩存與外層動態(tài)內(nèi)容均能正確渲染testVariations驗證不同variations產(chǎn)生獨立緩存條目字符串形式與數(shù)組形式均可。使用建議與注意事項結(jié)合原文檔與源碼在使用片段緩存時有幾點值得注意緩存組件先行片段緩存依賴cache應(yīng)用組件可通過FragmentCache::$cache選項替換為其他緩存對象或組件 ID。關(guān)于緩存組件的配置方式如 memcached、文件緩存、數(shù)據(jù)庫緩存等參見數(shù)據(jù)緩存文檔與應(yīng)用組件文檔。合理設(shè)置時長與依賴靜態(tài)性強的片段如年度報表表格適合較長duration依賴數(shù)據(jù)庫變化的片段應(yīng)優(yōu)先使用DbDependency等依賴而非單純依賴過期時間。變體鍵要一致讀寫以及刪除時$id與variations必須保持一致否則會產(chǎn)生緩存鍵錯位。嵌套緩存注意失效傳播外層緩存有效時內(nèi)層失效內(nèi)容仍會被外層保留嵌套層級的時長/依賴需要通盤考慮。動態(tài)內(nèi)容只放必要部分renderDynamic()中的代碼每次請求都會執(zhí)行濫用會削弱緩存收益只把真正需要實時刷新的部分如當(dāng)前用戶名、CSRF token、asset 注冊放入動態(tài)內(nèi)容。片段緩存是 Yii 2 在數(shù)據(jù)緩存之上提供的高性價比優(yōu)化手段在視圖層用一對beginCache()/endCache()調(diào)用即可獲得整段 HTML 的緩存能力配合duration、dependency、variations、enabled四個選項與動態(tài)內(nèi)容機制可以精細(xì)地平衡緩存命中率與內(nèi)容新鮮度是構(gòu)建高性能 Yii 2 應(yīng)用時值得優(yōu)先掌握的技能之一。贊分享后端Web框架【免費下載鏈接】yii2Yii 2: The Fast, Secure and Professional PHP Framework項目地址https://gitcode.com/gh_mirrors/yi/yii2點擊查看免費下載相關(guān)推薦Yii 2 片段緩存Fragment Caching實戰(zhàn)指南從 beginCache 到動態(tài)內(nèi)容Yii 2 片段緩存Fragment Caching實戰(zhàn)指南從 beginCache 到動態(tài)內(nèi)容 片段緩存Fragment Caching是 Yii后端Web框架Yii 2 片段緩存Fragment Caching實戰(zhàn)指南從 beginCache 到動態(tài)內(nèi)容的全鏈路解析Yii 2 片段緩存Fragment Caching實戰(zhàn)指南從 beginCache 到動態(tài)內(nèi)容的全鏈路解析 片段緩存Fragment Caching后端Web框架Yii 2 片段緩存Fragment Caching完全指南從視圖片段緩存到動態(tài)內(nèi)容Yii 2 片段緩存Fragment Caching完全指南從視圖片段緩存到動態(tài)內(nèi)容 片段緩存Fragment Caching是 Yii 2 提供的頁后端Web框架創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考