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























