37 điểm bởi GN⁺ 2025-11-03 | 2 bình luận | Chia sẻ qua WhatsApp
  • Cấu trúc URL không chỉ là một địa chỉ đơn thuần mà còn hoạt động như phương tiện lưu trữ và khôi phục trạng thái ứng dụng
  • Đưa ra ví dụ như trang tải xuống PrismJS, nơi chỉ với một URL có thể tái hiện hoàn toàn cấu hình theme, ngôn ngữ và plugin
  • Mỗi thành phần như path, query parameter, fragment biểu diễn nhiều loại trạng thái khác nhau như điều hướng phân cấp, lọc và điều hướng phía client
  • Bộ lọc tìm kiếm, phân trang, chế độ hiển thị, khoảng ngày phù hợp để đưa vào URL, còn thông tin nhạy cảm hoặc trạng thái UI tạm thời thì không phù hợp
  • Một URL được thiết kế tốt sẽ tăng khả năng chia sẻ, tính dễ dự đoán và hiệu quả cache, từ đó củng cố độ tin cậy và trải nghiệm người dùng của ứng dụng web

Tiềm năng của URL

  • URL không chỉ là địa chỉ tài nguyên mà còn đóng vai trò như giao diện người dùng (UI)container trạng thái
    • Tự động bảo toàn trạng thái trong chia sẻ, bookmark, lịch sử trình duyệt và deep link
    • Hoạt động như cơ chế quản lý trạng thái mặc định của web từ năm 1991
  • Mỗi thành phần của URL biểu diễn một loại trạng thái khác nhau
    • Path: điều hướng tài nguyên theo cấu trúc phân cấp (/users/123/posts)
    • Query: bộ lọc, tùy chọn, thiết lập (?theme=dark&lang=en)
    • Fragment: vị trí trong tài liệu hoặc định tuyến SPA (#features, #/dashboard)
  • Tính năng Text Fragments cho phép liên kết trực tiếp tới một đoạn văn bản cụ thể trong trang

Các mẫu query parameter

  • Dùng delimiter để đưa nhiều giá trị vào cùng một key (?tags=frontend,react,hooks)
  • Tuần tự hóa dữ liệu lồng nhau bằng JSON hoặc Base64 (?config=eyJyaWNrIjoicm9sbCJ9==)
  • Boolean flag được biểu diễn bằng sự hiện diện của key (?mobile)
  • Bracket notation biểu diễn nhiều giá trị dưới dạng tags[]=frontend&tags[]=react
    • Được tự động nhận diện trong qs của Node hay middleware Express, nhưng chưa được chuẩn hóa
  • Điều cốt lõi là duy trì tính nhất quán

Các ví dụ biểu diễn trạng thái qua URL

  • PrismJS: lưu toàn bộ cấu hình theme, ngôn ngữ và plugin trong URL hash
  • GitHub: tô sáng một phạm vi dòng code cụ thể bằng #L108-L136
  • Google Maps: đưa tọa độ, mức zoom và loại bản đồ vào URL
  • Figma: chia sẻ ngữ cảnh làm việc như vị trí canvas, mức zoom và phần tử được chọn qua URL
  • Các trang thương mại điện tử: đưa bộ lọc, sắp xếp và khoảng giá vào URL để khôi phục trạng thái tìm kiếm

Các mẫu kỹ thuật frontend

  • Các trạng thái phù hợp để đưa vào URL
    • Từ khóa tìm kiếm, bộ lọc, trang/sắp xếp, chế độ hiển thị, khoảng ngày, tab đang mở, cấu hình UI, feature flag
  • Các trạng thái không phù hợp với URL
    • Thông tin nhạy cảm như mật khẩu hay token, trạng thái UI tạm thời, dữ liệu nhập chưa lưu, dữ liệu dung lượng lớn, trạng thái thay đổi với tần suất cao
  • Tiêu chí đánh giá: khi người khác nhấp vào cùng một URL, họ có nên thấy cùng một trạng thái hay không

Cách triển khai trong JavaScript

  • Có thể đọc/ghi query parameter bằng API URLSearchParams
  • pushState thêm mục lịch sử mới, replaceState cập nhật mục hiện tại
  • Khôi phục UI khi người dùng bấm quay lại trình duyệt bằng sự kiện popstate

Cách triển khai trong React

  • Có thể quản lý trạng thái URL gọn gàng bằng hook useSearchParams của React Router
    • Tự động đồng bộ URL và UI khi đọc/cập nhật parameter

Best practice trong quản lý trạng thái bằng URL

  • Không đưa giá trị mặc định vào URL (chỉ giữ ?theme=dark, còn giá trị mặc định xử lý trong code)
  • Dùng debouncing để tránh cập nhật URL quá nhiều khi đang nhập (lodash.debounce)
  • pushState vs replaceState
    • pushState: dùng cho trạng thái có thể quay lại như thay đổi bộ lọc hay chuyển trang
    • replaceState: dùng cho các chỉnh sửa chi tiết như nhập từ khóa tìm kiếm

Nhìn URL như một hợp đồng (Contract)

  • Một URL được thiết kế tốt đóng vai trò như hợp đồng tường minh giữa ứng dụng và người dùng
    • Làm rõ ranh giới giữa trạng thái công khai/riêng tư, client/server, chia sẻ/phiên làm việc
  • URL dễ đọc giúp diễn đạt ý định và có thể được cả con người lẫn máy móc hiểu được
    • Dạng example.com/products/laptop?color=silver&sort=price truyền đạt ý nghĩa rõ ràng hơn
  • Cải thiện hiệu quả cache
    • Cùng một URL được xem là cùng một tài nguyên nên tăng tỷ lệ cache hit
    • Có thể điều khiển các biến thể cache bằng query parameter
  • Quản lý phiên bản và thử nghiệm
    • Có thể phân biệt phiên bản API hay A/B test bằng ?v=2, ?beta=true, ?experiment=new-ui

Anti-pattern cần tránh

  • Chỉ giữ trạng thái trong bộ nhớ của SPA, khiến mất trạng thái khi refresh
  • Đưa thông tin nhạy cảm vào URL (?password=secret123)
  • Tên parameter mơ hồ (?foo=true&bar=2 thay vì ?mobile=true&page=2)
  • Mã hóa JSON phức tạp bằng Base64 làm URL dài quá mức
  • Vượt quá giới hạn độ dài URL (bị ràng buộc bởi trình duyệt, server và CDN)
  • Làm vô hiệu nút Back (có thể xảy ra khi lạm dụng replaceState)

Kết luận

  • Một URL tốt không chỉ trỏ tới nội dung mà còn thể hiện cuộc đối thoại giữa người dùng và ứng dụng
  • URL là phương tiện quản lý trạng thái lâu đời và thanh lịch nhất, chứa đựng ý định, ngữ cảnh và khả năng chia sẻ
  • Dù có nhiều công cụ quản lý trạng thái phức tạp như Redux, MobX, Zustand hay Recoil,
    đừng quên chức năng nền tảng là URL mới chính là sức mạnh thực sự của web
  • Một ứng dụng làm mất trạng thái khi refresh đang bỏ lỡ bản chất cốt lõi của web

2 bình luận

 
GN⁺ 2025-11-03
Ý kiến Hacker News
  • Khi review code, tôi cố lưu càng nhiều trạng thái (state) vào URL càng tốt
    Việc sau khi làm mới trang lại bị đưa đến một vị trí hoàn toàn khác, hoặc URL đã chia sẻ lại hiển thị một màn hình không liên quan, là điều khá xúc phạm từ góc nhìn người dùng
    Cách làm này khiến tốc độ phát triển chậm lại, nhưng giúp nâng cao nhận thức về UX trong nhóm và làm rõ view đang chứa bao nhiêu trạng thái
    Cũng có lo ngại rằng URL sẽ trở thành một dạng API công khai và tạo ra ràng buộc, nhưng tôi nghĩ phần lớn URL chỉ được dùng trong ngắn hạn nên không phải vấn đề quá lớn
    Nếu cần thì có thể xử lý bằng mã migration từ URL cũ sang URL mới khi tải trang

    • Tôi thích cách tiếp cận này, nhưng đôi khi tự động hoàn thành lịch sử của trình duyệt lại gọi về những trạng thái không mong muốn
      Tôi nghĩ dùng query parameter thay vì path sẽ tốt hơn một chút
    • Ứng dụng web dùng cho công việc mà tôi đang dùng tự tạo nút “quay lại” riêng, khiến nút quay lại của trình duyệt bị hỏng hoàn toàn
      Từ góc nhìn người dùng, từ “quay lại” gắn liền với nút của trình duyệt nên rất dễ gây nhầm lẫn
      Việc trạng thái bị đặt lại khi làm mới trang còn ít khó chịu hơn. Vì người dùng vốn có nhận thức “làm mới = bắt đầu lại từ đầu”
    • Với trang render phía server, khi làm mới thì vị trí cuộn sẽ tự động được khôi phục
      Nếu mọi thứ đều xử lý bằng JS thì các tính năng mặc định như vậy dễ bị hỏng một cách tinh vi
    • Tôi nghĩ thiết kế URL là một phần của thiết kế UX
      Nhưng dù đã làm việc với hơn 30 UX designer, tôi chưa từng nhận được hướng dẫn nào liên quan đến URL
    • Khi web phát triển, ý nghĩa của việc làm mới trang cũng thay đổi theo từng ngữ cảnh
      Đặc biệt trên mobile, rất khó đưa trang về trạng thái ban đầu nên làm mới là cách giải quyết nhanh nhất
      Với infinite scroll hay UI bộ lọc phức tạp, càng nhiều trạng thái nằm trong URL thì việc reset lại càng phiền phức
      Nếu UX đã gây khó chịu sẵn mà còn bắt người dùng phải dọn dẹp cả URL nữa thì đó là gấp đôi căng thẳng cho họ
  • Tôi cảm thấy ngay cả những người có hiểu biết số cao cũng hiểu chưa tốt về URL và DNS
    Họ nên có khả năng giảm rủi ro phishing, hiểu ý nghĩa của các tham số URL (?t=_, utm_), và loại bỏ thông tin cá nhân trước khi chia sẻ
    Họ cũng nên biết rằng biểu tượng ổ khóa HTTPS không có nghĩa là “đáng tin cậy”

    • Nhưng trình duyệt mặc định lại ẩn hoặc rút gọn URL, còn các công ty thì quảng bá bằng mã QR hoặc từ khóa tìm kiếm, nên rất khó giáo dục về điều này
  • Nếu dùng URL làm state container thì cấu trúc bên trong sẽ bị lộ ra, và cần quản lý phiên bản
    Cũng có thể phát sinh vấn đề về tương thích giữa các trình duyệt hoặc trong luồng xác thực
    Dù vậy tôi vẫn cố gắng đưa càng nhiều trạng thái vào URL càng tốt, giống như tham số dòng lệnh
    Tuy nhiên đây là một trade-off có chủ đích, chứ không phải vì thiếu hiểu biết hay thiếu kinh nghiệm

  • Tôi muốn giới thiệu Rison, một thư viện cũ nhưng vẫn hữu ích
    Nó cho phép lưu JSON vào URL một cách gọn gàng, và cũng được dùng trong Kibana của Elastic
    Ví dụ: http://example.com/service?query=q:'*',start:10,count:10

    • Đúng cái tôi đang tìm! Trước đây tôi từng tự chế tạm một thứ tương tự, nhưng cái này trông chuẩn hóa và gọn gàng hơn nhiều
  • Khi hệ thống phát triển thì cấu trúc trạng thái cũng thay đổi, nên đưa trạng thái vào URL sẽ tạo ra ràng buộc cho quá trình tiến hóa
    Vì URL về bản chất là một chuỗi mang tính lâu dài
    Thay vào đó, tôi nghĩ nên xem URL như một dạng protocol, rồi mã hóa/giải mã trạng thái trong đó
    Với những trang đơn giản thì cũng có thể đưa toàn bộ trạng thái vào URL

    • Với nội dung có thời gian tồn tại dài (ví dụ: bài blog), việc giữ trạng thái trong URL là hữu ích
      Nhưng với những thứ như feed thì còn tùy vào kỳ vọng của người dùng, chẳng hạn “khi làm mới có nên quay về trạng thái mới nhất không?”
    • Nếu áp dụng versioning thì có thể giảm bớt những vấn đề này
  • Giới hạn độ dài URL khác nhau tùy theo cấu hình của trình duyệt, server, CDN và công cụ tìm kiếm, nhưng thường là dưới 2000 ký tự
    Điều đáng suy nghĩ là trong giới hạn đó có thể chứa được bao nhiêu trạng thái, hay có cần cách tiếp cận khác hay không

    • Mỗi ký tự ngoài phần domain có thể dùng 66 giá trị (chữ hoa/thường, số, ký tự đặc biệt - . _ ~), nên mật độ thông tin thực ra khá cao
  • draw.io có thể lưu toàn bộ trạng thái vào URL để chia sẻ
    Dữ liệu sơ đồ được mã hóa bằng Base64, nên chỉ với một liên kết là có thể khôi phục hoàn toàn
    Tuy nhiên tôi không chắc điều này có đúng với định nghĩa “state container” hay không

  • Tôi dùng hash routing (#/dashboard) cho các ứng dụng self-host
    Vì không cần viết lại URL phía server (.htaccess v.v.), nên dù không hoàn hảo, nó vẫn giúp giảm bớt ràng buộc của môi trường triển khai

  • Microsoft Teams bản mới xử lý mọi màn hình bằng một URL duy nhất nên không thể bookmark
    Không thể mở trực tiếp một team hay channel cụ thể nên rất bất tiện

  • HATEOAS không được chú ý nhiều vì cái tên không hay, nhưng rốt cuộc nó vẫn là khái niệm nền tảng của web

    • Từ góc nhìn người dùng, việc đi theo liên kết và gửi form chính là HATEOAS
      Nhưng trong môi trường mà bạn kiểm soát cả server lẫn client thì nó chỉ tạo thêm độ phức tạp
      Đặc biệt nếu client vẫn phải biết cấu trúc endpoint, thì đó chỉ là làm cho URL trở nên mờ đục hơn mà thôi
    • Thực ra chủ đề này không liên quan trực tiếp đến HATEOAS. Cả hai đều dùng URL, nhưng HATEOAS không nói về lưu trạng thái mà nói về cấu trúc điều hướng
    • Nói đùa thôi, nhưng rốt cuộc HATEOAS hợp với cụm “cerealization” hơn là “serialized format”
 
ndrgrd 2025-11-03

Tôi hay dùng tính năng tab ngủ, nhưng với các web app cố định URL và hoạt động như một khối thì khi vào chế độ ngủ, thông tin sẽ bị mất.

Thế nhưng mấy trang web kiểu đó lại cái nào cũng nặng, nên cũng không thể không cho chúng ngủ được.