9 điểm bởi GN⁺ 2025-02-19 | 7 bình luận | Chia sẻ qua WhatsApp
  • Tổng hợp các vấn đề gặp phải sau khi nâng cấp ứng dụng web gần đây lên Svelte 5
    • Đã xuất hiện các hành vi ngoài dự kiến do tính năng deep reactivity và vòng đời đã thay đổi
  • Dù đã gắn bó rất lâu với Svelte 3/4, nhưng có lẽ sẽ không chọn Svelte cho các dự án mới trong tương lai

Nhu cầu về tốc độ

  • Đội ngũ Svelte đã thử tối ưu hiệu năng thông qua deep reactivity, và đạt được hiệu năng tốt hơn
  • Trước đây, Svelte vốn đã cung cấp hiệu năng nhanh nhờ quá trình biên dịch, và đây từng là điểm mạnh tạo khác biệt với các framework khác
  • Điều này tạo ra một framework kém minh bạch, khiến việc debug khó khăn hơn, nhưng vẫn được cảm nhận là một sự đánh đổi chấp nhận được về mặt hiệu năng và năng suất

Nhu cầu về tốc độ

  • Thay đổi trọng tâm mà đội ngũ Svelte theo đuổi trong Svelte 5 là “deep reactivity”, nhằm tăng hiệu năng thông qua khả năng phản ứng chi tiết hơn
  • Ở các phiên bản Svelte trước, mục tiêu này chủ yếu được đạt tới nhờ trình biên dịch Svelte
    • Việc có thể tái cấu trúc logic nội bộ mà không buộc lập trình viên phải trực tiếp học thêm khái niệm mới là điểm làm nổi bật sự độc đáo của Svelte
  • Đồng thời, quá trình biên dịch này cũng làm framework trở nên kém minh bạch, khiến việc debug các vấn đề phức tạp trở nên khó khăn
    • Đã có những lỗi khó xác định nguyên nhân do chính bug của compiler gây ra, và đôi khi chỉ có thể giải quyết bằng cách refactor toàn bộ component có vấn đề
  • Dù vậy, vì đây vẫn được xem là một sự đánh đổi hợp lý về tốc độ và năng suất, nên tác giả đã chấp nhận cả sự bất tiện khi phải định kỳ khởi tạo lại dự án

Svelte không phải là Javascript

  • Svelte 5 nhân đôi sự đánh đổi này
  • Khác biệt cốt lõi là điểm cân bằng giữa trừu tượng hóa và hiệu năng đã vượt qua giai đoạn biên dịch để xâm nhập cả vào phần runtime
    • Sử dụng proxy để hỗ trợ deep reactivity
    • Trạng thái vòng đời component mang tính ngầm định (implicit)
  • Hai thay đổi này cải thiện hiệu năng, đồng thời khiến API cho lập trình viên trông mượt mà hơn
  • Có gì để ghét ở đây? Đáng tiếc là hai tính năng này là ví dụ điển hình của leaky abstraction
    • Cuối cùng lại dẫn tới một môi trường phức tạp hơn cho lập trình viên

Proxy không phải là object

  • Nhờ dùng Proxy, đội ngũ Svelte có thể vắt thêm một chút hiệu năng của framework mà không đòi hỏi lập trình viên phải làm thêm việc gì
    • Ở những framework như React, khi truyền state qua nhiều component thì rất dễ phát sinh re-render không cần thiết, và Svelte đưa Proxy vào nhằm giảm điều đó
    • Trình biên dịch Svelte trước đây vốn đã tránh được một số vấn đề có thể phát sinh trong quá trình so sánh virtual DOM, nhưng có vẻ họ cho rằng có thể cải thiện hiệu năng thêm nữa bằng Proxy
    • Đội ngũ Svelte cũng nói rằng Proxy góp phần cải thiện trải nghiệm lập trình viên, với lập luận rằng “có thể tối đa hóa cả hiệu quả lẫn tính dễ sử dụng”
  • Vấn đề là Svelte 5 trông có vẻ đơn giản hơn ở bề ngoài, nhưng thực tế lại thêm nhiều lớp trừu tượng hơn
    • Ví dụ, khi dùng Proxy để phát hiện các method của mảng, sẽ có lợi thế là không cần viết value = value như ở Svelte 4
    • Ở Svelte 4, để kích hoạt reactivity, lập trình viên cần hiểu ở mức nào đó cách compiler hoạt động. Ngược lại, Svelte 5 tạo cảm giác như “có thể quên compiler đi”, nhưng thực tế không phải vậy
    • Sự tiện lợi có được từ lớp trừu tượng mới đi kèm với việc số quy tắc mà lập trình viên phải biết để compiler hoạt động đúng ý cũng tăng lên
  • Sau thời gian dài dùng Svelte, cá nhân tác giả dần chuyển sang dùng Svelte store nhiều hơn và ít dùng reactive declaration hơn
    • Về cơ bản, Svelte store gần với khái niệm JavaScript hơn, cách gọi method update cũng đơn giản, còn cú pháp $ chỉ là một lợi thế bổ sung
    • Proxy, cũng giống như reactive declaration, gây ra vấn đề “nhìn có vẻ là một thứ, nhưng ở các ranh giới thực tế lại hoạt động khác đi”
  • Lần đầu dùng Svelte 5 thì mọi thứ đều chạy tốt, nhưng khi cố lưu trạng thái Proxy vào IndexedDB thì phát sinh DataCloneError
    • Tệ hơn nữa, để chắc chắn một giá trị có phải Proxy hay không, phải thử structured clone bằng try/catch, mà cách này lại tốn kém về hiệu năng
    • Cuối cùng, người dùng buộc phải nhớ thứ gì là Proxy, và ở các ngữ cảnh bên ngoài không nhận diện được Proxy thì phải luôn dùng $state.snapshot
    • Kết quả là, trái với ý định ban đầu rằng “trừu tượng hóa sẽ tăng sự tiện lợi cho lập trình viên”, tình huống này lại buộc họ phải tuân theo nhiều quy tắc và quy trình phức tạp hơn

Component không phải là function

  • Khoảng năm 2013, virtual DOM trở nên phổ biến vì nó cho phép mô hình hóa ứng dụng như tổ hợp của các hàm
    • Svelte từ trước đến nay vẫn duy trì cách tiếp cận dùng compiler thay vì virtual DOM để đơn giản hóa các hàm vòng đời và tăng hiệu năng
    • Tuy nhiên, trong Svelte 5, khái niệm vòng đời lại được đưa trở lại theo hướng khá giống React Hooks
  • Trong React, Hooks là một lớp trừu tượng giúp giảm phần code liên quan đến trạng thái trong các method vòng đời
    • Code có thể gọn hơn, nhưng cũng có rất nhiều điểm mà lập trình viên phải cẩn thận, chẳng hạn khi tham chiếu state trong setTimeout
    • Ngay cả ở Svelte 4, nếu code bất đồng bộ truy cập phần tử DOM vào thời điểm component unmount thì cũng có thể gây ra vấn đề
    • Giờ đây ở Svelte 5, có vẻ như trạng thái ngầm định đã được thêm vào vòng đời component để điều phối thay đổi trạng thái và effect
  • Tài liệu chính thức về $effect mô tả như sau:
    > “$effect có thể được đặt ở bất kỳ đâu, nhưng phải được gọi trong quá trình khởi tạo component (hoặc khi effect cha đang hoạt động), và sẽ biến mất khi component (hoặc effect cha) bị unmount”
  • Điều này cho thấy, trái với lời giải thích rằng vòng đời chỉ tồn tại ở hai giai đoạn mount/unmount, thực tế vẫn tồn tại một cấu trúc effect phức tạp đòi hỏi phải theo dõi biến đổi trạng thái
  • Tài liệu chính thức về vòng đời nói rằng “không có before update/after update”, nhưng lại xuất hiện các khái niệm mới như $effect.pretick
  • Điều này trên thực tế có nghĩa là ngoài mount/unmount, người dùng vẫn cần hiểu cả thời điểm thay đổi trạng thái
  • Phần thực sự gây ra vấn đề trong quá trình sử dụng là ngay cả state được truyền cho một hàm không liên quan đến Svelte cũng bị ràng buộc với vòng đời component
  • Ví dụ, tác giả dùng pattern quản lý modal bằng store và truyền callback xuống component con
    const { value } = $props()  
    const callback = () => console.log(value)  
    const openModal = () => pushModal(MyModal, { callback })  
    
  • Nếu đoạn code này nằm trong component modal, thì component gọi modal sẽ unmount trước, và tại thời điểm đó value bị đổi thành undefined
  • Một ví dụ tái hiện tối giản đã được đăng trong repository này
  • Nói cách khác, props mà callback đang tham chiếu có thể đột ngột trở thành undefined ngay cả khi callback đó vẫn còn tồn tại sau khi vòng đời component kết thúc
  • Đây là cách hành xử khác với JavaScript thuần, và trông như thể Svelte đang tự làm một thứ gì đó giống garbage collection
  • Có thể có lý do về mặt kỹ thuật, nhưng đây vẫn là một hành vi bất ngờ và gây ngạc nhiên

Kết luận

  • Cái gì dễ dùng rõ ràng cũng hấp dẫn, nhưng như Rich Hickey từng nói, dễ không đồng nghĩa với đơn giản
  • Cũng như Joel Spolsky từng nói, tác giả không thích những hành vi bất ngờ xảy ra ngoài dự tính
  • Svelte từ trước đến nay đã thể hiện rất nhiều “phép màu”, nhưng ở phiên bản này, để dùng được những phép màu đó, số thứ phải ghi nhớ đã tăng lên đến mức gánh nặng lớn hơn lợi ích
  • Mục đích của bài viết này không phải để chỉ trích đội ngũ Svelte; ngược lại, tác giả hiểu rằng vẫn có rất nhiều người ưa thích Svelte 5 (và React Hooks)
  • Điều quan trọng là sự cân bằng giữa việc mang lại tiện lợi cho người dùng và việc để người dùng nắm được quyền chủ động
  • Phần mềm thực sự tốt không dựa trên sự “khôn khéo”, mà dựa trên “sự thấu hiểu”
  • Khi các công cụ AI tiếp tục phát triển, điều quan trọng là chọn những công cụ giúp tận dụng trí tuệ đã tích lũy và hỗ trợ hiểu sâu hơn, thay vì những công cụ khiến người dùng không biết mình đang làm gì
  • Cảm ơn Rich Harris và cả đội vì quãng thời gian phát triển đầy thú vị. Mong rằng bài viết này sẽ trở thành một phản hồi không phải là thiếu chính xác

7 bình luận

 
firea32 2025-02-24

proxy giúp người tạo ra nó thấy tiện hơn, nhưng người phải debug thì phát bực lên ấy chứ haha

 
bichi 2025-02-21

Dự án phụ có DX của solidjs là số một >m< / hạnh phúc

 
pcj9024 2025-02-20

Tôi nghĩ chính vì có những lựa chọn thay thế như Svelte nên React/Next.js cũng đã có thể nhận được cú hích lớn. Về bản chất, Svelte là một ngôn ngữ, nên tôi cũng hy vọng nó sẽ chỉ ra thật rõ hướng đi mà một ngôn ngữ dùng để mô tả UI nên tiến tới.

Tôi sẽ dùng React.

 
iolothebard 2025-02-20

Làm quá hóa dở
Tẩu hỏa nhập ma
Chồng chất thêm tầng lớp vô ích

 
colus001 2025-02-20

Tôi nghĩ nó đã trở nên kỳ lạ khi chịu ảnh hưởng không ít từ React, đặc biệt là next. +page rất khó hiểu nếu nhìn vào mà không biết svelte, còn các rune như $state, $derived thì có vẻ như đang đi theo React; tôi còn thấy thời dùng $: trước biến lại tốt hơn. Cú pháp kiểu cũ như {#each a in array} {/each} thì vẫn có thể chịu được, nhưng vẫn khá phiền. Nếu là cải thiện hiệu năng nhờ reactivity tùy chọn, tôi nghĩ solidjs là hướng đi tốt hơn nhiều. Vì dùng nguyên jsx, nên việc chuyển từ react sang cũng tương đối dễ hơn. Đến mức solidjs tương đối không được chú ý nhiều mới khiến tôi thấy khó hiểu.

 
xiniha 2025-02-19

Có vẻ như Signals đang hướng tới giai đoạn Trough of disillusionment trong Gartner hype cycle 🤔 Khi các trường hợp sử dụng dần được định hình rõ hơn, có lẽ cách đánh giá cũng sẽ được cải thiện.

 
GN⁺ 2025-02-19
Ý kiến Hacker News
  • Lúc đầu tôi không mấy hứng thú với runes. Nhưng tôi đã đổi ý khi có thể đưa các thành phần phản ứng từ bên ngoài vào template .svelte và đóng gói tính phản ứng ở bên trong. Điều đó có nghĩa là bạn vẫn có thể viết các bài kiểm thử bằng vitest mà vẫn tận dụng được lợi ích của reactivity. Điều này thực sự rất mạnh và theo như tôi biết thì là khá độc nhất trong thế giới frontend

    • Hầu hết các lập trình viên frontend hoàn toàn không viết test. TypeScript là công cụ mọi người dùng để đảm bảo tính chính xác, và điều đó có lý do của nó. Tuy nhiên, người dùng Svelte từ trước đến nay luôn có cái nhìn khá hẹp về TypeScript, và điều đó cũng có lý do
    • Cá nhân tôi thích viết mã frontend có thể kiểm thử được, và Svelte 5 mang tính cách mạng ở điểm đó. Nó vừa phản ứng trong trình duyệt vừa tốt ngang với unit test
    • Nói tất cả điều đó, bài blog đang nói đúng sự thật. Việc thêm proxy tạo cảm giác rất khó chịu. React và Vue đã khiến tôi mất hứng khi họ bắt đầu chồng thêm các tầng trừu tượng lên nhau, và proxy chính là điểm khởi đầu của chuyện đó
    • Việc Svelte 5 không phải là JavaScript chính là <i>lý do tôi thích Svelte 5</i>
    • Tôi nghĩ có hai cách chính để làm frontend/web một cách hợp lý
        1. HTML tĩnh hoặc template render phía server, có thể là HTMX
        1. Một ngôn ngữ/nền tảng biên dịch xuống JavaScript. Tối thiểu là TypeScript, nhưng vì tôi nghĩ HTML và CSS thực ra đã khá tốt rồi nên JSX, React, Tailwind v.v. bị <i>loại</i>, còn Svelte 5 và một vài framework khác thực sự là cải tiến so với TypeScript thuần
    • Svelte 5 là người chiến thắng rõ ràng trong nhóm 2
    • Nó có template HTML đẹp, cách truyền trạng thái dễ dàng và hợp lý, khả năng giúp viết mã module hóa một cách đơn giản. Nó làm việc tốt với CSS, thường cho phép tạo các app và công cụ đơn giản dùng một lần chỉ trong một hoặc hai file, đồng thời cũng có thể dùng cho các ứng dụng lớn và nghiêm túc hơn. Nó ít ma thuật và ít gây ngạc nhiên hơn Svelte 4, và thành thật mà nói là rất vui để sử dụng. May mắn là tôi không quan tâm đến IndexedDB
    • Ngày nay tôi hoàn toàn không biết có lý do gì để viết dù chỉ một dòng JavaScript, nhưng ai cũng có cách riêng của mình
  • Tôi đang tích cực phát triển một ứng dụng SvelteKit đã được triển khai thương mại và muốn chia sẻ một vài suy nghĩ về trải nghiệm này

    • Điều đầu tiên thu hút tôi đến với SvelteKit là sự đơn giản của nó. Sau khi thiết lập dự án, tôi có thể làm việc với từng file HTML/JS/CSS một, đồng thời tận dụng lợi ích của một framework hiện đại mà không phải gánh thêm sự phức tạp. Điều này gợi nhớ đến những ngày đầu của phát triển web, khi mọi thứ chỉ cần thả các file HTML lên máy chủ Apache là chạy
    • Tuy nhiên, thật đáng thất vọng khi thấy Svelte rời xa mô hình đơn giản đó. Ngay từ đầu, Rich Harris đã quảng bá tính dễ dùng và đơn giản của Svelte như những điểm bán hàng cốt lõi. Phiên bản SvelteKit hiện tại không tệ, nhưng tôi thích các phiên bản trước hơn. Khi đó không cần phải xử lý cấu trúc định tuyến như +page. Bạn có thể đặt file Svelte ở bất kỳ đâu mình muốn và nó vẫn được render trơn tru trong khi vẫn hưởng lợi từ framework hiện đại
    • Những thay đổi này thêm vào sự phức tạp vốn trước đây không cần thiết, và có thể khiến Svelte rời xa chính sức hút ban đầu của nó. Tôi đã chọn nó dựa trên những gì mình từng biết
  • Thật tiếc khi EmberJS đã biến mất. API của nó khá ổn định trong suốt 10 năm qua. Trớ trêu thay, người đã viết ứng dụng EmberJS trong 10 năm qua có lẽ sẽ gặp ít khó khăn khi di chuyển hơn so với người làm cùng công việc bằng React, Svelte, Vue v.v.

    • Đáng tiếc là đội Ember đã đưa ra một vài quyết định kỳ lạ trong thời kỳ đầu, và so với JavaScript thông thường thì nó không dễ hiểu lắm (dù phần lớn giờ đã được sửa)
    • Nó có phải JavaScript hay không thật ra không quan trọng bằng độ ổn định của API
    • Cá nhân tôi thấy vấn đề của JavaScript là nó thay đổi quá thường xuyên
  • Hai liên kết Github mà tác giả liệt kê ở đầu bài viết đều trỏ đến cùng một vấn đề, và vấn đề đó có cách giải quyết (dùng $state.raw)

    • Tôi là fan của Svelte từ thời Svelte 2 hoặc 3. Đó là vì nó gần với việc viết HTML/CSS/JS thuần nhất mà vẫn có được lợi ích của framework (và còn dùng Sass cùng TypeScript). Thật tuyệt vời
    • Svelte 5 ban đầu khiến tôi thấy bất an vì runes trông kỳ lạ. Sau khi nâng cấp dự án và làm việc trên các dự án khác, tôi thấy nó không tệ đến thế. Svelte 5 không cho phép trộn lẫn cách xử lý state cũ và mới, còn thông báo lỗi thì phần lớn đều hữu ích
    • Tôi thấy trong phần bình luận này có người nói htmx và JS thuần khách quan là tốt hơn... không hẳn? Có thể đúng với việc bạn đang làm. Cá nhân tôi vẫn thấy Svelte dễ hiểu hơn React rất nhiều. Với những ai quan tâm đến benchmark, Svelte nhanh ngang SolidJS (người dùng React có lẽ sẽ chuyển sang Solid khá dễ vì cú pháp cho cảm giác tương tự)
  • Svelte 5 là kiểu <i>không phải JavaScript</i> theo cách <i>tệ nhất</i>. Nó không giải quyết được vấn đề của js, mà chỉ cung cấp những lớp trừu tượng bị rò rỉ cho một số vấn đề frontend. Tôi nghĩ Solid đi theo hướng tốt hơn Svelte, vì Solid không phụ thuộc vào một ngôn ngữ mới. Tuy nhiên, cũng có bản năng muốn tạo ra thứ gì đó không phải js vì js không lý tưởng. Elm là tiền thân của svelte. Nhưng tôi nghĩ chúng ta có thể tạo ra thứ tốt hơn...[0]

  • Tôi bắt đầu dùng Svelte 5 vì cực kỳ ghét store trong Svelte 4. Tôi đang dùng Sveltekit và Svelte 5 cho một dự án mới, và phải nói rằng... năng suất của React vẫn là vô đối, dù về mặt kỹ thuật Sveltekit và công nghệ Svelte tốt hơn

    • Một vài điều thực sự gây khó chịu: mọi trang đều được đặt tên là +page.server.ts hoặc +page.svelte hay các biến thể của chúng, khiến việc tìm kiếm mã không hề dễ dàng. Công cụ của Svelte tồn tại tách biệt với tsc và ESLint nên việc tích hợp vào CI và dùng trong quá trình phát triển khó hơn
    • Cũng có những vấn đề tương thích ngược kỳ lạ. Ví dụ, phần lớn các package Svelte vẫn dùng store, nên bạn phải vật lộn với hai thế giới phiên bản cùng lúc, khiến việc viết mã đôi khi thực sự rối rắm. Ngoài ra, Svelte HMR dường như vẫn còn ở giai đoạn đầu, nên trạng thái có thể bị hỏng khi module Svelte được nạp lại
    • Tôi thực sự muốn thích Svelte. Tốc độ render của nó khá nhanh và tôi thích các ý tưởng đằng sau nó. Nhưng năng suất của React là vô đối
  • Nói rằng Svelte không phải JavaScript chỉ vì hành vi bất ngờ khi truyền closure vào callback nghe có vẻ kỳ quặc. Một tiêu đề tốt hơn có lẽ là "Tôi ghét Svelte 5 vì nó khiến tôi bất ngờ"

  • Nếu bạn đang tìm một thư viện cho phép xây dựng component và ứng dụng bằng JavaScript thuần, hãy xem Lit: Lit

    • Có một package bổ sung cho signals để hỗ trợ deep reactivity, và nó đang hướng tới việc tích hợp với đề xuất Signals TC39 sắp tới: Signals
    • Lit được sử dụng trong các ứng dụng lớn như Photoshop, Reddit, Home Assistant và The Internet Archive
  • Muốn có trải nghiệm frontend hợp lý hơn? Hãy dùng JavaScript thuần, web components, htmx, Blazor

    • Framework JS, vì lý do nào đó, là một sự điên rồ