Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

แอปพลิเคชั่น Rails ที่โรยด้วย JavaScript

การแคชแฟรกเมนต์ของ 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 แบบโรยสำหรับสิ่งนี้มีข้อดีสองประการ:

  1. หน้าสามารถแคชได้เพียงครั้งเดียวและนำกลับมาใช้ใหม่สำหรับผู้เยี่ยมชมที่ไม่ผ่านการตรวจสอบสิทธิ์ เช่นเดียวกับผู้ใช้รายอื่นโดยไม่ต้องแยกส่วนแคชสำหรับผู้ใช้ทุกคน
  2. เนื้อหาที่สำคัญที่สุดจะถูกโหลดก่อนเพื่อเวลาในการตอบกลับที่เร็วที่สุด และคุณลักษณะรอง เช่น จำนวนที่ยังไม่ได้อ่าน จะถูกโหลดในภายหลัง
  3. เนื่องจากคำขอพิเศษดำเนินการผ่าน 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