2 điểm bởi GN⁺ 2024-04-05 | 1 bình luận | Chia sẻ qua WhatsApp
  • 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ư useState củ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
      • Dùng JS module của LiveView để vô hiệu hóa dropdown thứ hai khi dropdown thứ nhất thay đổi
      • Dùng hook để đăng ký event listener cho dropdown thứ hai, rồi gửi action bật lại từ backend
      • Nếu cần bật lại theo điều kiện thì còn có thể phải dính cả trạng thái LiveView
    • 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ùng send_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_update nê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

Ranh giới mà LiveSvelte tạo ra

  • LiveSvelte cho phép render component Svelte trong LiveView
  • Trong render củ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 prop live do 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 khi form thay đổ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
  • 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áicomponent 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 pushEvent từ 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 pushEvent mà 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
  • Ở 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/else thay vì toán tử ba ngôi
    • Có thể dùng thiết lập thuộc tính có điều kiện
  • 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

 
GN⁺ 2024-04-05
Ý 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

    • Tôi đã dùng đúng cách này ở https://territoriez.io/
      Đâ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 chơi các game như Overwatch, nơi ragdoll physics được mô phỏng cục bộ theo cách không tất định, tôi ước gì họ làm phần đó đúng hơn
      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 đó
    • Game đối kháng đẩy vấn đề giải quyết xung đột độ trễ thấp này đến cực hạn, và có hẳn một nhánh kỹ thuật riêng gọi là “netcode” để điều phối stream sự kiện thời gian thự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=34399790https://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
    • Chẳng phải đó chỉ là optimistic update sao? Trong logic phía client thì cách này đã phổ biến từ lâu rồi mà
    • Cần dùng cách này cẩn thận
      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

    • Mạng di động nhìn chung cũng có độ trễ cao, và trong nhiều trường hợp client cần hệ thống cache local-first
      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
    • Trong tình huống mạng bị ngắt quãng, trang LiveView dùng được đến mức nào khi offline?
  • 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

    • Nếu chèn dòng mới bằng animation chậm và vô hiệu hóa thao tác bấm trong lúc dữ liệu mới đang đi vào, cách này cũng có thể dùng được
  • Đ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

    • State của ứng dụng web không đơn giản như vậy
      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
    • Cuối cùng có vẻ một dạng nào đó của paradigm này sẽ thắng
      Ứ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
    • Một số thứ nếu xử lý hoàn toàn phía client sẽ mang lại trải nghiệm tốt hơn
      Đừ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
    • Gần giống như thế hệ mới đang tái khám phá ColdFusion, Web Forms, JSF, PHP, Spring, Rails
    • Điều này không khác các tối ưu hóa trong những hệ sinh thái phần mềm khác
      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

    • Vậy persistence thì làm thế nào? Tôi xem đó là mục đích cốt lõi của backend
      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 đó
    • Nghe như over-engineering
      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
    • Có lý do gì không thể chỉ dùng Service Worker à?
    • Có thể chia sẻ thêm chi tiết không?
    • Nghe rất giống client-side prediction mà các game multiplayer hiện đại đang làm
  • Tôi đã tạo LiveSvelte, nên nếu có gì tò mò thì cứ cho tôi biết

    • Nếu dùng LiveSvelte thì liệu vẫn còn lý do nào phải dùng JS Hooks không?
  • 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

    • Có các backend LiveView được viết bằng JavaScript(http://liveviewjs.com), Go(https://github.com/canopyclimate/golive), Java(https://github.com/floodfx/undead), Python(https://github.com/ogrodnek/pyview)
      Nhân tiện, ba cái đầu là do tôi tạo ra hoặc đã hỗ trợ tạo ra
    • Có thể cài erlang/BEAM/OTP và elixir bằng asdf, và nếu có chút kinh nghiệm dùng công cụ thì chỉ mất vài phút
      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ờ
    • “Tích hợp lập trình hướng thông điệp có trạng thái, hoặc lập trình phản ứng, vào full-stack chủ lưu” cũng từng được gọi là MVC trước khi Rails định nghĩa lại thuật ngữ này
  • Đâ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

    • Khi Stimulus/Turbo lần đầu được công bố, tôi đã khá kỳ vọng chúng sẽ giúp giải quyết những vấn đề mà tác giả mô tả
      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ôi cũng dùng Stimulus cùng với LiveView
      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 ở client
    Khi 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