跳到內容 跳到搜尋

Action View 緩存輔助程式

命名空間
方法
C
U

執行個體公開方法

cache(name = {}, options = {}, &block)

此輔助程式公開一個方法,用於將檢視區段快取,而不是整個動作或頁面。此技術可用於快取類似選單、新主題清單、靜態 HTML 區段等區段。此方法採用一個區段,該區段包含您希望快取的內容。

使用的最理想方式為在 Memcached 或 Redis 等快取儲存的上方執行可回收金鑰快取到期,它會自動將舊的項目踢出。

在使用此方法時,您會將快取相關性列為快取的名稱,如下所示

<% cache project do %>
  <b>All the topics on this project</b>
  <%= render project.topics %>
<% end %>

這種方法會假設當新增新主題時,您會變更專案。從此呼叫產生的快取金鑰將類似於

views/template/action:7a1156131a6928cb0026877f8b749ac9/projects/123
      ^template path  ^template tree digest            ^class   ^id

此快取金鑰是穩定的,但它已與一個從專案記錄衍生的快取版本結合在一起。當專案更新 updated_at 時,cache_version 會變更,即使金鑰保持穩定。這表示,與傳統的金鑰快取到期方法不同的是,您不會產生快取垃圾、未使用的金鑰,這只是因為相關記錄已更新。

如果您的範本快取依賴於多個來源(請避免這樣做,以維持簡單性),您可以將所有這些相關性命名為陣列的一部分

<% cache [ project, current_user ] do %>
  <b>All the topics on this project</b>
  <%= render project.topics %>
<% end %>

這會將兩筆紀錄都包含為快取金鑰的一部分,而兩個金鑰任何一個更新都會使快取到期。

範本摘要

加入快取金鑰的範本摘要是透過建立整個範本檔案內容的 MD5 進行運算。這可確保在變更範本檔案時,您的快取會自動到期。

請注意,建立 MD5 是採用整個範本檔案,而不只是在快取 do/end 呼叫內的內容。因此,變更該呼叫以外的內容仍可能會使快取到期。

此外,摘要器會自動透過您的範本檔案尋找明確和隱含的相關性,並將它們包含為摘要的一部分。

摘要器可透過將 skip_digest: true 作為選項傳遞至快取呼叫,然後略過

<% cache project, skip_digest: true do %>
  <b>All the topics on this project</b>
  <%= render project.topics %>
<% end %>

隱含相關性

大多數的範本相關性都可以從範本本身在 render 中的呼叫中衍生。以下是快取摘要知道如何解碼的一些 render 呼叫範例

render partial: "comments/comment", collection: commentable.comments
render "comments/comments"
render 'comments/comments'
render('comments/comments')

render "header"        # translates to render("comments/header")

render(@topic)         # translates to render("topics/topic")
render(topics)         # translates to render("topics/topic")
render(message.topics) # translates to render("topics/topic")

雖然如此,無法衍生所有類似的 render 呼叫。以下是無法衍生的幾個範例

render group_of_attachments
render @project.documents.where(published: true).order('created_at')

您必須將這些改寫為明確形式

render partial: 'attachments/attachment', collection: group_of_attachments
render partial: 'documents/document', collection: @project.documents.where(published: true).order('created_at')

最後一種類型相關性可以隱含地決定

render "maintenance_tasks/runs/info/#{run.status}"

由於傳遞給 render 的值會結束內插,Action View 會將 “maintenance_tasks/runs/info” 資料夾中的所有部分標記為相關性。

明確相關性

有時您會遇到根本無法衍生的範本相關性。當您在輔助程式中有範本呈現時,通常會發生這種情況。以下是範例

<%= render_sortable_todolists @project.todolists %>

您需要使用特殊註解格式來呼叫它們

<%# Template Dependency: todolists/todolist %>
<%= render_sortable_todolists @project.todolists %>

在某些情況下,例如單一資料表繼承設定中,可能會有一堆明確的相依性。您可以使用萬用字元來比對目錄中的任何範本,無需寫出每個範本

<%# Template Dependency: events/* %>
<%= render_categorizable_events @person.events %>

這會將目錄中的每個範本標記為相依項。若要找到這些範本,必須從 app/views 絕對定義萬用字元路徑,或以 prepend_view_pathappend_view_path 新增其他路徑。這樣一來,app/views/recordings/events 的萬用字元就會變成 recordings/events/* 等。

用於比對明確相依項的模式為 /# Template Dependency: (\S+)/,因此務必完全照樣輸入。每行只能宣告一個範本相依性。

外部相依性

例如,如果您在快取區塊內使用輔助方法,然後更新該輔助方法,您也必須增加快取。執行方式並不要緊,但範本檔案的 MD5 一定要變更。一個建議是直接用註解說明,例如

<%# Helper Dependency Updated: May 6, 2012 at 6pm %>
<%= some_helper_method(person) %>

現在您只需在輔助方法變更時,變更該時間戳記即可。

集合快取

繪製每個物件使用相同偏好的物件集合時,可以傳遞一個 :cached 選項。

針對這樣繪製的集合

<%= render partial: 'projects/project', collection: @projects, cached: true %>

cached: true 會讓 Action View 的繪製一次從快取讀取多個範本,而非每個範本呼叫一次。

集合中尚未快取的範本會寫入快取。

和個別的範本片段快取十分搭配。例如,如果集合繪製的範本像這樣快取

# projects/_project.html.erb
<% cache project do %>
  <%# ... %>
<% end %>

任何集合繪製都會在嘗試一次讀取多個範本時,找到那些快取的範本。

如果您的集合快取取決於多個來源(為了簡化起見,請避免這樣做),您可以將所有這些相依性命名為回傳陣列的區塊的一部分

<%= render partial: 'projects/project', collection: @projects, cached: -> project { [ project, current_user ] } %>

這會將兩筆紀錄都包含為快取金鑰的一部分,而兩個金鑰任何一個更新都會使快取到期。

# File actionview/lib/action_view/helpers/cache_helper.rb, line 176
def cache(name = {}, options = {}, &block)
  if controller.respond_to?(:perform_caching) && controller.perform_caching
    CachingRegistry.track_caching do
      name_options = options.slice(:skip_digest)
      safe_concat(fragment_for(cache_fragment_name(name, **name_options), options, &block))
    end
  else
    yield
  end

  nil
end

cache_fragment_name(name = {}, skip_digest: nil, digest_path: nil)

這個輔助方法會回傳特定片段快取呼叫的快取金鑰名稱。透過在快取中提供 skip_digest: true,可以手動略過快取片段的消化作用。當快取片段無法手動過期,除非您知道確切金鑰(在使用快取記憶體的情況下),這項功能會很有用。

# File actionview/lib/action_view/helpers/cache_helper.rb, line 248
def cache_fragment_name(name = {}, skip_digest: nil, digest_path: nil)
  if skip_digest
    name
  else
    fragment_name_with_digest(name, digest_path)
  end
end

cache_if(condition, name = {}, options = {}, &block)

如果 condition 為真,快取檢視的片段

<% cache_if admin?, project do %>
  <b>All the topics on this project</b>
  <%= render project.topics %>
<% end %>
# File actionview/lib/action_view/helpers/cache_helper.rb, line 223
def cache_if(condition, name = {}, options = {}, &block)
  if condition
    cache(name, options, &block)
  else
    yield
  end

  nil
end

cache_unless(condition, name = {}, options = {}, &block)

除非 condition 為真,否則快取檢視的片段

<% cache_unless admin?, project do %>
  <b>All the topics on this project</b>
  <%= render project.topics %>
<% end %>
# File actionview/lib/action_view/helpers/cache_helper.rb, line 239
def cache_unless(condition, name = {}, options = {}, &block)
  cache_if !condition, name, options, &block
end

caching?()

回傳目前檢視片段是否在 cache 區塊中。

當某些片段無法快取時很有用

<% cache project do %>
  <% raise StandardError, "Caching private data!" if caching? %>
<% end %>
# File actionview/lib/action_view/helpers/cache_helper.rb, line 196
def caching?
  CachingRegistry.caching?
end

無法快取!()

快取區塊內呼叫時,會引發 UncacheableFragmentError

用於表示無法參與區塊快取的輔助函式方法

def project_name_with_time(project)
  uncacheable!
  "#{project.name} - #{Time.now}"
end

# Which will then raise if used within a +cache+ block:
<% cache project do %>
  <%= project_name_with_time(project) %>
<% end %>
# File actionview/lib/action_view/helpers/cache_helper.rb, line 213
def uncacheable!
  raise UncacheableFragmentError, "can't be fragment cached" if caching?
end