Hướng thành phần web

Thành phần web là các Phần tử tuỳ chỉnh, Shadow DOM và Mẫu HTML: các thành phần gốc của nền tảng cho các thành phần có thể sử dụng lại, không phụ thuộc vào khung, có mã đánh dấu và kiểu được đóng gói từ tài liệu. Sử dụng các thành phần này cho hệ thống thiết kế, tiện ích có thể nhúng và giao diện người dùng độc lập.

Các quy tắc dưới đây áp dụng cho tất cả các trường hợp. Truy xuất hướng dẫn cụ thể cho nhiệm vụ hiện tại; mỗi hướng dẫn đều nêu lại bối cảnh cần thiết và ghi lại các API cũng như những điểm bất lợi riêng.

Quyết định xem bạn có cần một phần tử tuỳ chỉnh hay không

Chỉ sử dụng phần tử tuỳ chỉnh khi bạn cần hành vi, vòng đời được đóng gói hoặc API dựa trên JS có thể sử dụng lại trên một thẻ. Đừng tạo kỹ năng khi:

  • Một phần tử HTML thuần tuý có CSS là đủ. Một <button> hoặc <details> được tạo kiểu sẽ dễ tiếp cận hơn và ít tốn kém hơn so với việc triển khai lại.
  • Thành phần này hoàn toàn mang tính trình bày và không có hành vi. Một lớp trên phần tử gốc sẽ có hiệu suất cao hơn và có thể xác định phạm vi bằng @layer hoặc @scope.
  • Bạn chỉ cần phạm vi kiểu trên máy chủ mà không cần tương tác; một biểu định kiểu trong phạm vi hoặc DOM bóng khai báo (không cần nâng cấp) có thể là đủ.

Việc xây dựng lại ngữ nghĩa gốc (một <my-button> tuỳ chỉnh tạo lại tiêu điểm, kích hoạt và ARIA) là cách sử dụng sai phổ biến nhất: bạn chỉ kế thừa khả năng hỗ trợ tiếp cận của nền tảng miễn phí nếu sử dụng lại phần tử gốc. Khi cần có một trình bao bọc kiểu <my-button> (thường là cho các điểm nối kiểu dùng chung, mã đánh dấu bổ sung hoặc hành vi mà một phần tử gốc không thể thể hiện), hãy bao bọc <button> thực thay vì triển khai lại (xem phần Tạo kiểu cho thành phần web để chuyển tiếp kiểu đến một phần tử gốc nội bộ).

Bài kiểm tra là liệu bạn có cần trạng thái đóng gói, API có thể viết tập lệnh hay hành vi mà phần tử gốc không thể thể hiện hay không — chứ không chỉ là một giao diện khác. Nút bật/tắt thích/yêu thích đủ điều kiện: nút này mang trạng thái miền được chốt, hiển thị trạng thái đó dưới dạng một điểm nối tạo kiểu cho trang xung quanh và gửi sự kiện change của riêng nút này — không có sự kiện nào trong số này được cung cấp bởi <button> được tạo kiểu. Một nút chỉ thay đổi màu khi được nhấn sẽ không: tiếp cận <button> + CSS. Khi bạn tạo thành phần, hãy lập mô hình trạng thái của thành phần đó bằng một trạng thái hoặc thuộc tính tuỳ chỉnh nếu phù hợp (xem phần Thành phần tuỳ chỉnh).

Chọn công cụ phù hợp với toàn bộ mã nguồn

Việc một thành phần có nên là một thành phần tuỳ chỉnh hay không phụ thuộc vào những gì dự án đã sử dụng. Các tác nhân có xu hướng trừu tượng hoá quá mức và tìm đến một phần tử mới quá sớm. Khi bạn nghi ngờ, lựa chọn nhẹ hơn (phần tử gốc, CSS, mẫu máy chủ) thường là lựa chọn phù hợp và bạn có thể nâng cấp lên một phần tử tuỳ chỉnh sau này nếu việc sử dụng lại hoặc hành vi thực sự yêu cầu. Heuristic, theo mức độ ưu tiên tương đối:

  • Toàn bộ mã nguồn đã có một mô hình thành phần (React, Vue, Svelte, …). Hãy mặc định sử dụng thành phần của khung đó cho giao diện người dùng trong ứng dụng. Chỉ sử dụng phần tử tuỳ chỉnh khi phần tử đó phải thoát hoặc tồn tại lâu hơn khung: một phần tử gốc của hệ thống thiết kế được chia sẻ giữa các ứng dụng hoặc khung, một tiện ích được thả vào mã đánh dấu mà khung không kiểm soát hoặc kết quả phải hoạt động dưới dạng HTML thuần tuý (email, CMS, mảnh SSR). Đừng thiết lập một hệ thống thành phần song song cho các chế độ xem ứng dụng thông thường trong ứng dụng.
  • Không có công cụ, chủ yếu là mã đánh dấu tĩnh. Ưu tiên HTML + CSS thuần tuý hoặc mẫu/phần mẫu của máy chủ. Một phần tử tuỳ chỉnh chỉ có giá trị khi bạn thực sự cần hành vi, vòng đời hoặc API dựa trên JS trên thẻ — không chỉ để chia sẻ mã đánh dấu, mà một phần tử bao gồm máy chủ hoặc <template> thực hiện mà không cần cung cấp bất kỳ JS nào.
  • Trình tạo trang web tĩnh có các thành phần tương tác. Hiển thị mã đánh dấu theo cách tĩnh; dành riêng phần tử tuỳ chỉnh cho phần tương tác. Đối với "nhiều mã đánh dấu, ít tính tương tác", hãy giữ mã đánh dấu trong Light DOM (hoặc Declarative Shadow DOM) và để một phần tử tuỳ chỉnh nhỏ dần dần cải tiến tăng dần mã đánh dấu đó, thay vì kết xuất lại mọi thứ từ JS.
  • Thay vào đó, bạn có thể đẩy một phần tử tích hợp sẵn đi xa hơn. Việc sử dụng một phần tử gốc (CSS, một vài trình nghe, phần tử giả thỉnh thoảng) thường được ưu tiên hơn phần tử tuỳ chỉnh, vì bạn giữ được khả năng hỗ trợ tiếp cận và hành vi của nền tảng mà không mất phí. Chuyển sang một phần tử tuỳ chỉnh khi các giải pháp thay thế không còn duy trì được nữa — khi bạn phải chống lại hành vi tích hợp của phần tử hoặc trải nghiệm biên soạn trở nên tệ hơn so với API thành phần sạch. Việc cần một chút JS trên một phần tử tích hợp sẵn không phải là lý do để thay thế phần tử đó.

Quy tắc chung

Những nguyên tắc này áp dụng cho mọi hướng dẫn bên dưới:

  • BẮT BUỘC: tên thẻ phải ở dạng kiểu viết kebab-case và có ít nhất một dấu gạch nối (ví dụ: my-element). Đây là điểm giúp phân biệt các phần tử tuỳ chỉnh với các phần tử tích hợp sẵn hiện tại và trong tương lai.
  • BẮT BUỘC: gọi super() trước trong hàm khởi tạo, trước khi chạm vào this; gọi hàm này sau bất kỳ câu lệnh nào đọc hoặc ghi this throws.
  • BẮT BUỘC: sử dụng các phần tử ngữ nghĩa (<button>, <nav>, <ul>) bên trong mẫu bóng. Hầu hết lỗi hỗ trợ tiếp cận trong cây bóng đều xuất phát từ mã đánh dấu không ngữ nghĩa; một vùng không thay đổi vai trò của nội dung mà vùng đó chiếu.
  • BẮT BUỘC: Các tham chiếu ARIA id (aria-labelledby, aria-describedby, aria-controls) không được vượt qua ranh giới bóng; phần tử tham chiếu và id mà phần tử đó trỏ đến phải nằm trong cùng một cây.
  • Ưu tiên attachShadow({ mode: 'open' }) để các công cụ cho nhà phát triển, tập lệnh trang và quá trình hydrat hoá máy chủ có thể đạt đến element.shadowRoot. Chỉ sử dụng 'closed' khi bạn phải ẩn cấu trúc nội bộ khỏi tập lệnh trang.
  • Tránh các phần tử tích hợp tuỳ chỉnh (extends HTMLButtonElement có is="…"); Safari đã từ chối vĩnh viễn việc cung cấp các phần tử này.

Ma trận hướng dẫn về trường hợp sử dụng

Xác định nhiệm vụ và truy xuất hướng dẫn:

Nếu bạn cần… Truy xuất
Xác định thẻ ngữ nghĩa mới có hành vi và vòng đời Phần tử tuỳ chỉnh
Xác định phạm vi kiểu và mã đánh dấu cho một thành phần, đồng thời đưa nội dung của người dùng vào thành phần đó Shadow DOM, Mẫu và Khe
Hiển thị cây bóng của thành phần trên máy chủ (SSR) mà không có nội dung chưa được tạo kiểu Hiển thị trước các phần tử tuỳ chỉnh
Tạo kiểu trên ranh giới bóng (giao diện, ::part, thuộc tính tuỳ chỉnh) Tạo kiểu cho thành phần web
Gửi giá trị của thành phần điều khiển tuỳ chỉnh đến <form> xung quanh Phần tử tuỳ chỉnh liên kết với biểu mẫu
Tăng khả năng hỗ trợ tiếp cận cho phần tử tuỳ chỉnh (vai trò, tiêu điểm, ARIA trên các gốc bóng) Thành phần web dễ tiếp cận