WebWorker 即將被取代!JS 發布全新 API
背景
首先,簡單介紹一下背景。我們正在開發一個基于 Vue3 的項目,客戶習慣通過多個瀏覽器標簽頁同時打開相同的項目,這樣可以輕松地操作多個頁面。比如,客戶會這樣操作(雖然這只是一個簡化的例子,實際情況可能是兩個非常不同的頁面):
圖片
這種做法其實可以理解,因為一些客戶不喜歡頻繁地在同一標簽頁內切換菜單,他們認為這樣太麻煩了。
盡管這兩個頁面的布局和內容差異很大,但有一部分內容其實是相似的(雖然它們有所不同,因此沒有封裝成獨立組件)。
圖片
這部分相似的邏輯是依賴同一個接口和相同的數據處理邏輯的,而且這兩個過程都需要花費比較長的時間:
- 接口請求: 需要大約3000ms,因為后端數據獲取邏輯比較復雜,而且返回的數據量也很大。
- 數據處理: 大約300ms,前端拿到數據后還需要進行處理。
因此,客戶提出了一個建議:既然這兩部分內容的邏輯是一樣的,能否讓多個頁面(比如頁面二、頁面三、頁面四)共享頁面一的數據呢?
分析與優化
在客戶提出優化需求之前,我們先從優化點的角度來考慮一下。
接口請求和數據處理這兩個過程都是比較耗時且涉及大數據量的操作。一開始,我的思路是嘗試使用 WebWorker 來處理這些任務。
但是,客戶的建議讓我意識到 WebWorker 并不適用,原因如下:
- 每個標簽頁的 WebWorker 是獨立的,不能共享數據。
- 即便使用 WebWorker 和 IndexedDB 來緩存數據并嘗試共享,但依然很難共享數據的狀態。
第一個原因大家可能都理解。第二個原因則需要進一步解釋:為什么數據狀態共享如此重要呢?
我們可以通過下圖來理解這個問題。為了讓頁面二、頁面三、頁面四能夠知道頁面一的數據狀態,數據的狀態管理就變得至關重要。可以把數據狀態理解為類似于 Promise 的三種狀態:未緩存、緩存中和已緩存。
圖片
舉個例子來說明兩種可能的情況:
圖片
- 情況一: 頁面一加載后點擊按鈕發起數據請求并進行緩存,接著再點擊頁面二的按鈕。此時,頁面二應該能直接使用頁面一的緩存數據,這種情況就沒有問題。
- 情況二: 頁面一點擊按鈕1秒后,用戶點擊了頁面二的按鈕。此時,頁面二并不知道頁面一的數據狀態,無法判斷是否需要發起新的請求,或者應該等待頁面一的請求完成。
因此,共享數據狀態顯得尤為重要。
SharedWorker
經過分析,我決定放棄 WebWorker 的方案,改用 SharedWorker 來實現數據共享。
圖片
SharedWorker 是什么呢?它可以被理解為類似于 WebWorker,但有一個關鍵的不同點:SharedWorker 允許多個標簽頁共享,而 WebWorker 每個標簽頁都獨立。
當多個標簽頁連接同一個 SharedWorker 時,SharedWorker 會通過port
管理每個標簽頁,每個標簽頁對應一個port
。這樣,通過 SharedWorker,多個標簽頁可以共享數據。
為了說明 SharedWorker 的工作原理,下面是一個簡單的例子,演示如何通過 SharedWorker 實現多個頁面間的count
共享。
圖片
圖片
如上圖所示,count
變量在多個標簽頁間共享。
圖片
接下來,我們修改shared-worker.js
中的邏輯,讓其負責數據請求和處理:
圖片
圖片
然而,經過測試,我們發現目前的實現并沒有達到預期的效果!雖然共享了count
數據,但如果我們按順序點擊頁面一和頁面二,數據并不會同時出現,反而出現了請求重復的情況,這樣并沒有優化到位。
解決方案
此時,我們意識到需要實現數據狀態共享,這可以通過利用 Promise 來完成。通過 Promise,頁面可以等待數據請求和處理完成,然后再進行后續的操作。
圖片
最終效果如下:
圖片
這樣,即使用戶沒有同時點擊頁面一和頁面二,只要頁面一的數據請求和處理完成,頁面二也能同時獲取到已經緩存的數據。
通過這種方式,我們成功地實現了多個頁面之間的數據共享,并且保證了數據狀態的同步。