Đọc định dạng có chọn lọc: Một chế độ mặc định tốt hơn cho Async Clipboard API

Prashant Singh
Prashant Singh

Đã xuất bản: Ngày 3 tháng 7 năm 2026

Nếu từng sử dụng trình chỉnh sửa tài liệu, ứng dụng bảng tính hoặc bất kỳ ứng dụng nào mà việc dán nội dung từ bảng nhớ tạm là tương tác chính, thì bạn sẽ biết kỳ vọng của người dùng: thao tác dán phải diễn ra tức thì.

Rất tiếc là trong nhiều trường hợp, ứng dụng web đọc nhiều dữ liệu trong bảng nhớ tạm hơn mức cần thiết. Điều này có thể khiến thao tác dán nội dung diễn ra chậm hơn mức cần thiết, lãng phí chu kỳ CPU và sử dụng bộ nhớ cho dữ liệu mà ứng dụng không bao giờ cần.

Tính năng đọc định dạng có chọn lọc giải quyết vấn đề về hiệu suất này bằng cách sửa đổi API Bảng nhớ tạm không đồng bộ để đáp ứng tốt hơn kỳ vọng của người dùng và cung cấp những gì nhà phát triển cần – chỉ đọc các định dạng mà ứng dụng thực sự cần.

Vấn đề: Đọc trước tất cả định dạng dữ liệu

Khi người dùng sao chép từ bất kỳ ứng dụng nào (ví dụ: Excel, Google Tài liệu hoặc Photoshop), ứng dụng thường ghi một số biểu diễn của cùng một dữ liệu vào bảng nhớ tạm của hệ thống: văn bản thuần tuý, HTML, hình ảnh và đôi khi là các định dạng tuỳ chỉnh. Ứng dụng web đọc dữ liệu (khi người dùng dán từ bảng nhớ tạm) hiếm khi cần tất cả các biểu diễn này. Trình chỉnh sửa bảng tính có thể muốn HTML. Trình chỉnh sửa văn bản có thể muốn hiển thị lệnh "dán dưới dạng văn bản thuần tuý". Trình chỉnh sửa mã có thể chỉ muốn văn bản và không có gì khác.

Cho đến nay, navigator.clipboard.read() đã tìm nạp mọi biểu diễn có sẵn của dữ liệu từ bảng nhớ tạm của hệ thống, chạy quá trình làm sạch (có thể mất thời gian, đặc biệt là với các tải trọng HTML hoặc hình ảnh lớn) và giữ các byte trong bộ nhớ, ngay cả đối với các định dạng mà ứng dụng sẽ không bao giờ sử dụng. Tải trọng không dùng càng lớn thì người dùng càng phải đợi lâu và thẻ càng tiêu thụ nhiều bộ nhớ, ngay khi khả năng phản hồi của thao tác dán quan trọng nhất.

Giải pháp: Chỉ liệt kê, sau đó đọc theo yêu cầu

Từ Chrome và Edge 149, ClipboardItem các đối tượng do phương thức navigator.clipboard.read() trả về chỉ mô tả nội dung trong bảng nhớ tạm, thay vì cấp quyền truy cập ngay vào dữ liệu. Các byte thực tế cho một định dạng nhất định chỉ được đọc từ bảng nhớ tạm khi bạn gọi ClipboardItem.getType(mimeType). Giao diện API không thay đổi. Mã mà bạn đã viết ngày hôm qua vẫn hoạt động hôm nay, mang lại cho bạn lợi ích về hiệu suất ngay lập tức:

// Enumerate the available MIME types.
// No payload is fetched yet.
const items = await navigator.clipboard.read();

for (const item of items) {
  if (item.types.includes('text/html')) {
    // Only read the HTML data.
    // The data is fetched (and sanitized) here only.
     const blob = await item.getType('text/html');
     useThePastedHtml(await blob.text());
     break;
   }

   // text/plain, image/png, or any other custom formats
   // stored in the clipboard are never read.
}

Một lợi ích khác về hiệu suất ở đây là tính năng lưu vào bộ nhớ đệm sẽ tự động. Việc gọi getType('text/html') hai lần trên cùng một ClipboardItem chỉ đọc từ bảng nhớ tạm của hệ thống một lần và trả về Blob được lưu vào bộ nhớ đệm trong lần gọi thứ hai.

Lợi ích

Độ trễ. Công việc cần thực hiện khi navigator.clipboard.read() được gọi, chẳng hạn như sao chép byte trên các quy trình, làm sạch HTML hoặc giải mã hình ảnh, giờ đây chỉ xảy ra đối với các định dạng mà ứng dụng của bạn thực sự cần. Các ứng dụng chỉ đọc một định dạng sẽ tiết kiệm được nhiều nhất. Bạn bỏ qua các định dạng càng lớn thì lợi ích càng lớn. HTML có lợi nhất vì quá trình làm sạch HTML tốn kém.

Bộ nhớ. Trước đây, việc gọi navigator.clipboard.read() khi bảng nhớ tạm chứa hình ảnh 50 MB có nghĩa là cần sử dụng 50 MB trong bộ nhớ trình kết xuất của trang web cho đến khi ClipboardItem được phát hành, ngay cả khi ứng dụng chỉ cần biểu diễn text/plain của dữ liệu. Giờ đây, chỉ các định dạng mà ứng dụng của bạn đọc khi gọi ClipboardItem.getType() mới sử dụng bộ nhớ trong quy trình trình kết xuất. Đối với các ứng dụng thường xuyên dán, chẳng hạn như trình chỉnh sửa tài liệu, trình soạn thư hoặc công cụ thiết kế, điều này giúp giảm đáng kể mức sử dụng bộ nhớ trong quá trình dán, đặc biệt là trên các phiên nhiều thẻ, trong đó một số trình chỉnh sửa có thể đọc bảng nhớ tạm song song.

Không cần chọn nhận. Mọi trang web trên web đều tự động nhận được những lợi ích này. Bạn không cần di chuyển mã hoặc mã cổng phía sau logic phát hiện tính năng.

Một thay đổi về hành vi cần lưu ý

Việc đọc dữ liệu sau này sẽ gây ra một thay đổi nhỏ về hành vi: đối tượng ClipboardItem không còn là ảnh chụp nhanh cố định của bảng nhớ tạm tại thời điểm lệnh gọi đến navigator.clipboard.read() được giải quyết.

Khi read() được gọi và trả về ClipboardItem, mục này chỉ là một xử lý trì hoãn đối với dữ liệu trong bảng nhớ tạm của hệ thống. Nếu nội dung của bảng nhớ tạm thay đổi giữa lệnh gọi đến read() và lệnh gọi sau đến getType(), thì lệnh gọi đó sẽ bị từ chối bằng InvalidStateError. Điều này đúng ngay cả đối với định dạng mà bạn đã đọc thành công trước đó trên cùng một ClipboardItem.

// Read from the clipboard.
const items = await navigator.clipboard.read();
// Get the first ClipboardItem.
const item = items[0];

// Actually fetch the data now, which works fine.
const text = await item.getType('text/plain');

// The user writes something new to the clipboard
// by copying from another app, or your own code writes to
// the clipboard again.
await navigator.clipboard.writeText('something new');

// Fetching the data from the earlier ClipboardItem
// rejects with InvalidStateError, because the contents
// of the clipboard changed.
const html = await item.getType('text/html');

Điều này xảy ra vì việc trả về dữ liệu không còn phản ánh bảng nhớ tạm sẽ gây nhầm lẫn và dẫn đến các vấn đề khó gỡ lỗi.

Trong thực tế, thay đổi này không hiển thị đối với hầu hết mã, vì các ứng dụng thường gọi read()getType() liên tiếp trong cùng một trình xử lý thao tác dán. Tuy nhiên, bạn có thể muốn theo dõi các trường hợp có mã giữ lại ClipboardItem trên các ranh giới không đồng bộ.

Sau đây là ví dụ về trường hợp thay đổi này có thể trở thành vấn đề: lưu vào bộ nhớ đệm các đối tượng ClipboardItem trên window, hiển thị các đối tượng này trong giao diện người dùng lịch sử bảng nhớ tạm hoặc chờ tương tác của người dùng trước khi gọi getType(). Để tránh các vấn đề với một tình huống như thế này:

  • Gói các lệnh gọi getType() trong các khối try / catch và coi InvalidStateError là tín hiệu cho thấy bảng nhớ tạm đã chuyển sang và mã sẽ đọc lại.
  • Nếu bạn cần giữ lại dữ liệu trong bảng nhớ tạm, hãy sao chép Blob vào cấu trúc của riêng bạn ngay khi bạn getType(). Đừng coi ClipboardItem là bộ nhớ dài hạn.
  • Sử dụng sự kiện clipboardChange để chủ động vô hiệu hoá các trình xử lý lỗi thời.

Tiêu chuẩn và khả năng hỗ trợ trình duyệt

Tính năng đọc định dạng có chọn lọc là thay đổi về thông số kỹ thuật của API Bảng nhớ tạm do Nhóm làm việc về chỉnh sửa web và nhóm nền tảng web Microsoft Edge thúc đẩy.

Safari cũng hỗ trợ hành vi này và Firefox đã cho biết ý định và vị trí tiêu chuẩn tích cực để điều chỉnh.

Một cải tiến về hiệu suất và khả năng tương tác âm thầm nhưng ý nghĩa đối với API Bảng nhớ tạm, với Chromium hiện đang hội tụ về cùng một mô hình.