Svelte join forces with Rails and Turbo
- stimulus
เริ่มต้นมันเกิดจากที่ว่าอยากทดลองใช้งาน Svelte ดู แต่ก็อยากใช้ Rails เป็น server แทนที่จะใช้ SvelteKit เลยต้องหาแนวทางที่จะทำงานร่วมกัน
ด้วยที่ปกติใช้ Stimulus ก็เลยได้ไอเดียของการ register คอนโทรลเลอร์ที่ใช้งานเก็บไว้ และถ้าจะใช้งานคอนโทรลเลอร์ตัวไหนกับ HTML ใดก็จะผูกกันด้วย data-attributes
ดังนั้นเราจึงได้พัฒนา Registry ง่ายๆ ขึ้นมาทดสอบกัน
// app/javscript/application.js
import "@hotwired/turbo-rails"
import { Registry } from "./svelte/registry"
import App from './components/App.svelte'
import About from './components/About.svelte'
const registry = new Registry()
registry.start()
registry.register("main", App)
registry.register("about", About)
สำหรับหน้าที่ของ Registry จะทำหน้าที่เก็บชุดข้อมูลชื่อ และคอมโพเนนต์ของ Svelte และเมื่อโค้ดสั่งให้ทำงานจะทำการค้นหา data-attributes ที่ตรงตามเงื่อนไข
ซึ่งเราได้กำหนดให้เป็น data-svelte-component={name}
และทำการ render คอมโพเนนต์เข้าไปยัง element นั้น
จากโค้ดข้างต้นจะเห็นว่า Registry ได้ลงทะเบียนคอมโพเนนต์ 2 ตัวคือ
- App ซึ่งจะทำการผูกกับชื่อ main
- About ซึ่งจะทำการผูกกับชื่อ about
เพื่อความสะดวกในการใช้งาน เราจะได้สร้าง tag helper ที่ชื่อ svelte_component
ขึ้นมาตอนใช้งานด้วย ทำให้เวลาที่ต้องการ render คอมโพเนนต์บนหน้า HTML
จะเรียกผ่าน svelte_component
ดังแสดงในตัวอย่างด้านล่าง
module SvelteHelper
def svelte_component(identifier, props = {}, tag_name: "div")
data_attr = { svelte_component: identifier }
data_attr = data_attr.merge(svelte_props: props) unless props.empty?
tag.__send__(tag_name, data: data_attr)
end
end
ActionView::Base.extend SvelteHelper
<!-- app/views/home/index.md -->
<main class="w-full md:w-3/5 lg:w-2/5 mx-auto">
<%= link_to "about", :about, class: "inline-flex rounded-full border items-center h-9 mt-6 px-3" %>
<%= svelte_component "main" %>
</main>
ทดสอบการทำงาน
Home
<script>
import { onMount } from "svelte"
import { get } from "@rails/request.js"
let books = []
async function getBooks () {
const response = await get('http://localhost:3000/api/books.json', { responseKind: "json" })
if (response.ok) {
return response.json
}
return null
}
onMount(async () => {
books = await getBooks()
})
</script>
<h1 class="font-bold text-3xl pt-8">Books</h1>
{#if books}
<ul class="bg-slate-50 p-4 my-4 text-sm leading-6">
{#each books as book}
<li>
<a href={book.url} class="group flex rounded-md m-2 p-3 bg-white ring-1 ring-slate-200 shadow-sm">
...
</a>
</li>
{/each}
</ul>
{/if}
HTML ที่ถูกสร้างจาก Svelte
About
ทั้งนี้ Registry ยังรองรับการทำงานร่วมกับ Turbo อีกด้วย ทำให้เวลาที่มีการเปลี่ยนหน้าตัว browser จะไม่ต้องโหลด resource ใหม่ทั้งหมด