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

การใช้ Hotwire กับ Rails

หากคุณกำลังมองหาวิธีเพิ่มความเร็วในการเปลี่ยนหน้าและการส่งแบบฟอร์ม และแบ่งหน้าที่ซับซ้อนออกเป็นส่วนประกอบโดยไม่ต้องเขียนโค้ด JavaScript ใดๆ โพสต์นี้จะช่วยให้คุณนำ Rails ไปสู่อีกระดับด้วย Hotwire บทความนี้จะสอนวิธีใช้เครื่องมือสำหรับการเรนเดอร์ฝั่งเซิร์ฟเวอร์

Hotwire คืออะไร

Hotwire มีวิธีสร้างเว็บแอปพลิเคชันที่ทันสมัยโดยไม่ต้องเขียน JavaScript โดยส่ง HTML แทน JSON ผ่านสาย ซึ่งทำให้โหลดหน้าเว็บได้เร็วขึ้น มันรักษาการเรนเดอร์บนฝั่งเซิร์ฟเวอร์และช่วยให้ได้รับประสบการณ์การพัฒนาที่เรียบง่ายและมีประสิทธิภาพมากขึ้น เนื่องจาก Rails ได้รวมเอาเทคโนโลยีของมันไว้เสมอ โดยไม่ลดความเร็วหรือการตอบสนองที่เกี่ยวข้องกับแอปพลิเคชันหน้าเดียว (SPA) แบบดั้งเดิม

แก่นของ Hotwire คืออัญมณีเทอร์โบ เป็นชุดของเทคนิคเสริมที่เร่งความเร็วการนำทางของหน้าและการส่งแบบฟอร์ม แบ่งหน้าที่ซับซ้อนออกเป็นส่วนประกอบ และส่งการอัปเดตบางส่วนของหน้าผ่าน WebSocket (ซึ่งประกอบด้วย ActionCable ช่อง และข้อมูลการสตรีม)

ทำไมคุณจึงควรใช้

หากคุณมีปัญหากับ JavaScript และต้องการใช้ประโยชน์จากประสบการณ์ผู้ใช้ที่ดีขึ้นผ่านความรู้สึกของการนำทางระหว่างหน้าต่างๆ ได้เร็วขึ้นมาก Hotwire เป็นทางเลือกสำหรับคุณ

Hotwire ทำงานอย่างไร? Hotwire ใช้การเรนเดอร์ฝั่งเซิร์ฟเวอร์ (SSR) เพื่อแก้ปัญหาบางอย่างที่เกี่ยวข้องกับ SPA ในขณะที่ยังคงข้อได้เปรียบหลัก SSR ย้อนกลับกระบวนการเรนเดอร์ โดยนำส่วนหนึ่งของความพยายามในการเรนเดอร์ SPA ไปยังเซิร์ฟเวอร์ คล้ายกับการโหลดแบบดั้งเดิม SSR สามารถให้ผู้ใช้โหลดแอปพลิเคชันได้อย่างมีประสิทธิภาพมากขึ้น เนื่องจากมีการเรนเดอร์บางส่วนบนเซิร์ฟเวอร์ นอกจากความเป็นไปได้ในการปรับปรุงประสิทธิภาพแล้ว ยังช่วยจัดการกับปัญหา SEO บางอย่าง เช่น การจัดทำดัชนี

ใช้งานง่ายหรือไม่

ใช้งานง่ายมาก คุณเพียงแค่ต้องใช้แพ็คเกจเริ่มต้นของโปรเจ็กต์ Rails (Ruby, RoR, ActionCable และ WebSocket) ซึ่งเป็น Turbo gem ที่ดาวน์โหลดการพึ่งพา JS ทั้งหมด และ Redis เพื่อจัดเก็บข้อมูลชั่วคราวขณะเรียกดู WebSocket

คุณไม่จำเป็นต้องเรียนรู้ภาษาอื่น (JS) เพื่อให้มีความเร็วเท่ากับเว็บแอปพลิเคชันหน้าเดียวเพราะ Turbo ได้รับการเสริมด้วย Turbo Drive, Frames, Streams และ Native ขับเคลื่อนลิงก์และแบบฟอร์มให้เร็วขึ้น และลดความจำเป็นในการโหลดเครือข่ายซ้ำ ในขณะที่ Frames แบ่งเครือข่ายออกเป็นบริบทอิสระที่โหลดได้ง่ายขึ้น

ใช้กับ Rails อย่างไร

ส่วนนี้จะแสดงตัวอย่างทีละขั้นตอน

สำหรับตัวอย่างนี้ เราต้องการ:

  • ทับทิม
  • รูบี้ ออน เรลส์
  • เรดิส
  • SQLite (ฐานข้อมูลเริ่มต้น)
  • อัญมณี ฮอตไวร์
  • TurboRails
  • StimulusJS
  • WebSocket
  • ActionCable

เราจะสร้างโครงการอะไร

เราจะสร้างโครงการโซเชียลมีเดีย

ขั้นแรก เปิดเทอร์มินัลของคุณ เริ่มต้นด้วยโครงการรางใหม่:

rails new social-media

เข้าสู่โครงการของคุณ:

cd social-media

กำลังเพิ่ม Hotwire

เพิ่ม Hotwire gem ในโครงการของคุณ:

bundle add hotwire-rails

หรือเปิด Gemfile . ของคุณ และเพิ่มสิ่งนี้:

gem "hotwire-rails"

หากคุณใช้ตัวเลือกที่สอง คุณจะต้องเรียกใช้บันเดิลทันที

bundle install

จากนั้นจึงค่อยติดตั้ง

rails hotwire:install

การตั้งค่าเริ่มต้น

ตอนนี้เป็นเวลาที่ดีในการวิเคราะห์การตั้งค่าเริ่มต้นที่ Hotwire ทำ เมื่อไปที่ Gemfile ควรมีลักษณะดังนี้:

gem 'redis', '~> 4.0'

เหตุใดเราจึงต้องใช้ Redis

เพิ่ม Redis gem เนื่องจาก ActionCable ต้องการให้เก็บข้อมูลชั่วคราวขณะเรียกดู WebSocket อย่างไรก็ตาม เพียงแค่ติดตั้ง Redis นั้นไม่เพียงพอต่อการใช้งาน เราจำเป็นต้องตรวจสอบว่ามีการกำหนดค่าอย่างถูกต้องหรือไม่ เมื่อคุณไปที่ config/cable.yml ไฟล์ควรมีลักษณะดังนี้:

development:
 adapter: redis
 redis://localhost:6379/1

ตรวจสอบให้แน่ใจว่า Redis ทำงานเมื่อคุณเริ่มแอปพลิเคชันของคุณ (redis-server )

การอ้างอิง JS

ตรวจสอบการขึ้นต่อกันบน package.json :

dependencies: {
 @hotwired/turbo-rails: ^7.0.0-beta.5,
 @rails/actioncable: ^6.0.0,
 @rails/activestorage: ^6.0.0,
 @rails/ujs: ^6.0.0,
 @rails/webpacker: 4.3.0,
 stimulus: ^2.0.0
}

กำลังสร้างโมเดลของเรา

หลังจากตรวจสอบไฟล์ทั้งหมดแล้ว เราจะสร้าง views , controllers , models และ migrations สำหรับ posts ตารางที่มี body และ likes คอลัมน์ ในการดำเนินการนี้ ให้เรียกใช้สิ่งต่อไปนี้ในเทอร์มินัล:

rails g scaffold posts body:text likes:integer

กำลังสร้างฐานข้อมูลของเรา

ตอนนี้เรามีทุกอย่างที่สร้างขึ้นแล้ว เราจำเป็นต้องส่งการเปลี่ยนแปลงเหล่านี้ไปยังฐานข้อมูล ดังนั้นให้รันสิ่งต่อไปนี้ในเทอร์มินัล:

rails db:create db:migrate

หากทุกอย่างเป็นไปด้วยดี เราสามารถเรียกใช้เซิร์ฟเวอร์ได้ (rails server ) และตรวจสอบว่าทุกอย่างเรียบร้อยดีหรือไม่ ในการดำเนินการดังกล่าว เราจะเปิดเซิร์ฟเวอร์แล้วไปที่ posts ซึ่งจะเป็น https://localhost:3000/posts :

การใช้ Hotwire กับ Rails หน้าจอพิมพ์ของไดเรกทอรีโพสต์

แสดงรายการโพสต์

ตอนนี้เราจะแสดงรายการโพสต์ ในการทำเช่นนั้น เราจะสร้าง app/views/posts/_post.html.erb ไฟล์และเพิ่มรหัสต่อไปนี้ลงไป:

<div style="background: lightgrey; width: 300px; padding: 10px;">
  <%= post.body %>
  <br>
  <%= link_to :edit, edit_post_path(post) %>
  <%= button_to "likes (#{post.likes || 0})", post_path(post, like: true), method: :put %>
</div>
<br>

การตรวจสอบความถูกต้อง

เราจำเป็นต้องตรวจสอบฟิลด์เนื้อหา (ซึ่งไม่สามารถเป็นโมฆะได้) และเราจะบอกให้การออกอากาศแสดงทวีตบนหน้าจอเดียวกับทวีตแรกหลังจากสร้าง ในการดำเนินการนี้ เราจะแก้ไขไฟล์ app/models/post.rb และใส่รหัสดังต่อไปนี้:

class Post < ApplicationRecord
  validates_presence_of :body

  after_create_commit { broadcast_prepend_to :posts }
end

การสั่งซื้อ

เราต้องสั่งโพสต์ของเรา ดังนั้นเปิดคอนโทรลเลอร์ของคุณ (app/controllers/posts_controller.rb )

...
def index
  @posts = Post.all.order(created_at: :desc)
  @post = Post.new
end
...

สรุปผล

ตอนนี้ มาแก้ไข index . ของเรา (app/views/posts/index.html.erb ) เพื่อแสดง new post หน้าและรายการ posts . ทั้งหมด .

<%= turbo_stream_from :posts %>

<%= turbo_frame_tag :post_form do %>
  <%= render 'posts/form', post: @post %>
<% end %>

<%= turbo_frame_tag :posts do %>
  <%= render @posts %>
<% end %>

กำลังเปลี่ยนเส้นทางไปยังดัชนี

สุดท้าย มาแก้ไข create วิธีการในตัวควบคุมของเรา เพื่อหลีกเลี่ยงการเปลี่ยนเส้นทางเราไปที่ show post ให้ทุกอย่างอยู่ในหน้าเดียวกัน

...
def create
  @post = Post.new(post_params)

  respond_to do |format|
  if @post.save
   format.html { redirect_to posts_path }
   format.json { render :show, status: :created, location: @post }
 else
    format.turbo_stream { render turbo_stream: turbo_stream.replace(@post, partial: 'posts/form', locals: { post: @post }) }
    format.html { render :new, status: :unprocessable_entity }
   format.json { render json: @post.errors, status: :unprocessable_entity }
 end
  end
end
...

หน้าสุดท้ายของเราควรมีลักษณะดังนี้:

การใช้ Hotwire กับ Rails หน้าจอพิมพ์ของหน้าสุดท้าย

กำลังตรวจสอบบันทึก

เมื่อมีการสร้างโพสต์ ให้ตรวจสอบบันทึกเทอร์มินัลของคุณ ควรมีลักษณะดังนี้:

[ActionCable] Broadcasting to posts: "<turbo-stream action=\"prepend\" target=\"posts\"><template><div style=\"background: lightgrey; width: 300px; padding: 10px;\">\n  first post\n  <br>\n  <a href=\"/posts/1/edit\">edit</a>\n  <form class=\"button_to\" method=\"post\" action=\"/posts/4?like=true\"><input type=\"hidden\" name=\"_method\" value=\"put\" /><input type=\"submit\" value=\"likes (0)\" /><input type=\"hidden\" name=\"authenticity_token\" value=\"<token>==\" /></form>\n</div>\n<br>\n</template></turbo-stream>"

ความคิด

หมายความว่า ActionCable ทำงานกับ turbo stream ที่มีประสิทธิภาพสูง ดังที่คุณเห็น:Completed 302 Found in 18ms .

กำลังปรับใช้

เราต้องการไฟล์สั่งการอื่นเพื่อทำงานควบคู่ไปกับเซิร์ฟเวอร์ Rails ของเราหรือไม่

ไม่ เพราะ ActionCable ทำงานได้ดีกับเซิร์ฟเวอร์ยอดนิยม เช่น Unicorn, Puma และ Passenger

Heroku รองรับ Hotwire หรือไม่

ใช่ Heroku รองรับฐานของ Hotwire (websockets) หากคุณมีคำถามเพิ่มเติม คุณสามารถตรวจสอบเอกสารอย่างเป็นทางการของ Heroku ได้

ในกรณีที่คุณสงสัยว่าเหตุใดเราจึงใช้ Redis มีอีกเหตุผลหนึ่ง

หากใช้ Heroku โดยไม่มี Redis ข้อความจะไม่ถูกส่งไปยังทุกคนเมื่อแอปของคุณถูกปรับขนาดเป็นมากกว่าหนึ่งไดโน เนื่องจากแอปปัจจุบันไม่มีสถานะ ไคลเอ็นต์ที่เชื่อมต่อกับไดโนตัวแรกจะไม่ได้รับข้อความที่ส่งโดยไคลเอ็นต์ที่เชื่อมต่อ สู่ไดโนตัวที่สอง Redis แก้ปัญหานี้ด้วยการจัดเก็บสถานะข้อความในที่จัดเก็บข้อมูลส่วนกลาง

ตั้งค่า Redis บน Heroku

ในการสร้างเซสชัน Redis CLI กับอินสแตนซ์ Redis ระยะไกลของคุณ ให้ใช้ heroku redis:cli .หากคุณไม่ระบุอินสแตนซ์ อินสแตนซ์จะอยู่ที่ REDIS_URL ถูกใช้โดยค่าเริ่มต้น หากคุณมีมากกว่าหนึ่งอินสแตนซ์ ให้ระบุอินสแตนซ์ที่คุณต้องการเชื่อมต่อ

มันทำงานอย่างไรบนแอปพลิเคชันเซิร์ฟเวอร์

เราจำเป็นต้องตั้งค่าไดโนแต่ละตัวเพื่อใช้ระบบย่อย Redis ไดโนทั้งหมดจะสมัครรับข้อมูลจากแชนเนลเดียวกัน (ค่าเริ่มต้น) และรอข้อความ เมื่อแต่ละเซิร์ฟเวอร์ได้รับข้อความ จะสามารถเผยแพร่ไปยังไคลเอนต์ที่เชื่อมต่อได้ เราจะพูดถึง subscribe ในเธรดแยกต่างหาก เช่น subscribe เป็นฟังก์ชันการบล็อก ดังนั้นการรอข้อความจะหยุดขั้นตอนการดำเนินการ นอกจากนี้ จำเป็นต้องมีการเชื่อมต่อ Redis ครั้งที่สอง เนื่องจากเมื่อมีการสร้างคำสั่ง subscribe บนการเชื่อมต่อ การเชื่อมต่อจะสามารถยกเลิกการสมัครรับข่าวสารหรือรับข้อความเท่านั้น เรียนรู้เพิ่มเติมเกี่ยวกับการปรับขนาดที่นี่

ความปลอดภัย

ขณะนี้ แอปพลิเคชันมีความเสี่ยงต่อการโจมตีจำนวนมาก โปรดตั้งค่า WSS และล้างข้อมูลที่คุณป้อน เรียนรู้เพิ่มเติมเกี่ยวกับ WebSocket Security ที่นี่

บทสรุป

คุณสามารถใช้ Hotwire ผ่านแอปพลิเคชันใหม่ดังที่แสดงไว้ที่นี่ แต่สามารถอัปเดตจาก Turbolinks ได้

บทความนี้น่าจะช่วยให้คุณเข้าใจว่า "กรอบงานวิเศษ" ทำงานอย่างไร ในทางปฏิบัติ คุณมีความเป็นไปได้ในการพัฒนาแอปพลิเคชันง่ายๆ ขนาดเล็กที่แสดงคำขอผ่านสตรีมเทอร์โบรวมที่มีการจัดการและการนำทางผ่าน WebSocket