發布日期:2026 年 7 月 3 日
如果您曾使用文件編輯器、試算表應用程式或任何應用程式,且從剪貼簿貼上內容是主要互動方式,您就會知道使用者的期望:貼上內容應該要感覺是即時完成。
遺憾的是,在許多情況下,網路應用程式讀取的剪貼簿資料量超出所需,導致貼上內容時感覺比實際慢,浪費 CPU 週期,並將記憶體用於應用程式永遠不需要的資料。
選擇性格式讀取會修改 Async Clipboard API,以更符合使用者期望,並提供開發人員所需的功能,也就是只讀取應用程式實際需要的格式,藉此解決效能問題。
問題:預先讀取所有資料格式
使用者從任何應用程式 (例如 Excel、Google 文件或 Photoshop) 複製內容時,應用程式通常會將相同資料的幾種表示法寫入系統剪貼簿:純文字、HTML、圖片,有時也會寫入自訂格式。讀取資料的 Web 應用程式 (使用者從剪貼簿貼上資料時) 很少需要所有這些表示法。試算表編輯者可能需要 HTML。文字編輯器可能會想公開「以純文字形式貼上」指令。程式碼編輯器可能只需要文字,不需要其他內容。
到目前為止,系統會從系統剪貼簿擷取navigator.clipboard.read()資料的所有可用表示法,執行清除作業 (這可能需要時間,尤其是 HTML 或圖片酬載較大的情況),並將位元組保留在記憶體中,即使是應用程式永遠不會使用的格式也一樣。未使用的酬載越大,使用者就必須等待越久,且分頁消耗的記憶體越多,而這時貼上作業的回應速度最為重要。
解決方法:只列舉,然後依需求讀取
從 Chrome 和 Edge 149 版起,navigator.clipboard.read() 方法傳回的 ClipboardItem 物件只會說明剪貼簿中的內容,不會立即提供資料存取權。只有在呼叫 ClipboardItem.getType(mimeType) 時,系統才會從剪貼簿讀取特定格式的實際位元組。API 介面維持不變。您昨天編寫的程式碼今天仍可運作,立即帶來效能優勢:
// 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.
}
此外,快取功能會自動運作,在同一個 ClipboardItem 上呼叫 getType('text/html') 兩次,系統只會讀取剪貼簿一次,第二次呼叫時則會傳回快取的 Blob。
優點
延遲時間:現在,只有應用程式實際需要的格式,才會執行在呼叫 navigator.clipboard.read() 時需要執行的工作,例如跨程序複製位元組、清除 HTML 或解碼圖片。如果應用程式只讀取單一格式,就能節省最多空間。跳過的格式越大,
獲得的增幅就越大。HTML 效益最大,因為 HTML 掃毒費用高昂。
記憶體。先前,如果剪貼簿含有 50 MB 的圖片,即使應用程式只需要資料的 text/plain 表示法,呼叫 navigator.clipboard.read() 也表示網頁的轉譯器記憶體必須使用 50 MB,直到 ClipboardItem 發布為止。現在,只有在呼叫 ClipboardItem.getType() 時應用程式讀取的格式,才會使用轉譯器程序中的記憶體。對於經常需要貼上的應用程式 (例如文件編輯器、郵件撰寫工具或設計工具),這項功能可大幅減少貼上作業期間的記憶體用量,尤其是在多個編輯器可能同時讀取剪貼簿的多分頁工作階段中。
無需啟用。網路上的每個網站都會自動享有這些優點。您不需要遷移程式碼,也不必在功能偵測邏輯後方閘道程式碼。
需要注意的一項行為變更
稍後讀取資料時,行為會略有不同:ClipboardItem 物件不再是 navigator.clipboard.read() 呼叫解析時剪貼簿的凍結快照。
呼叫 read() 並傳回 ClipboardItem 時,這個項目只是系統剪貼簿中資料的延遲控制代碼。如果在呼叫 read() 和稍後呼叫 getType() 之間,剪貼簿的內容有所變更,系統會拒絕該呼叫並傳回 InvalidStateError。即使您先前在同一個 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');
這是因為傳回不再反映剪貼簿的資料會造成混淆,否則會導致難以偵錯的問題。
在實務上,這項變更對大多數程式碼而言都是隱形的,因為應用程式通常會在同一個貼上處理常式中,連續呼叫 read() 和 getType()。不過,您可能想監控程式碼在非同步界線中保留 ClipboardItem 的情況。
以下是這項變更可能造成問題的範例:在 window 上快取 ClipboardItem 物件、在剪貼簿記錄 UI 中顯示這些物件,或等待使用者互動後再呼叫 getType()。如要避免發生這類問題:
- 將
getType()呼叫包裝在try / catch區塊中,並將InvalidStateError視為剪貼簿已移至其他位置的信號,程式碼應再次讀取。 - 如需保留剪貼簿資料,請在
getType()後立即將Blob複製到自己的結構中。請勿將ClipboardItem視為長期儲存空間。 - 使用
clipboardChange事件主動使過時控制代碼失效。
標準和瀏覽器支援
選擇性格式讀取是 Clipboard API 規格變更,由 Web Editing Working Group 和 Microsoft Edge 網頁平台團隊推動。
Safari 也支援這項行為,Firefox 則已表明積極採用標準,並有意與之保持一致。
Chromium 現在會採用相同模型,因此剪貼簿 API 的效能和互通性將有顯著提升。