การแคชแฟรกเมนต์ของ Rails ช่วยเพิ่มความเร็วได้มากขึ้นเมื่อแคชส่วนใหญ่ของเพจถูกแคชไว้ สิ่งนี้ยากกว่าสำหรับหน้าที่มีเนื้อหาแบบไดนามิกหรือเฉพาะผู้ใช้จำนวนมาก วิธีแก้ไขคือใช้ “JavaScript sprinkles” ซึ่งเหมือนกับ hagelslag แต่ไม่มีช็อกโกแลตและมีคำขอพิเศษให้โหลดเนื้อหาเฉพาะผู้ใช้หลังจากที่หน้าที่เหลือได้รับบริการโดยตรงจากแคช
การแคชแฟรกเมนต์
เทคนิคต่างๆ เช่น การแคชแฟรกเมนต์ใช้เพื่อเร่งการตอบสนองในแอปพลิเคชัน Rails โดยการแคชแฟรกเมนต์ของหน้าที่แสดงผล เมื่อใช้คีย์แคชอัจฉริยะ แฟรกเมนต์จะใช้งานไม่ได้โดยอัตโนมัติเมื่อเนื้อหาเปลี่ยนแปลง เนื่องจากข้อมูลที่แสดงในมุมมองได้รับการอัปเดต
แฟรกเมนต์มุมมองแคชช่วยเพิ่มความเร็วที่มากขึ้นจากแคชที่มีขนาดเล็กลง เมื่อใช้แฟรกเมนต์ที่แคชบ่อยขึ้น เมื่อเนื้อหาของหน้าขึ้นอยู่กับผู้ใช้ที่ลงชื่อเข้าใช้อยู่ในปัจจุบัน เช่น การแคชส่วนย่อยของหน้าจะยากขึ้น
การตอบกลับที่ยังไม่ได้อ่าน
ตัวอย่างเช่น ลองใช้บล็อกที่ใช้ Rails ในบล็อกนี้ แต่ละบทความสามารถมีคำตอบได้หลายแบบ เพิ่มระบบตรวจสอบสิทธิ์เพื่อให้ผู้ใช้ลงชื่อเข้าใช้และโพสต์คำตอบด้วยบัญชีของตนได้ ในหน้าบทความแต่ละหน้า เราจะแสดงรายการคำตอบ เพื่อให้ผู้ใช้ของเราง่ายขึ้น เราจึงทำเครื่องหมายคำตอบใหม่ด้วยไอคอนและสีพื้นหลังที่แตกต่างกัน
ถ้าเราห่อแต่ละบทความใน cache
บล็อก เราเสี่ยงต่อการทำเครื่องหมายคำตอบที่ไม่ถูกต้องว่ายังไม่ได้อ่าน เมื่อผู้ใช้ร้องขอหน้าดัชนี การตอบสนองจะถูกแคชไว้ เมื่อผู้ใช้รายอื่นร้องขอหน้าเดียวกันในภายหลัง พวกเขาจะได้รับส่วนย่อยที่แคชไว้ พร้อมคำตอบที่ยังไม่ได้อ่านของผู้ใช้คนแรก
# app/views/articles/show.html.erb
<%= cache(@article) do %>
<h1><%= @article.title %></h1>
<%= simple_format(@article.content) %>
<section id="responses">
<h2>Responses</h2>
<% @article.responses.each do |response| %>
<div class="<%= response.read_by?(@current_user) ? 'read' : 'unread' %>">
<%= time_tag(response.created_at) %>
<strong><%= response.name %></strong>: <%= response.content %>
</div>
<% end %>
</section>
<% end %>
วิธีแก้ไขคือการเพิ่มผู้ใช้ที่ลงชื่อเข้าใช้ในแคชคีย์โดยใช้ [@article, @current_user]
แทนที่จะเป็นแค่ @article
เมื่ออาร์กิวเมนต์ส่งผ่านไปยัง cache
วิธีการช่วยเหลือ
# app/views/articles/show.html.erb
<%= cache([@article, @current_user]) do %>
<h1><%= @article.title %></h1>
# ...
<% end %>
เพื่อให้แน่ใจว่าผู้ใช้แต่ละรายเห็นการตอบสนองที่ยังไม่ได้อ่านของตนเอง แต่จะลบล้างการเร่งความเร็วส่วนใหญ่จากการแคชแฟรกเมนต์ เนื่องจากตอนนี้แฟรกเมนต์บทความถูกแคชแยกกันสำหรับผู้ใช้แต่ละราย
JavaScript โรย
เนื่องจากหน้าส่วนใหญ่เหมือนกันสำหรับผู้ใช้ทั้งหมด เราจึงต้องการใช้ส่วนย่อยของบทความที่แคชซ้ำสำหรับผู้เยี่ยมชมแต่ละราย ในการทำเช่นนี้ เราสามารถโหลดบทความจากแคชและเพิ่มคำขอพิเศษเพื่อดึงเนื้อหาเฉพาะผู้ใช้หลังจากที่โหลดหน้าเว็บผ่านคำขอ JavaScript การใช้ฟังก์ชัน JavaScript แบบโรยสำหรับสิ่งนี้มีข้อดีสองประการ:
- หน้าสามารถแคชได้เพียงครั้งเดียวและนำกลับมาใช้ใหม่สำหรับผู้เยี่ยมชมที่ไม่ผ่านการตรวจสอบสิทธิ์ เช่นเดียวกับผู้ใช้รายอื่นโดยไม่ต้องแยกส่วนแคชสำหรับผู้ใช้ทุกคน
- เนื้อหาที่สำคัญที่สุดจะถูกโหลดก่อนเพื่อเวลาในการตอบกลับที่เร็วที่สุด และคุณลักษณะรอง เช่น จำนวนที่ยังไม่ได้อ่าน จะถูกโหลดในภายหลัง
- เนื่องจากคำขอพิเศษดำเนินการผ่าน JavaScript ทั้งหน้าจึงสามารถแคชขอบบน CDN เพื่อปรับปรุงประสิทธิภาพต่อไปได้
ล้างข้อมูล
ก่อนอื่นเราจะลบเนื้อหาแบบไดนามิกออกจากหน้าเว็บของเราเพื่อให้แคชง่ายขึ้น เราจะลบ @current_user
จากคีย์แคชใน cache
บล็อกอีกครั้ง ดังนั้นจึงไม่มีการแคชสำหรับผู้ใช้แต่ละรายอีกต่อไป จากนั้น เราจะลบการสืบค้นที่พบจำนวนที่ยังไม่ได้อ่านออกจากคอนโทรลเลอร์ และลบชื่อคลาส CSS ออกจากมุมมอง
# app/views/articles/show.html.erb
<%= cache(@article) do %>
<h1><%= @article.title %></h1>
<%= simple_format(@article.content) %>
<section id="responses">
<h2>Responses</h2>
<% @article.responses.each do |response| %>
<div data-response-id="<%= response.id %>">
<%= time_tag(response.updated_at) %>
<strong><%= response.name %></strong>: <%= response.content %>
</div>
<% end %>
</section>
<% end %>
เราเหลือหน้าทั่วไปที่แคชง่ายกว่า แต่ไม่มีฟีเจอร์ตอบกลับที่ยังไม่ได้อ่าน มาเพิ่มกลับกันเถอะ
จุดสิ้นสุด
ก่อนอื่นเราจะสร้างปลายทางเพื่อค้นหาคำตอบที่ยังไม่ได้อ่านสำหรับผู้ใช้ เราต้องการเปลี่ยนแปลงสถานะปัจจุบันของหน้าหลังจากที่แสดงผลแล้ว ดังนั้นเราจะขอ JSON จากหน้านั้น
# app/controllers/unread_responses_controller.rb
class UnreadResponsesController < ApplicationController
def index
@article = Article.find(params[:article_id])
@responses = @article.unread_responses_for(@current_user)
end
end
# app/views/unread_responses/index.json.jbuilder
json.array! @responses do |response|
json.extract! response, :id
end
# config/routes.rb
Rails.application.routes.draw do
resources :articles do
resources :responses
resources :unread_responses
end
end
ปลายทางของเราจะจัดทำรายการรหัสตอบกลับที่ยังไม่ได้อ่าน
# GET /articles/1/unread_responses.json
[{"id":1},{"id":2},{"id":3}]
เคล็ดลับ :เมื่อโหลดองค์ประกอบไดนามิกที่สามารถแสดงผลล่วงหน้าบนเซิร์ฟเวอร์ มักจะเร็วกว่าในการเรนเดอร์ HTML บนฝั่งเซิร์ฟเวอร์ จากนั้นแทรก HTML ลงในเพจของคุณโดยตรงผ่าน JavaScript
แสดงการตอบกลับที่ยังไม่ได้อ่าน
แทนที่จะฮาร์ดโค้ด URL สำหรับปลายทางการตอบกลับที่ยังไม่ได้อ่านในโค้ด JavaScript เราจะเพิ่มลงในแอตทริบิวต์ข้อมูลในมุมมอง เพื่อให้เราสามารถอ้างอิงได้ในภายหลัง
# app/views/articles/show.html.erb
<section id="responses" data-url="<%= article_unread_responses_path(@article, json: true) %>">
# ...
</section>
หลังจากที่หน้าโหลดเสร็จแล้ว เราจะขอรหัสตอบกลับที่ยังไม่ได้อ่านจากปลายทางใหม่ของเรา จากนั้นเราจะใช้ข้อมูลจากรายการนั้นเพื่อทำเครื่องหมายแต่ละบทความว่ายังไม่ได้อ่านโดยเพิ่มคลาส CSS ลงไป
// app/assets/javascripts/application.js
document.addEventListener("turbolinks:load", function () {
responses = document.getElementById("responses");
if (!responses.dataset.loaded) {
Rails.ajax({
url: responses.dataset.url,
type: "GET",
success: function (data) {
responses.dataset.loaded = true;
data.forEach(function (response) {
element = document.querySelector(
"[data-response-id='" + response.id + "']"
);
element.classList.add("unread");
});
},
});
}
});
เนื่องจากแอปพลิเคชัน Rails ของเราใช้ Turbolinks เราจะรอให้หน้าโหลดโดยการฟัง turbolinks:load
เหตุการณ์. เมื่อเหตุการณ์นั้นเริ่มทำงาน เราจะพบช่องตอบกลับโดยใช้รหัส
จากนั้นเราจะตรวจสอบว่าองค์ประกอบการตอบสนองมี loaded
แอตทริบิวต์ข้อมูล เราจะตั้งค่าแอตทริบิวต์นี้หลังจากอัปเดตการตอบกลับที่ยังไม่ได้อ่านในภายหลัง ดังนั้นเราจึงไม่ส่งคำขอเพิ่มเติมในกรณีที่หน้าถูกโหลดซ้ำโดยใช้ปุ่มย้อนกลับของเบราว์เซอร์
ในการโหลดครั้งแรก loaded
ยังไม่ได้ตั้งค่าแอตทริบิวต์ ดังนั้นเราจะทำการร้องขอไปยังปลายทางของเราต่อไป เมื่อประสบความสำเร็จ เราจะวนรอบบทความแต่ละบทความในผลลัพธ์ที่ส่งคืน ค้นหาองค์ประกอบของการตอบกลับตามรหัส และเพิ่มคลาส CSS ที่ "ยังไม่ได้อ่าน" ลงไป
โรย!
Fragment แคชเนื้อหาที่ใช้ซ้ำได้และโปรย JavaScript ลงบนหน้าเว็บของคุณเพื่อเพิ่มบิตไดนามิกเฉพาะผู้ใช้ในภายหลัง สามารถทำให้เวลาตอบสนองเริ่มต้นของแอปเร็วขึ้นโดยให้บริการเนื้อหาที่สำคัญส่วนใหญ่ส่งตรงจากแคช แม้ว่าจะต้องใช้เวลาในการร้องขอเพิ่มเติมและอัปเดตหน้าหลังจากโหลดเนื้อหาหลักแล้ว การเลื่อนเนื้อหาแบบไดนามิกจะทำให้แอปของคุณรู้สึกกระฉับกระเฉงขึ้น แม้ว่าคำขอแบบเต็มจะใช้เวลานานกว่าการให้บริการทุกอย่าง รวมถึงส่วนเฉพาะของผู้ใช้ตั้งแต่ แคช
สำหรับการตั้งค่าที่ซับซ้อนยิ่งขึ้น อย่าลืมตรวจสอบ Stimulus ซึ่งเป็นไลบรารี JavaScript ที่รวมรูปแบบการโรยเป็นเฟรมเวิร์กที่เชื่อมโยงมุมมอง HTML ของคุณกับ JavaScript
เราหวังว่าคุณจะชอบการแนะนำ JavaScript Sprinkle ในแอปพลิเคชัน Rails หากคุณมีความคิดเห็นเกี่ยวกับบทความนี้ บล็อกหรือหัวข้ออื่น ๆ ที่คุณต้องการกล่าวถึง โปรดส่งอีเมลหาเราที่ @AppSignal