LiveView phù hợp nhất với Svelte
(blog.sequin.io)- Sau khi đội ngũ Sequin đã dùng cả React SPA lẫn Phoenix LiveView thuần, họ kết luận rằng cách kết hợp LiveView với Svelte bằng LiveSvelte là năng suất nhất
- LiveView có thể tạo ra UI phong phú mà không cần SPA nhờ trạng thái trên server và cập nhật DOM gia tăng, nhưng khi cần trạng thái phía client thì module JS, hook và trạng thái LiveView bắt đầu trộn lẫn, làm ranh giới trở nên mơ hồ
- Trong LiveView thuần, LiveView, LiveComponent, Component có vòng đời, cách truyền props và phương thức giao tiếp khác nhau, khiến việc refactor và bố trí trạng thái bất tiện hơn React
- LiveSvelte cung cấp một ranh giới hai chiều rõ ràng: Elixir đẩy props xuống, còn Svelte gửi sự kiện ngược lên bằng
live.pushEvent - Với tổ hợp này, routing, business logic và chuẩn bị dữ liệu nằm ở server, còn trạng thái UI, animation và render có điều kiện do Svelte đảm nhiệm, giúp giảm độ phức tạp kiểu SPA
Mô hình phát triển mà LiveView mang lại
- Ứng dụng server-rendering truyền thống thì server không giữ trạng thái, và render trang cho mỗi request từ client
- SPA để client đảm nhiệm cấu trúc trang, đọc ghi dữ liệu qua backend API, và quản lý trạng thái phía client theo cách như
useStatecủa React - LiveView vẫn để server tiếp tục đảm nhiệm việc render trang nhưng duy trì trạng thái, rồi sau khi xử lý action từ frontend sẽ cập nhật DOM theo kiểu gia tăng giống SPA
- Độ phức tạp của SPA tăng lên vì ứng dụng JS phía client trên thực tế hoạt động như một microservice frontend
- Các yêu cầu riêng cho frontend làm số lượng route và controller ở backend tăng lên
- Một request có thể đi qua
onMount, gọi API, parse response, router, auth plug, controller, hàm domain và tầng chuẩn bị response
- Điểm mạnh của LiveView là có thể tạo trải nghiệm client phong phú mà không cần microservice frontend
- Có thể truy vấn cơ sở dữ liệu ngay gần hàm render bảng
- Khi có hàng mới, có thể push vào bảng và LiveView sẽ cập nhật client
- Nó cho phép tạo ra tương tác gần với framework frontend có trạng thái hơn là kiểu server-rendering truyền thống vốn phải dựng lại toàn bộ trạng thái ở mỗi request
Những ma sát gặp phải với LiveView thuần
-
Không thể tránh trạng thái phía client
- LiveView không thể tránh được độ trễ khứ hồi giữa server và người dùng
- Những việc như animation, tooltip, hiện/ẩn DOM, vô hiệu hóa field trong form đôi lúc buộc phải xử lý ở client
- Với một form có hai dropdown phụ thuộc lẫn nhau, UX tốt sẽ là ngay sau khi chọn ở dropdown thứ nhất, dropdown thứ hai bị vô hiệu hóa ngay, rồi được bật lại sau khi server điền danh sách mới
- Để triển khai hành vi này chỉ bằng LiveView thì phải kết hợp nhiều khái niệm
- Khi JS, hook và trạng thái LiveView cùng được dùng, một phần trạng thái DOM sẽ tồn tại bên ngoài LiveView, khiến rất khó xây dựng một pattern nhất quán
-
Ranh giới trạng thái khác với React
- React xử lý hầu hết chuyển đổi bằng các khái niệm cốt lõi là trạng thái và action, nên ranh giới giữa trạng thái DOM và trạng thái component tương đối rõ ràng
- Vì không có độ trễ giữa action phía client và trạng thái phía client, React có thể xử lý action và transition theo mô hình trạng thái
- Trạng thái của LiveView nằm ở phía server, nên phải tính đến độ trễ giữa action từ client và trạng thái trên server
- Ở ví dụ field nhập liệu, React đi qua đường trạng thái trước khi ký tự được nhập, và component được render lại sau mỗi lần gõ
- LiveView gần với mô hình người dùng đổi field nhập trước, rồi một lúc ngắn sau trạng thái server mới biết và phản ứng
- Khi độ trễ gần như bằng không thì nó trông giống React, nhưng độ trễ càng lớn thì sự khác biệt giữa hai mô hình càng rõ
Độ phức tạp do ba loại component tạo ra
- LiveView có ba loại component là LiveViews, LiveComponents, Components
- LiveViews và LiveComponents gần với component có trạng thái trong React, còn Components gần với functional component hơn
- LiveView luôn là component cha cấp cao nhất, bên dưới nó là LiveComponents và Components được render
- Trong React, có thể chuyển đổi khá dễ giữa component có trạng thái và functional component bằng cách thêm hoặc bỏ hook
useState- Cả hai nhận props theo cùng một cách
- Ngoài trạng thái ra thì bộ tính năng là như nhau
- Cách đăng ký và phản hồi DOM event cũng giống nhau
- Trong LiveView, khác biệt giữa ba loại component lớn đến mức việc refactor từ LiveView sang LiveComponent trở nên bất tiện
- LiveViews và LiveComponents có cú pháp render và truyền props khác nhau
- Vòng đời khác nhau
- Các lựa chọn giao tiếp khác nhau; LiveViews dùng
send, còn LiveComponents dùngsend_update - LiveComponents không phải process nên không thể tương tác với các phần khác của hệ thống như LiveViews
Giới hạn của LiveComponent
- LiveView là một process Elixir, nên có
pid, trạng thái và inbox, và có thể hoạt động như các process khác trong hệ thống Elixir/OTP - Trong LiveView có thể dùng pub/sub để subscribe các thay đổi trên toàn hệ thống
- LiveComponent không phải process riêng mà là module do LiveView gọi
- Process LiveView cha giữ trạng thái của các component con, nên LiveView còn phải xử lý cả việc định tuyến message cho các LiveComponents con
- Nếu LiveComponents muốn có khả năng quản lý trạng thái và xử lý action độc lập thì mỗi cái phải là một process, nhưng mô hình hiện tại không như vậy
- Trong phát triển thực tế, kể cả khi muốn gửi event hoặc action đến LiveComponent cũng không có cách nào thật sự tốt, và rất khó phân định
send_updatenên dùng để truyền action hay vá trạng thái- Nếu dùng để vá trạng thái thì còn phải cân nhắc cách phân biệt mount và update trong clause
update
- Nếu dùng để vá trạng thái thì còn phải cân nhắc cách phân biệt mount và update trong clause
Ranh giới mà LiveSvelte tạo ra
- LiveSvelte cho phép render component Svelte trong LiveView
- Trong
rendercủa LiveView, có thể mã hóa cấu trúc dữ liệu Elixir thành plain map cho frontend rồi truyền làm props cho component Svelte - Component Svelte nhận props bằng
export let, và giao tiếp với LiveView qua proplivedo LiveSvelte cung cấp - Ở phía Svelte, có thể gửi event lên server bằng
live.pushEvent- Trong ví dụ, reactive block của Svelte gửi event
"form_updated"lên server khiformthay đổi - LiveView giải mã tham số từ frontend trong
handle_event("form_updated", ...)rồi cập nhật changeset - Các lỗi validate của changeset lại được gửi về frontend qua prop
errors
- Trong ví dụ, reactive block của Svelte gửi event
- Dữ liệu đi từ Elixir xuống Svelte qua props, còn từ Svelte đi lên LiveView qua websocket event
- Process LiveView có thể cập nhật props bất cứ lúc nào để khiến component Svelte được render lại
Các thuộc tính cốt lõi của tổ hợp LiveView và Svelte
- Điểm cốt lõi của tổ hợp này là process backend có trạng thái và component frontend có trạng thái cùng hoạt động
- Có ba thuộc tính nâng đỡ cho tổ hợp này
- Backend kiểm soát props của component frontend
- Cả frontend lẫn backend đều có trạng thái
- Hai phía có một kênh giao tiếp hai chiều riêng tư, nơi cả hai đều có thể chủ động khởi tạo message
- Thuộc tính đầu tiên có được nhờ cách LiveView render
- Kết quả rerender từ server được tự động push sang client và áp dụng
- Server có thể cập nhật props của component như một component cha trong JS
- Thuộc tính thứ hai đến từ việc LiveView là process Elixir
- Process Elixir là cách đóng gói trạng thái
- Thuộc tính thứ ba có được nhờ kết nối websocket bền vững do LiveView cung cấp
Khác biệt với SPA
- Toàn bộ routing trong trình duyệt đều do backend xử lý nên cấu trúc đơn giản hơn
- Trong SPA thông thường, phải duy trì hai loại route là route trình duyệt và route API
- Backend có trạng thái và biết route hiện tại cũng như resource đang được thao tác
- Mỗi action có thể được xử lý như thay đổi trạng thái của chính backend, thay vì phải dựng lại trạng thái từ đầu
- Giao tiếp giữa frontend và backend là riêng tư và được ghép chặt
- Không cần tăng số lượng route công khai trên server chỉ để phục vụ các lệnh RPC cho một component đơn lẻ
- Khi nhìn vào
pushEventtừ client, có thể biết handler tương ứng nằm trong module Elixir đang phối hợp với nó
- Tính năng chủ yếu được chia vào hai file
- Module backend gọi các hàm backend như truy vấn cơ sở dữ liệu
- Frontend import component và style
- Vòng giao tiếp frontend-backend không phải đi qua stack API module, router, controller
- Quy trình giao tiếp cũng được rút gọn
- Backend có thể báo thay đổi cho frontend chỉ bằng cập nhật props
- Frontend có thể gửi
pushEventmà không cần token hết hạn, timeout hay handler xử lý lỗi - Nếu websocket mở thì server đang hoạt động; nếu đóng thì LiveView sẽ hiển thị banner “disconnected” toàn cục
Phân tách trách nhiệm và cấu trúc mã
- Trong cấu trúc này, nó vận hành như thể microservice frontend đã bị loại bỏ
- Giữ business logic ở backend
- Cách nạp dữ liệu
- Dữ liệu nào cần lấy
- Cách sắp xếp và lọc
- Validator
- Mã frontend Svelte trở nên đơn giản hơn
- Các block
if/endđể render có điều kiện - Animation
- Các hàm
pushEventđơn giản gửi lên server
- Các block
- Ở frontend SPA thông thường, rất dễ có nhiều logic
map,reduce,filterđể xử lý dữ liệu từ server, chuẩn bị dữ liệu để hiển thị và chuẩn bị dữ liệu để gửi ngược lên server - Trong ứng dụng LiveSvelte, các xử lý này có thể được thực hiện ở phía server
- LiveView chuẩn bị dữ liệu theo đúng dạng component Svelte cần
- Độ phức tạp được giữ trong ngôn ngữ server, cấu trúc dữ liệu server và bộ test phía server
- LiveView và component Svelte không giống hai phần tử kết nối lỏng lẻo, mà gần như là hai nửa của cùng một tính năng
- LiveView chỉ render component Svelte đó
- Và component Svelte đó cũng chỉ được render bởi LiveView tương ứng
Điều thay đổi so với LiveView thuần
- Cách làm bằng LiveSvelte chấp nhận trạng thái và chuyển đổi trạng thái ở frontend
- Tầng ranh giới giữa frontend và backend trở nên rõ ràng hơn
- Có thể tận dụng mô hình component đã trưởng thành của Svelte
- Cũng có thể dùng hệ sinh thái framework JavaScript mà với LiveView thuần khó tiếp cận
- Svelte cung cấp các animation primitives
- Khi chuyển thêm trách nhiệm sang frontend, cảm giác bị mắc ở vùng trung gian gượng gạo sẽ giảm đi
Lý do chọn Svelte và vị trí của LiveView
- Lý do chọn LiveSvelte là vì chưa có thư viện LiveView nào cho React đạt mức hoàn thiện tương tự
- Svelte được xem là framework frontend nhẹ và nhanh với trạng thái mặc định và tính phản ứng tốt
- Vì LiveView đảm nhận phần lớn quản lý trạng thái, phần trạng thái bên Svelte vẫn được giữ đơn giản
- Khả năng template của Svelte cũng được ưa thích hơn React
- Có thể dùng
if/elsethay vì toán tử ba ngôi - Có thể dùng thiết lập thuộc tính có điều kiện
- Có thể dùng
- runes của Svelte 5 được kỳ vọng sẽ khiến Svelte dễ học và dễ suy luận hơn nữa
- LiveView tỏa sáng nhất khi đóng vai trò backend-for-frontend: render component frontend, thực hiện cập nhật gia tăng, duy trì process backend có trạng thái và cung cấp API websocket
1 bình luận
Ý kiến trên Hacker News
Một pattern thường dùng trong game multiplayer về cơ bản là chạy cùng một đoạn code ở cả client lẫn server
Code phía client dự đoán trạng thái server và chạy trước; khi trạng thái server đến thì ghi đè lên trạng thái client
Trong game, client có thể đoán khá tốt kết quả input của chính nó, nhưng không biết input của người chơi khác, nên từ “dự đoán” rất phù hợp
Tuy nhiên cách này cũng có thể dùng để phản hồi ngay trong lúc chờ trạng thái server chính thức, như bật/tắt dropdown hay loading spinner
Cũng có nhiều trạng thái client không cần chạy trên server, như particle system hay ragdoll; nếu có kết nối server liên tục thì có vẻ không có lý do gì để không làm được trong mô hình reactive
Đây là bản clone của https://generals.io/ và được làm bằng LiveSvelte
Thực ra nó gần như là board game và chỉ 2 tick mỗi giây nên không cần tính năng dự đoán, nhưng có dùng optimistic update để hiển thị trạng thái game trước khi server cập nhật
Nếu không đồng bộ với server, server sẽ ghi đè trạng thái game
Toàn bộ logic game nằm trong Elixir; để dự đoán đúng nghĩa thì server và client phải chia sẻ logic
Nếu không thì sẽ phải viết logic hai lần, và đó là con đường dẫn tới thảm họa
Tôi chưa tìm hiểu kỹ, nhưng một giải pháp khả thi là viết toàn bộ logic game bằng gleam(https://gleam.run/)
Gleam tương thích với Elixir và cũng có thể compile sang JavaScript, nên về lý thuyết có thể chạy cùng một code ở server và client
Tuy vậy, nói “cứ viết hết bằng js là được mà” cũng khá đúng, và có lẽ tôi cũng đồng ý
Dù sao lợi ích nhận được là có thể dùng BEAM và các tính năng tốt cho hệ thống phân tán thời gian thực; nếu bỏ qua phần frontend thì nó rất hợp với game multiplayer
Khi ai đó chết và cái xác bay đi hoặc kẹt ở một tư thế buồn cười, không ai khác thấy được, và trong replay cũng không xem lại được vì mỗi client render khác nhau
Nếu không ghi lại bằng capture thời gian thực thì nó biến mất mãi mãi
Với một game đôi khi rất hài hước như Overwatch, thật tiếc khi không ai có thể cùng thấy những khoảnh khắc đó
https://arstechnica.com/gaming/2019/10/explaining-how-fighti... là một bài giải thích rất hay, và các thảo luận liên quan nằm ở https://news.ycombinator.com/item?id=34399790 và https://news.ycombinator.com/item?id=26289933
Nếu có tính bền vững trạng thái phía server như MMO, cũng phải tính đến nút thắt I/O
https://prdeving.wordpress.com/2023/09/29/mmo-architecture-s... là một bài viết thú vị từ góc nhìn đó, và phần thảo luận ở https://news.ycombinator.com/item?id=37702632
Ví dụ, một form live/interactive trông như hoạt động tốt khi cập nhật dữ liệu phái sinh trên trang, nhưng nếu sau đó mọi thứ bị rollback vì lỗi server hoặc sự cố mạng thì sẽ rắc rối
Tệ hơn nữa là người dùng có thể không biết điều đó và đóng trang hoặc chuyển sang trang khác
Tôi thích tổ hợp LiveView + Svelte
Tôi đã trình bày cách kết hợp hai thứ này tại ElixirConf 2022, nhưng chính các contributor của live_svelte mới là những người biến nó thành thứ thực sự dùng được
Tôi cho rằng các app có trải nghiệm người dùng phong phú luôn cần trạng thái phía client
Đặc biệt khi sống ở New York, kết nối mạng khi đang di chuyển không phải là điều hiển nhiên
Một tính năng mạnh mẽ mà bài viết chưa đề cập là có thể dùng pubsub của Phoenix để push một cách reactive tới mọi client cả những thay đổi trạng thái phía server phát sinh từ server khác
Khi xử lý lưu lượng từ mức trung bình trở lên, việc có nhiều web server là khá phổ biến
Nếu muốn trải nghiệm người dùng kiểu LiveView nhưng vẫn chịu được mạng chập chờn, tôi đã thêm binding Svelte 5 cho Triplit[1]
Bạn có thể viết query phía client và đồng bộ với server qua websocket
Với developer Elixir thì có thể không hấp dẫn lắm, nhưng nếu Svelte là trung tâm thì Triplit là một lựa chọn tốt
[1] https://www.triplit.dev/docs/frameworks/svelte
Khi có hàng mới xuất hiện, cách cứ đẩy thẳng vào bảng và để LiveView cập nhật client là điều nên tránh trong các ứng dụng nghiệp vụ có thể tương tác với các hàng đó
Do độ trễ nhận thức, người dùng rất dễ bấm nhầm mục, gửi email cho nhầm khách hàng, hoặc hoàn tiền nhầm giao dịch
Trải nghiệm người dùng được ưa chuộng là một banner cố định kiểu “Dữ liệu đã thay đổi. Bấm vào đây để làm mới”
Nếu gấp, nên render hàng mới theo kiểu chỉ thêm vào và bảo đảm vị trí cuộn không bị thay đổi
Đang dùng Svelte cùng LiveView trong BeaconCMS
Chắc chắn có những trường hợp sử dụng tốt khi cần kiểm soát UI phía client tinh vi hơn, nhưng cần cẩn trọng nếu cả đội muốn áp dụng toàn diện Svelte + LiveView cho mọi thứ
Ngay cả trong Phoenix, LiveView cũng không phải lúc nào là câu trả lời, đôi khi trang render thông thường là đủ
Không nên chọn mọi thứ theo kiểu được ăn cả ngã về không
Như bài viết nói, có những trường hợp sử dụng tốt để thoát khỏi “cách làm LiveView”
Nếu thời gian khứ hồi là 1.000ms thì có lẽ nên cân nhắc thứ khác, nhưng cũng có những đội vì lý do như chi phí mà không thể đặt server gần về mặt địa lý, nên quản lý state phía client có thể là lời giải
Ý là thay vì quản lý state phía client thì sẽ quản lý state ở cả client lẫn server sao?
Dù có lợi thế là không cần tạo thêm một API nữa, nhưng trông không giống một cải tiến
Luôn có state mà client phải theo dõi và state mà server phải theo dõi, rồi còn chồng lên đó là state tạm thời chỉ cần nằm trong bộ nhớ và state cần được duy trì qua các phiên
Có gần như vô hạn cách để quản lý chuyện này, và trong JavaScript thì “làm gì cũng chẳng có đáp án đúng”, nên cuối cùng hoặc dùng cách framework quy định, hoặc xử lý bằng giải pháp tạm bợ, tức là hỗn loạn
Trong ứng dụng single-page phức tạp gần nhất từng viết, mỗi lần client tải trang đều cho thực hiện đồng bộ hóa
Mọi state hoặc asset cục bộ đều có hash dựa trên ngày tháng, và nếu khác với giá trị trên server thì server trả xuống phiên bản đã cập nhật
Dù là biến đơn giản, một object JSON khổng lồ, hay toàn bộ blob hình ảnh/âm thanh cũng vậy
Khi client làm việc gì cần thay đổi state trên server, nó gửi cập nhật state đó qua WebSocket, và 99% ứng dụng dựa trên WebSocket
Không dùng framework hay pattern đặc biệt nào; trong lúc viết code, nếu thấy “cái này server phải theo dõi” thì gửi state mới cho server bằng message, còn việc có đồng bộ khi tải trang hay không thì để server quyết định
Với kiểu quản lý state này, tôi xem đây gần như là cơ chế đơn giản nhất
WebSocket thật sự giúp ích rất nhiều cho quản lý state
Ứng dụng thực tế cần state ở cả client lẫn server
Server không thể tin client nên phải có state có thẩm quyền, nhưng client cũng phải có thể phản hồi tương tác người dùng và render lại mà không cần một vòng khứ hồi
Đừng làm vậy với tất cả, chỉ dùng khi hợp lý
Một điểm nữa tôi thích là có thể dùng Svelte như ngôn ngữ template thay cho Heex
Nếu không cần thì không có lý do để làm; lý do khiến nó trở nên cần thiết là hiệu năng và trải nghiệm người dùng
Nếu ứng dụng web có nhiều tương tác người dùng nên cần JavaScript phía client, và có nhiều thử nghiệm khiến bundle khác nhau theo từng request, bạn sẽ muốn chia logic giữa server và client để giảm lượng JavaScript gửi xuống client
Nếu không, ứng dụng sẽ có bundle JavaScript cỡ MB chứa mọi trường hợp có thể gặp
Với ứng dụng như bản web của Photoshop, nơi người dùng hiểu thời gian tải ban đầu, điều đó có thể ổn, nhưng với ứng dụng như Stripe hay Gmail, nơi người dùng kỳ vọng tải ngay lập tức, bạn sẽ muốn giảm độ trễ ban đầu của trang
Cũng có thể chuyển toàn bộ lên server, nhưng như GitHub đã trải qua, các thao tác người dùng cần phản hồi tức thì lại phải đi một vòng tới server, làm tính tương tác của trang tệ đi
Khi đó tốc độ ánh sáng và việc phân tán server trở thành vấn đề
Cũng có thể trì hoãn tải bundle bằng những thứ như async import, nhưng JavaScript được tải trễ lại gửi thêm request để lấy dữ liệu của chính nó, tạo ra vấn đề thác nước
Khi gặp tất cả những vấn đề này, cuối cùng bạn sẽ tìm một giải pháp giúp xử lý dễ hơn bài toán phức tạp về chia sẻ logic/state giữa client và server
Cách tiếp cận này có đúng nghĩa giới hạn tốc độ ánh sáng trong câu “server có thể ở gần người dùng đến đâu cũng có giới hạn”
Bước tiếp theo là compile server thành WebAssembly rồi gửi xuống client
Khi đó có thể render phản hồi một cách lạc quan trong lúc chờ phản hồi thật từ server
Nghe có vẻ hơi điên, nhưng tôi đã làm được trong dự án thực tế và nó giống như phép thuật
Chạy server trên client cũng không giải quyết được chuyện đó, và cuối cùng độ trễ mạng của persistence vẫn còn nguyên
State phía server rốt cuộc phải thắng cũng chính là phần persistence đó
Rất nhiều over-engineering vẫn thường được triển khai trong thực tế dù chẳng có lý do gì đặc biệt
Tôi đã tạo LiveSvelte, nên nếu có gì tò mò thì cứ cho tôi biết
Nhìn chung, tôi luôn muốn xây dựng ứng dụng theo mô hình như thế này
Kiểu như hướng sự kiện, cập nhật thời gian thực hai chiều với server, sự kiện có thứ tự, trạng thái cục bộ/từ xa
Tôi không biết LiveView và cũng chưa từng dùng ngôn ngữ họ Erlang, nhưng rõ ràng có vẻ nó đang nắm bắt được điều gì đó
Mô hình request-response truyền thống thường âm thầm gây ra các vấn đề về tính nhất quán và dữ liệu cũ
Nếu 10 năm qua là thời kỳ tích hợp các khái niệm lập trình hàm vào các ngôn ngữ chủ lưu, thì hy vọng 10 năm tới sẽ đi theo hướng tích hợp lập trình hướng thông điệp có trạng thái vào full-stack chủ lưu
Nhân tiện, ba cái đầu là do tôi tạo ra hoặc đã hỗ trợ tạo ra
Làm theo hướng dẫn và thử nghịch một chút, bạn có thể dựng được một ứng dụng chat Phoenix cơ bản chạy trong trình duyệt trong khoảng hai giờ
Đây là một giải pháp tốt
Trong app của tôi, tôi dùng các Stimulus controller có thể tái sử dụng cùng với LiveView, và việc này cũng hoạt động mượt mà
Nhìn chung phát triển với LiveView rất thú vị, nhưng càng dùng nhiều trong các kịch bản thực tế, tôi càng cảm nhận được ưu điểm của các framework HTTP phi trạng thái như Hotwire
Cảm giác hiệu năng tốt hơn, chống chịu reconnect tốt hơn, và cũng giảm nhu cầu đặt thêm server gần người dùng để đảm bảo độ ổn định
Nhưng Stimulus không cung cấp một cách thanh lịch để duy trì trạng thái ở client
Nó kiểu như “trạng thái của ứng dụng Stimulus tồn tại dưới dạng thuộc tính của DOM”, nên không tốt hơn vanilla JS hay jQuery
Tôi chưa từng dùng Stimulus trong dự án thực tế, nên cũng có khả năng values và callback khi thay đổi mang lại trải nghiệm tốt hơn tôi nghĩ ban đầu
Tổ hợp này có mức độ phức tạp vừa đúng, còn Svelte khi dùng cùng LiveView thì có cảm giác xử lý cả nhiều thứ vốn dĩ không phải là vấn đề
Thứ cần là vanilla JS hoặc một lớp mỏng bên trên nó, chứ không phải cả một framework
Dù sao cũng không cần dùng nhiều JavaScript
JS trong app đang chạy production của tôi không quá 200 dòng, và có lẽ còn có thể bỏ vài Stimulus controller
LiveView tốt đến mức đó
Tôi cũng đã làm một adapter hook Stimulus-LiveView cực kỳ hacky, để Stimulus controller có thể gửi event trực tiếp tới process LiveView
Và LiveView, nếu bạn không uống quá nhiều Kool-Aid kiểu fly.io, thì không nhất thiết đòi hỏi server phân tán về mặt địa lý
Nó cũng xử lý mất kết nối rất tốt
Tôi cũng không phải làm gì riêng để hỗ trợ cập nhật không downtime
Toàn bộ quá trình client mất kết nối WebSocket, reconnect lên phiên bản mới và khôi phục trạng thái đều được xử lý tự động theo mặc định
Còn mong gì hơn nữa?
Công ty chúng tôi đã thu được lợi ích lớn khi dùng LiveView cùng Alpine, đặc biệt là dùng chế độ CSP vì không thể cho phép
evalở clientKhi lần đầu thấy LiveSvelte, nó trông rất mới và chưa được kiểm chứng nhiều, và cũng có những tác động chưa được giải quyết trong môi trường CSP nghiêm ngặt
Giờ có vẻ nó đã khá hữu ích, thật đáng mừng
Pattern của chúng tôi có thể tạm gọi là LiveAlpine
Nếu component cần HTML thì dùng HTML Component, nếu có trạng thái phía server thì dùng Live Component
Nếu cần hành vi hoặc trạng thái phía client thì cũng định nghĩa Alpine component
Nếu cần nhận event từ client ở server hoặc thực hiện HTTP request thì cũng định nghĩa Phoenix Hook