- Trên macOS, nếu chỉ dùng SwiftUI để tạo UI chat Markdown thì vẫn đạt được hiệu năng cơ bản ở mức nào đó, nhưng rất khó hỗ trợ chọn toàn bộ tài liệu
- Nếu chuyển sang
NSTextView và TextKit 2 thì sẽ mất phần lớn công sức kiểm thử và tối ưu hiệu năng đã làm với SwiftUI, đồng thời xuất hiện CPU spike khi nhập liệu theo kiểu streaming
- Việc tái triển khai bằng
NSCollectionView gây hiện tượng nhấp nháy ô, còn TextKit 2 thuần cũng có hiệu năng ổn nhưng tích hợp với streaming không tốt
- WebKit nhìn chung phù hợp hơn về render Markdown, hiệu năng, typography và mức độ kiểm soát; Electron cũng cho các tác vụ văn bản hoạt động sẵn theo mặc định
- Với các cuộc trò chuyện dài và rich text, SwiftUI cùng SDK native của Apple trở thành ràng buộc, còn nền tảng web có lợi thế hơn về mô hình văn bản và render
Giới hạn của UI chat Markdown native trên macOS
- Nếu tạo một giao diện chat đơn giản hỗ trợ Markdown chỉ bằng SwiftUI, bạn vẫn có thể đạt được hiệu năng cơ bản ở mức nào đó, nhưng không thể chọn toàn bộ tài liệu Markdown được cấu thành từ các primitive của SwiftUI
- Nếu chuyển sang
NSTextView, bạn sẽ có hỗ trợ TextKit 2, nhưng lại mất phần lớn phần kiểm thử và tối ưu hiệu năng đã làm trong SwiftUI, đồng thời nó cũng không còn ăn khớp tốt với SwiftUI nữa
- Khi đưa phản hồi của mô hình vào
NSTextView theo kiểu streaming, sẽ xuất hiện CPU spike
- Dù có triển khai lại bằng
NSCollectionView, các ô vẫn liên tục nhấp nháy, và đây là hành vi khó tránh do đặc điểm thiết kế
- Bản prototype dùng TextKit 2 thuần có hiệu năng ổn, nhưng streaming vẫn kém và cũng không phối hợp tốt với các thành phần hiện đại
- Ngay cả khi loại bỏ hoàn toàn SwiftUI và chỉ dùng AppKit, bạn vẫn phải tự xử lý các mảnh văn bản mở rộng, và chỉ khi rất nhiều phần bị vỡ thì mới có thể chọn văn bản
- Để đạt tới mức gần giống hành vi mặc định của macOS, bạn phải làm lại những tính năng mà người dùng mặc nhiên mong đợi như menu ngữ cảnh, tra từ điển, chọn, accessibility và tương tác văn bản
Điểm mà WebKit và Electron phù hợp hơn
- Nếu render Markdown bằng WebKit, vẫn có một vài điểm cần lưu ý nhưng nhìn chung nó hoạt động tốt, cho hiệu năng và typography tốt, đồng thời mức độ kiểm soát cũng đủ
- Nếu tạo một dự án Electron đơn giản, các tác vụ văn bản, render Markdown và typography đẹp đều hoạt động sẵn theo mặc định, đồng thời còn cho hiệu năng mà bản triển khai TextKit 2 thuần khó đạt được
- Electron cũng cung cấp tích hợp với macOS; chỉ với vài dòng mã đã có thể render Git diff đẹp mắt, và đã có những ví dụ như diffs.com
- Dù đã xem xét từ SwiftUI, AppKit, TextKit đến WebKit, vẫn rất khó đáp ứng trọn vẹn yêu cầu đơn giản là “một khung chat hỗ trợ Markdown và có thể chọn toàn bộ nội dung tin nhắn”
- Điều này càng làm rõ hơn vì sao các ứng dụng mới coi trọng chat, rich text dài và typography linh hoạt lại chuyển sang nền tảng web
- SwiftUI phù hợp với các màn hình đơn giản không có quá nhiều cuộn, còn Swift vẫn hữu ích ở những phần đòi hỏi hiệu năng cao
- Electron hay React Native có thể đạt hiệu năng đáng kể nhờ khả năng tương tác native, trong khi vẫn giữ được mô hình văn bản và render tốt hơn
- Trong việc render rich text cho các đoạn chat dài, SwiftUI và SDK native của Apple không còn là lợi thế mà trở thành ràng buộc
- Thảo luận liên quan: Hacker News, Lobsters
2 bình luận
Ý kiến trên Hacker News
Gần đây tôi đã phát hành một trình soạn thảo văn bản cho iOS dùng TextKit 2, và hiệu năng vẫn rất tốt ngay cả với tệp 5.000 dòng
Tôi đã kiểm thử bằng Moby Dick của Project Gutenberg, làm nó trong khoảng từ tháng 8 năm 2025 đến tháng 4 năm 2026, và vẫn đang tiếp tục phát triển
Mỗi lần gõ phím đều được áp dụng lại style trong vòng 8ms, và ngay cả khi nhập nhanh 20 lần liên tiếp mà không dùng debouncing hay render trì hoãn, toàn bộ việc restyle sau mỗi lần nhập cũng được xử lý trong 150ms
Gắn thẻ và tìm kiếm boolean hoàn tất trong vòng 20ms, và nếu chỉ render phần đang hiển thị thì nhanh hơn 25 lần so với style toàn bộ tài liệu, đồng thời hỗ trợ tần số làm tươi màn hình 120Hz
Kích thước tệp ứng dụng ở bản 1.0 là 722KB, còn bản 1.1 có thêm tính năng cũng có vẻ chỉ khoảng 950KB
Nếu mức này làm được trên iOS, thì trên macOS đáng lẽ phải dễ hơn khoảng 10 lần
https://www.gingerbeardman.com/apps/papertrail/
Không phải là “bất khả thi”, mà ý là tôi hiểu vì sao người ta lại chọn công nghệ web thay vì native cho việc này. Người ta muốn làm sản phẩm, chứ không muốn vật lộn với giới hạn của hệ thống
Nếu là trình xem văn bản thì ít nhất phải xử lý được các tệp lớn hơn cỡ đó hai bậc độ lớn. Tệp JSON hàng trăm nghìn dòng là chuyện thường, còn CSV và log thì còn dài hơn nữa
Trước đây lý do thường dùng API native thay vì webview là hiệu năng, nhưng giờ có vẻ không còn hẳn như vậy
Engine render của trình duyệt đã khá trưởng thành, có rất nhiều tăng tốc GPU, và đã bị stress test hơn 10 năm bởi các web app ngày càng phình to
Trong khi đó SwiftUI không hề cho cảm giác nhanh. Ngay cả phần Cài đặt hệ thống được Apple làm lại theo kiểu hiện đại cũng đã đơn giản hóa UI thành chủ yếu là các hàng checkbox, vậy mà lúc chuyển section còn giật hơn cả tải một trang web từ us-east-1
Tôi đã làm native app bằng Qt C++ và QML, và chứng minh được chúng nhanh hơn rất nhiều và dùng RAM ít hơn rất nhiều so với web app tương tự
Vì thế, nhìn chung web app chậm hơn và tốn tài nguyên hơn native app được thiết kế tốt
[1] https://notes.alinpanaitiu.com/SwiftUI%20is%20convenient,%20...
[2] https://x.com/daniel_nguyenx/status/1734495508746702936
[3] https://rubymamistvalove.com/block-editor#8-performance
Tôi vốn là web developer, nhưng 6–12 tháng gần đây bắt đầu làm native cross-platform app, và ngay cả với các tác vụ đơn giản thì chênh lệch hiệu năng cũng khá rõ
Thật lạ khi bỏ qua hàng nghìn nhân-năm tối ưu hóa và hàng triệu nhân-năm kiểm chứng ngoài thực tế, để rồi nghĩ rằng nên tự làm lại một engine render văn bản tệ hơn
Trên macOS, WebKit là framework OS native. Dùng WebKit cho Markdown rendering có vẻ hoàn toàn hợp lý
Tất nhiên, render mọi thứ bằng WebKit cũng vô lý chẳng khác gì render mọi thứ bằng PDFKit. Nhưng nếu là view Markdown thì WebKit là lựa chọn logic, mà cũng không có nghĩa phải lật bàn sang web app Chromium
Và OS X trong thời gian dài cũng đã render UI bằng DisplayPDF/Quartz
WebKit có trên nền tảng khác nữa nên bị xem là ăn gian sao? Nếu vậy thì dùng Java cũng được
Nếu render văn bản bằng bộ render HTML/CSS/JS là “hoàn toàn hợp lý”, thì đâu là vùng không hợp lý? Tại sao không render mọi thứ bằng nó?
Tôi không hiểu logic đi từ “render text thì hợp lý” sang “render mọi thứ thì vô lý”
Tôi đồng ý rằng trên macOS, WebKit là framework OS native, nên theo nghĩa đó thì nó là “native”
Nhưng điều đó cũng củng cố luận điểm lớn hơn rằng nếu muốn xử lý đúng rich text, Markdown, lựa chọn, typography và nội dung dài có định dạng, thì công nghệ web nhanh chóng trở thành lựa chọn thực tế duy nhất
Không phải dùng WebKit cho view Markdown là sai, mà rất có thể đó là lựa chọn hợp lý nhất. Vấn đề là ở đây, giải pháp “native” trên thực tế lại chính là giải pháp render web
Mỗi
WKWebViewđều mang theo engine WebKit với chi phí hiệu năng và bộ nhớ riêng, nên không thể rải nó khắp nơi rồi coi như một component macOS native miễn phíĐiều khiến tôi bực là với kiểu UI này, SwiftUI / AppKit / TextKit không đưa ra được một con đường sạch sẽ, hiện đại và có thể kết hợp tốt hơn câu trả lời “cứ dùng WebKit đi”
Chuyện “không thể cho phép chọn toàn bộ nội dung tin nhắn trong một đoạn chat có Markdown” nghe thật vô lý
Trong SwiftUI có thể tận dụng renderer Markdown đã khá trưởng thành. Cứ xem https://github.com/gonzalezreal/swift-markdown-ui và phiên bản thay thế thế hệ mới của nó là https://github.com/gonzalezreal/textual
Tôi đã tự dùng rồi và không gặp vấn đề gì. Ngay cả một kẻ ngốc không thích Swift hay SwiftUI và thích Objective-C như tôi cũng làm được mà không cần LLM hỗ trợ
Cuộn Markdown hoàn chỉnh tĩnh không qua được bài kiểm tra focus probe mới, với p95 là 18,86ms, vượt quá ngân sách 16,7ms, và tối đa là 232,49ms
Luồng cập nhật Markdown/code dài theo thời gian thực cũng thất bại, với p95 59,33ms so với 16,7ms, tối đa 75,94ms. Đây là một case stress riêng nhưng có liên quan, xử lý bề mặt rich text lớn trong lúc đang cập nhật
Mở rộng lịch sử dài thì về mặt kỹ thuật là qua được, nhưng khó gọi là khung hình mượt: 120 turn p95 21,35ms, 500 turn 23,11ms, 1000 turn 36,77ms
Không tệ, nhưng hơi chậm hơn giải pháp của tôi, và khoảng cách hiệu năng tương tự này có vẻ chủ yếu liên quan tới SwiftUI hơn là riêng cách triển khai của Textual
Trước đây tôi từng dùng swift-markdown-ui trong app, nhưng hiệu năng không thể so với wkwebview. Nếu stream tài liệu lớn có các thành phần khó như bảng lớn, code block, trích dẫn lồng nhau, thì còn có thể thấy beachball, trong khi dùng wkwebview thì không bị vậy
Rồi tôi nhận ra trình duyệt và các công nghệ nền tảng của nó đã đưa vào UI một mô hình mới, còn framework UI native thì không theo kịp
Ngay cả khi tôi thích native app hơn app web, tôi vẫn có cảm giác như vậy
Hoặc là cho xem code, hoặc là thôi. Ngay lúc này cũng có rất nhiều app Mac/iOS native xử lý đúng Markdown rendering và văn bản streaming
Tôi chỉ tò mò rốt cuộc đang viện cớ gì thôi
Hầu hết đều chốt theo hướng chỉ hỗ trợ chọn trong từng block liên tiếp, còn toàn bộ tin nhắn thì thêm nút copy
Điều thú vị là trước đây Apple cũng từng làm kiểu này
macOS / AppKit đời cũ từng dùng WebKit để render rich text trong NSTextField native. Văn bản là một bài toán khó
Hơn nữa, WebView native rất nhanh và nhẹ, nên dùng nó như một engine dàn trang văn bản không phải điều lạ. Ngay cả khi dùng một WebView riêng cho mỗi hàng trong bảng thì hiệu năng vẫn có thể rất tốt
iMessage trên Mac cũng từng dùng WebView, Adium cũng vậy. Nếu đang render rich/markup text thì HTML hoàn toàn là công cụ phù hợp
Mac chưa từng dùng WebKit để render NSTextField. Khi iOS mới được tạo ra, nó dùng WebKit làm text renderer khá rộng khắp, bao gồm cả các control của UIKit, và gọi đó là “sweet solution”
Nhưng rồi nó lộ ra là quá nặng nề và rườm rà, nên mới chuyển sang cách tiếp cận render text kiểu Core Text/AppKit
Tác giả phát hiện việc render text native phức tạp là khó, thử render text theo cách mức thấp rồi than phiền rằng mình phải tự triển khai lại tương tác native
Sau đó thử WebKit thì thấy cực kỳ ổn, nhưng rồi lại bỏ nó để quay về tình huống phải tự triển khai lại tương tác native
Cá nhân tôi thì đã dừng lại ở thời điểm WebKit chạy tốt
Tôi nhớ hồi còn là kỹ sư junior năm 2015 đã được giao việc render các liên kết có thể nhấp bên trong đoạn văn trong một app iOS
Khi đó Swift còn rất mới nên stack hoàn toàn là ObjC/UIKit, và đúng là ác mộng. Cuối cùng tôi cũng khiến nó chạy được
Từ khoảng 2016 trở đi tôi gần như không đụng tới iOS nữa, nên cứ nghĩ SwiftUI mới chắc chắn phải có sẵn thứ này theo mặc định, vậy mà hóa ra không có thì khá điên rồ
https://developer.apple.com/documentation/swiftui/link
Đến mức này thì tôi không biết còn có thể làm nó dễ hơn thế nào nữa
Chuyện “vượt ra khỏi màn hình đơn giản là native vẫn non nớt như vậy” vốn là điều dễ hiểu
Nếu người ta không đầu tư đủ công sức thì không thể kỳ vọng nó trưởng thành được
Vì ngày càng nhiều nỗ lực đổ sang công nghệ web nên người ta bị mắc kẹt ở đó. Họ nhìn native rồi nói “nó chưa phát triển đủ”, sau đó lại tiếp tục phát triển web nhiều hơn, tạo thành một vòng lặp
Trên trình duyệt, vì mọi thứ đã “cứ thế mà chạy” nên gần như không còn ai muốn bỏ công cải thiện native nữa
Một trong những lý do web trưởng thành hơn nhiều là vì các nhà sản xuất OS thương mại lớn không chịu theo kịp thời đại. Các UI kit của Windows thật sự rất tệ
Trong ứng dụng chat AI của tôi cũng có gần như y hệt trải nghiệm đó. Chẳng có gì chạy cho ra hồn
Markdown rendering thì chậm và giật, streaming cũng chậm và giật, và mọi thứ đều làm UI đứng hình
Tôi đã thử ít nhất 5 component biên tập văn bản phổ biến trên GitHub cho UIKit và SwiftUI, và tất cả đều hỏng theo cách này hay cách khác, hoặc có bug, lại còn chậm. Thật vô lý
Đây là một bài toán khó. Tôi đã viết khá dài về cách mình giải quyết nó khi tự xây dựng block editor từ đầu bằng Qt C++ và QML
Tôi gặp các vấn đề tương tự như lựa chọn giữa các block không liền nhau, hiển thị Markdown gốc bên dưới con trỏ, hay kích thước delegate khác nhau
Dựa trên những gì học được khi đó, giờ tôi đang làm một client LLM native có parser Markdown streaming
[1] https://rubymamistvalove.com/block-editor
[2] https://www.get-vox.com
Ý kiến trên Lobste.rs
Electron thực chất là một lớp bọc quanh WebView, nhưng vì kéo theo cả bộ máy Chromium nên cái giá phải trả cho sự tiện lợi là kích thước ứng dụng trở nên quá lớn
Vào khoảng 2001~2002, tôi đã triển khai bố cục văn bản bong bóng thoại mang tính biểu tượng của iChat bằng
NSTextViewvà đủ mọi mẹo vặt, và ngay cả khi có sự giúp đỡ của Hideki Itamura, người thiết kế phần văn bản của AppKit, thì vẫn khá vất vả. Bây giờ làm bằng HTML+CSS đã trở nên khá đơn giảnTôi từng tham gia vào một ứng dụng dùng Tauri, và khi chuyển sang dùng Chromium qua Electron thì nó chạy tốt hơn rất nhiều. Đặc biệt, việc phải nhắm tới dải rất rộng từ win7 đến win11 cũng là điểm quan trọng
Dòng Tauri trông có vẻ hay ở chỗ dùng webview của hệ thống, nhưng trên Windows thì điều đó có nghĩa là Chrome hoặc Edge, trên macOS là Safari, còn ở các môi trường khác thì gần như phó mặc cho may rủi. Cuối cùng, tính dự đoán được và độ trưởng thành đã chiến thắng, và không rõ vì sao hiệu năng cũng tốt hơn
Rốt cuộc tôi muốn phần mềm tốt hơn hơn là làm hài lòng biểu đồ
htopÝ chính của bài blog không phải là “ai cũng nên chọn Electron”, mà là giờ tôi đã hiểu vì sao ngay cả những công ty có tài nguyên và tiền bạc vẫn tiếp tục chọn Electron hoặc công nghệ web. Ít nhất theo tiêu chuẩn của tôi, đó là sự thỏa hiệp ở những chỗ phù hợp để mang lại trải nghiệm người dùng tốt
Có nhiều người bênh vực TextKit 2 của Apple hay các framework văn bản native khác, nhưng hầu như không có trình soạn thảo văn bản phổ biến, hiệu năng cao nào được xây dựng chỉ bằng SDK của Apple. Xcode làm khá tốt và có lẽ là trường hợp thực chiến gần như duy nhất. Zed, Sublime Text, Visual Studio Code, JetBrains IDE đều dùng giải pháp dựng văn bản riêng vì có lý do của họ
Vì thế dù làm theo cách tệ nhất thì trong môi trường của họ nó vẫn có thể trông ổn
Trong khi đó, phần còn lại dùng thiết bị cấu hình thấp hơn nhiều lại phải tiếp tục gánh phần mềm cồng kềnh và chậm chạp
Chỉ là có vẻ tác giả không đi sâu khám phá hướng đó ở đây
Nhìn từ bên ngoài, Flutter giống như câu trả lời cho câu hỏi “nếu giữ lại mỗi Skia, phần renderer của Chromium, rồi dùng nó cho ứng dụng GUI thì sao?”. Nó có vẻ nên là một công cụ đa nền tảng nhẹ hơn Electron nhưng có bộ tính năng tương tự
contentEditable, và sử dụng một lớp con củaWKWebViewĐiều mỉa mai là công cụ văn bản native của Apple dùng một mô hình dữ liệu tốt hơn rất nhiều so với cây HTML DOM, tức là chuỗi ký tự và các thuộc tính kiểu được mã hóa theo độ dài chạy
Chỉnh sửa văn bản trong DOM gần như là cơn ác mộng, vì vùng chọn thường cắt ngang nhiều phần tử ở nhiều tầng khác nhau, nên phải chia tách và gộp lại rất phức tạp. Trước đây khi lần đầu tôi dùng một bản Safari có hỗ trợ
contenteditable, tôi đã gửi một lượng khổng lồ báo cáo lỗi, và đến giờ nhiều trình soạn thảo rich text trên web vẫn hỏng khi cắt hoặc dán các mục danh sáchCuối cùng có vẻ như đã xảy ra điều tương tự cuộc đối đầu CISC với RISC trong thập niên 90~00. Một cấu trúc “kém hơn” lại nhận được nhiều tài nguyên hơn và vì thế tạo ra một phần triển khai vượt trội hơn hẳn