<font id="zqva1"></font>
<rt id="zqva1"></rt>
  • <tt id="zqva1"></tt>
    <cite id="zqva1"></cite>

    <cite id="zqva1"><noscript id="zqva1"></noscript></cite>
      <rp id="zqva1"><meter id="zqva1"></meter></rp>

        <cite id="zqva1"></cite>
          <b id="zqva1"></b>
          <rp id="zqva1"></rp>
          <cite id="zqva1"></cite>

          <rt id="zqva1"></rt>

        1. <rp id="zqva1"></rp>

          Service Worker的生命周期

          時間:?2017-12-07閱讀:?1613標簽:?Worker

          Note: 翻譯自 The Service Worker Lifecycle - by Jake Archibald, Jake Archibald參與了service worker標準的制定,也是該技術的推動者。(下文括號里的內容非作者原文)

          service worker的生命周期是它最復雜的部分。如果你不知道它在努力做什么和這么做的優勢,你會感到它在跟你對著干。但一旦你知道了它的原理,你就可以給用戶提供無縫的,優雅而不突兀的更新。一種同時具備網站應用和原生應用優勢的體驗。

          這是一次深入的探索,但要點會在每個部分開頭指出。

          目的

          生命周期的目的是:

          • 使離線優先成為可能
          • 允許service worker在版本更新時不影響當前版本的運行
          • 確保一個網站總是被 一個 service worker控制(或者沒有被控制)
          • 確保同時只有一個版本的網站在運行

          最后一點是很重要的。沒有service worker,用戶可能在一個標簽中打開你的網站,然后再在另一個標簽打開。這可能會導致網站的不同版本同時運行。有時這樣做并無大礙,但是當你需要處理數據時,不同版本的同一網站共享著同樣的存儲空間(你用service worker把數據緩存在本地,通常緩存是按網站來管理的),而它們可能要求用不同的方式來管理這些數據(刪除,增加和更新)。這會導致錯誤甚至數據丟失。

          注意:用戶很不喜歡數據丟失。這會讓他們很傷心

          首次加載的 service worker

          概括來說:

          • install事件是service worker得到的第一個事件,而且只會發生一次
          • 傳入installEvent.waitUntil()的promise對象決定了service worker安裝的時長和成功或失敗的情況
          • service worker不會接收fetch 和push事件直到它被成功的安裝并變為活動狀態
          • 默認情況下,只有網頁本身由service worker得到(由它緩存并從緩存中返回給請求,與之相對的是從網絡中得到),它的請求才會被service worker捕獲。所以你需要刷新網頁才能看到它的效果
          • clients.claim()可以改變默認值,然后控制沒有被控制的網頁

          看以下的html代碼

          <!DOCTYPE html>
          An image will appear here in 3 seconds:
          <script>
            navigator.serviceWorker.register('/sw.js')
              .then(reg => console.log('SW registered!', reg))
              .catch(err => console.log('Boo!', err));
          
            setTimeout(() => {
              const img = new Image();
              img.src = '/dog.svg';
              document.body.appendChild(img);
            }, 3000);
          </script>
          

          它做的是注冊一個service worker,3秒后添加一個狗的圖片。

          下面是他的service worker

          self.addEventListener('install', event => {
            console.log('V1 installing…');
          
            // 緩存貓貓圖片
            event.waitUntil(
              caches.open('static-v1').then(cache => cache.add('/cat.svg'))
            );
          });
          
          self.addEventListener('activate', event => {
            console.log('V1 now ready to handle fetches!');
          });
          
          self.addEventListener('fetch', event => {
            const url = new URL(event.request.url);
          
            // 如果請求是同源的并且路徑是 '/dog.svg'
            // 則從緩存中拿出貓的圖片'/cat.svg'給那個請求
            if (url.origin == location.origin && url.pathname == '/dog.svg') {
              event.respondWith(caches.match('/cat.svg'));
            }
          });
          

          上面的代碼會緩存一個貓的圖片,當頁面請求/dog.svg的時候給它。然而,運行上述代碼,你會首先看到狗的圖片。刷新頁面后你才會看到貓的圖片。

          提示:貓就是比狗可愛。

          作用域和控制

          service worker注冊的默認作用域是./相對于它自己所在的路徑(你可以通過navigator.serviceWorker.register('/sw.js', { scope: '/'})第二個參數來明確指定一個scope)。這意味著如果你的service worker的URL是//example.com/foo/bar.js它的默認作用域就是//example.com/foo/。

          我們把頁面,workers,和shared workers(一種worker,這東西也有自己的api)稱為clients。你的service worker只能控制作用域之內的client,一旦client被控制,它發出的fetch請求會被作用域內的service worker捕獲,你可以在此時介入。你可以使用navigator.serviceWorker.controller方法來判斷一個client是否被控制了,這個方法的返回值是null或者一個service worker實例。

          下載,解析和執行

          當你調用.register()方法時(第一個參數是service worker的URL),service worker會被下載。如果下載失敗或者解析(parse)失敗或者在首次執行時出現錯誤(throw error in initial execution),register返回的promise會被reject。service worker會被忽略(abandoned -> redundant)。

          chrome開發者工具會把錯誤顯示在console和application tab中的service worker部分。

          安裝

          service worker得到的第一個事件是install。當service worker開始執行時會立即觸發這個事件,每個service worker只觸發一次。開發者可以對其進行更新,瀏覽器認為這個更新是全新的service worker,它會得到一個安裝事件(每次加載頁面瀏覽器會檢查service worker的更新)。后面會詳細講它的_更新_階段。

          安裝事件發生時就是你緩存在控制client之前需要的資源的時候。傳入event,waitUntil()方法的promise對象會告訴瀏覽器什么時候安裝結束,以及安裝是否成功。(fulfilled -> 安裝成功,rejected -> 安裝失敗)。

          如果promise rejected,代表安裝失敗,瀏覽器會拋棄那個service worker,他不會控制clients。在上面的例子的service worker代碼中,這意味著我們可以確保service worker得到fetch事件時(第14行)cat.svg會作為頁面的依賴出現在緩存中(安裝成功意味著安裝過程中的緩存文件操作必然成功,因此開發者應該在這個階段緩存頁面的依賴,即頁面加載必要的資源)。

          激活

          一旦你的service worker準備好了控制clients并可以處理一些功能性事件例如push和sync(service worker能做很多強大的事),你會得到一個activate事件。但這不代表調用register()方法的頁面會被立即控制。

          當你首次打開上面的例子時,即使dog.svg的請求發生在service worker的activate事件之后,service worker還無法處理這些請求,你會看到狗的圖片。默認規則確保了一致性,如果你的頁面不是service worker返回的(用什么中文詞來合適的代表serve?)的,頁面的請求也不會由它來介入。如果你刷新上面的例子,service worker才會控制頁面以及它發出的請求(這時頁面本身就是service worker提供的)。頁面和圖片的請求都會被service worker監聽(和介入),你會看到貓的圖片。

          clients.claim

          你可以調用clients.claim()方法在service worker激活后立即控制未被控制的clients。

          這里有個上面例子的變種,它會在service worker的激活狀態調用clients.claim方法。你_應該_第一次加載就能看到貓的圖片。我說“應該”是因為這與時間有關。只有當service worker處于活動狀態并且clients.claim在頁面試圖下載圖片之前生效你才會看到貓的圖片。

          Note: 我看很多人把clients.claim()放在他們的boilerplate中,我自己很少這么做。這樣做只在首次加載時有效果,由于漸進增強(遵循這個原則,開發者不會把service worker當作頁面運行的依賴)的功勞,沒有service worker的網頁照樣正常的運行。

          更新 service worker

          概括來說:

          • 下列情況會觸發一次更新(瀏覽器檢查service worker的更新)
            • 訪問作用域下的頁面
            • 當`push`和`sync`等功能性事件發生時,除非在之前的24小時之內做過更新檢查
            • 在service worker的URL改變后調用`register()`
          • 你可以設置service worker的http緩存時間(response headers 中的`Cache-Control`),24小時之內瀏覽器會尊重這個緩存時間(如果你設置的緩存時間為25小時,但在24小時過后上面的三種情況之一發生,瀏覽器就會更新service worker)。我們(標準制定者)將來會將取消強制的24小時更新行為。多數情況下,你應該把service worker腳本的`max-age`設為0
          • 你的service worker在更新時,新文件只要跟瀏覽器已有的文件有一個字節的不同,就會被當作更新。我們會把這樣的政策擴展到導入(imported)的模塊/腳本
          • 更新的service worker會和舊的service worker并存,并得到自己的`install`事件
          • 如果新的worker未被成功下載,或者解析錯誤,或者在運行時出錯,或者在安裝階段不成功,新的worker會被丟棄,舊的會被保留
          • 一旦新的worker被成功安裝,更新的worker會進入等待狀態(只有有舊的worker控制clients時才有等待狀態),直到舊的worker控制的clients數量為0
          • `self.skipWaiting()`會跳過等待,直接讓新的worker在安裝后進入激活狀態

          下面我們更改我們的service worker的腳本,使它用馬的圖片來響應fetch

          const expectedCaches = ['static-v2'];
          
          self.addEventListener('install', event => {
            console.log('V2 installing…');
          
            // 把馬的圖片存入新的緩存, static-v2
            event.waitUntil(
              caches.open('static-v2').then(cache => cache.add('/horse.svg'))
            );
          });
          
          self.addEventListener('activate', event => {
            // 刪除不需要的緩存 static-v1
            event.waitUntil(
              caches.keys().then(keys => Promise.all(
                keys.map(key => {
                  if (!expectedCaches.includes(key)) {
                    return caches.delete(key);
                  }
                })
              )).then(() => {
                console.log('V2 now ready to handle fetches!');
              })
            );
          });
          
          self.addEventListener('fetch', event => {
            const url = new URL(event.request.url);
          
            // serve the cat SVG from the cache if the request is
            // same-origin and the path is '/dog.svg'
            if (url.origin == location.origin && url.pathname == '/dog.svg') {
              event.respondWith(caches.match('/horse.svg'));
            }
          });
          

          Note: 我對馬無感

          點擊這里查看上面代碼的demo,你仍然會看到貓的圖片,下面是原因:

          安裝

          注意到我把緩存的名稱從static-v1改成了static-v2。這意味著我可以重設一個緩存空間,而不必覆蓋當前的緩存,舊的service worker還在使用這個緩存。

          這種模式創造了版本特定的緩存(每個版本的service worker有自己的緩存空間),就像原生應用把它的可執行文件和它的靜態資源一起打包發布版本。當然你也可以設置非版本特定的資源,例如縮略圖等。

          等待

          更新的service worker在被成功安裝后,會延遲激活,直到舊的service worker不再控制clients。這個狀態叫等待狀態(waiting state)。瀏覽器用這種方法來保證同時只有一個service worker在運行。

          刷新頁面不足以讓新的service worker控制頁面。這是因為瀏覽器轉到新頁面時,當前頁面在請求的header被收到之后才會消失,甚至在那時舊的頁面還可能會停留因為請求的header里包含了Content-Disposition。因為這種__重疊__,當前的worker在刷新時總是控制著頁面。

          要想得到更新,你得關掉這個標簽或者先轉到別的網站,然后你再打開這個網站(“重啟”這個網站),你就會看到新的worker生效了。

          這種模式和許多應用程序的更新模式差不多(比如chrome),它會在后臺下載好更新,當它重啟后更新才會被安裝生效。同時,你可以繼續使用當前的版本。然而,這在開發時很不方便(因為你需要做很多實驗),但是開發者工具提供了解決方法,這個我在下面會提到。

          激活

          一旦舊的service worker不再生效,更新的版本就會開始控制clients。這時最好做一些舊版本仍在生效時不能做的事,例如遷移數據庫和清理緩存。

          在上面的例子中,我設置了需要緩存的文件的數組,在activate事件中我清除其他緩存(之前版本的)。

          注意: 用戶可能跨版本升級。

          如果你給event.waitUntil()傳入一個promise,它會推遲functional event(fetch, push, sync等等)直到promise resolve。因此當你的fetch事件發生時,激活已經完成了。

          注意:cache storage API是同源存儲(像localstorage, 和indexedDB)。如果你在同源上運行很多網站,當心不要誤刪除了其他網站的緩存。你可以給你的緩存加一個以網站名區分的獨特的前綴。

          跳過等待階段

          更新worker的等待階段意味著你同時只能運行一個版本的同個網站,但如果你不需要這項功能,你可以調用self.skipWaiting()方法使新的worker提前激活。

          這將導致新的worker趕走當前活動的worker,然后在進入等待階段后立即激活(已在等待階段則會立即激活)。這不會使你的worker跳過安裝階段。

          你在何時調用skipWaiting()其實不重要,只要在等待階段中或者在等待前。在安裝階段調用是通常的做法:

          self.addEventListener('install', event => {
            self.skipWaiting();
          
            event.waitUntil(
              // caching etc
            );
          });
          

          但是你應該讓這種行為基于用戶的行為,在用戶點擊某個按鈕后,給你的worker發送postMessage()。

          這里有個例子使用了skipWaiting()。代開后你應該會看到一幅牛的圖片。就像clients.claim() 這種情況也與時間相關,因此你只有在新的worker安裝并激活開始介入fetch等發生在頁面加載圖片之前才能看到牛的圖片。

          注意:skipWaiting()意味著新的worker可以控制用舊版本的worker加載的頁面。這意味著有的網絡請求是被舊的worker處理了,而新的worker會處理它控制頁面后的請求。這種情況可能會造成破壞,請謹慎使用skipWaiting()。

          手動更新

          我之前提到過,瀏覽器會在訪問頁面和事件后自動檢查service worker的更新,但你也可以手動觸發更新:

          navigator.serviceWorker.register('/sw.js').then(reg => {
            // sometime later…
            reg.update();
          });
          

          如果你預料到你的用戶可能會長時間使用你的網站而不關閉或重新加載,你可能想要定時使用update()方法檢查更新。

          避免更改service worker腳本的地址

          如果你讀我我的文章《緩存最佳實踐》(Caching best practices & max-age gotchas),你應該考慮給每個版本的service worker一個獨特的URL。__別這么做!__這么做通常是壞實踐,盡量在當前URL更新。

          設想下面的場景:

          1. index.html注冊了sw-v1.js作為它的service worker
          2. sw-v1.js緩存并serveindex.html讓它離線優先的工作
          3. 你更新了index.html好讓它注冊新的worker:sw-v2.js

          如果你像上面那么做,用戶永遠得不到sw-v2.js,因為sw-v1.js總是會從緩存中拿出舊的index.html來響應對其的請求。你讓自己陷入了這樣的狀況,你得在sw-v1.js做更新來讓用戶得到sw-v2.js,好蠢。

          讓開發容易

          設計service worker的生命周期是以用戶為中心的,但它會成為開發過程的阻礙。幸虧有一些開發工具可以幫助你:

          重載時更新

          這會更改service worker的生命周期,使其變得開發友好。每次加載網頁都會:

          1. 重新下載service worker
          2. 把它當作新版本安裝,即使它本身沒有改變,意味著install事件的代碼會運行,緩存會被更新
          3. 跳過等待,讓新的worker激活
          4. 導航到頁面

          著意味著每次你訪問頁面(包括刷新)都會得到更新,而不必刷新兩次或者關閉標簽。

          跳過等待

          如果有個worker正在等待,你可以點擊skipWaiting使它立即激活

          按shift刷新

          如果你硬性重新加載頁面,它會完全繞過service worker。頁面不會被控制。這個功能在標準文檔中,所以這個在別的支持service worker的瀏覽器也有效。

          處理更新

          service worker的設計是為使它成為可擴展web平臺(extensible web)的一部分。核心思想是,作為瀏覽器開發者,我們不比web開發者更擅長web開發。因此我們不應該提供狹隘的,只解決特定問題的API,而應該讓開發者利用瀏覽器的核心,按自己的方法來做,按更適合自己的用戶的方式來做。

          因此,為了使更多的開發模式成為可能,service worker的整個更新周期都是可見的:

          navigator.serviceWorker.register('/sw.js').then(reg => {
            reg.installing; // 正在安裝的 worker, 或者 undefined
            reg.waiting; // 處于等待階段的 worker, 或者 undefined
            reg.active; // 激活狀態的 worker, 或者 undefined
          
            reg.addEventListener('updatefound', () => {
              // 有一個service worker正在安裝
              const newWorker = reg.installing;
          
              newWorker.state;
              // "installing" - install 事件被觸發, 但沒有完成
              // "installed"  - 安裝完成
              // "activating" - activate 事件被觸發, 但沒有完成
              // "activated"  - 完全激活
              // "redundant"  - 被忽略。安裝失敗或者被新的worker取代
          
              newWorker.addEventListener('statechange', () => {
                // newWorker的狀態發生改變
              });
            });
          });
          
          navigator.serviceWorker.addEventListener('controllerchange', () => {
            // 控制頁面的service worker發生改變后會觸發這個事件
            // 例如:一個新的service worker跳過了等待階段變成活躍的worker
          });
          

          總結(非作者原文)

          作者在文章開始說了生命周期的四個目的,我們現在看一下這些目的是如何達成的:

          使離線優先成為可能

          開發者在service worker安裝階段緩存頁面的依賴,當用戶再次導航到頁面,它會直接響應請求,返回緩存中的文件。這樣使得用戶再次加載頁面的速度大大提升,也意味著你更新了頁面也必須更新一下service worker使更新生效。

          此外,除了頁面依賴,頁面請求的其他數據你也可以用service worker緩存到本地(一旦頁面被service worker控制,它能監聽到頁面發出的任何請求),用戶再次打開你的應用,首先顯示已緩存的資源,然后進行網絡請求。

          允許service worker在版本更新時不影響當前版本的運行

          用戶導航到頁面,瀏覽器會檢查service worker的更新,這時更新的service worker只會執行安裝操作,然后進入等待狀態,不打斷當前版本的運行。這時開發者可以通知用戶有更新,然后讓用戶決定是否更新。你可以用skipWaiting()方法跳過等待,然后刷新頁面,讓舊的版本消失。

          這時,新的worker處于激活狀態,我們也只有在這時才能清理之前的緩存,遷移數據庫。

          確保一個網站總是被 一個 service worker控制(或者沒有被控制)

          網站不會被多個service worker控制,即使你使用skipWaiting()方法,這也會首先廢棄舊的service worker,然后才讓新的worker生效。

          確保同時只有一個版本的網站在運行

          總結來說就是,瀏覽器檢測service worker的更新,而開發者在service worker中更新頁面(寫入新資源的url,讓瀏覽器緩存)。

          事實上,service worker的版本就是網站的版本,由于網站同時只能有一個service worker控制,因此你也只能同時運行一個版本的網站。

          同時,不同版本的緩存也不會起沖突,因為worker總是在安裝階段緩存資源,在激活階段就會清除無用的緩存(開發者自己編程實現)。

          來源:https://jimmyqsc.github.io/sw-life-cycle
          吐血推薦

          1.站長廣告聯盟: 整理了目前主流的廣告聯盟平臺,如果你有流量,可以作為參考選擇適合你的平臺點擊進入...

          2.休閑娛樂: 直播/交友    優惠券領取   網頁游戲   H5游戲

          鏈接: http://www.modern-decoration.com.cn/article/detial/234

          動態創建 Web Worker 實踐指南

          作為前端,在消費接口提供的數據時,往往由于數據實際分布在不同地方(如一部分存儲在 ODPS ,而另一部分可能更適合在應用初始化時從本地載入內存)而需要對數據進行區分處理。當然,交互的實現可能也會需要很重的計算邏輯

          Node.js中的Worker Threads

          想要明白workers,首先需要明白node是怎樣構成的。當一個node進程開始,它其實是:一個進程:是指一個全局對象,這個對象能夠訪問任何地方,并且包含當前處理時的此時信息。

          Web Worker模擬選票

          思路:五個人(5個div窗口模擬)同時進行搶票,有百分之十的幾率可以搶到票,搶到票后對應的窗口(即隨機生成的數大于等于0小于9的情況)會編程天藍色,沒搶到票的窗口(即隨機生成的數大于9小于100的情況)會變成紅色

          web worker是什么?理解并使用web worker

          Web Worker 是為了解決 JavaScript 在瀏覽器環境中沒有多線程的問題。正常形況下,瀏覽器執行某段程序的時候會阻塞直到運行結束后在恢復到正常狀態,而HTML5的Web Worker就是為了解決這個問題,提升程序的執行效率。

          Web Worker 詳細介紹_Web Workers的使用

          web worker 是運行在后臺的 JavaScript,獨立于其他腳本,也就是說在Javascript單線程執行的基礎上,開啟一個子線程,進行程序處理,而不影響主線程的執行。Service Worker 是一個由事件驅動的 worker,它由源和路徑組成,以加載 .js 文件的方式實現的。

          fly63.com版權所有,內容以共享、參考、研究為目的,不存在任何商業目的。其版權屬原作者所有,如有侵權,請與小編聯系!情況屬實本人將予以刪除!

          文章投稿關于web前端網站點搜索站長推薦網站地圖站長QQ:522607023

          小程序專欄: 土味情話心理測試腦筋急轉彎幽默笑話段子句子語錄成語大全

          国产精品高清视频免费 - 视频 - 在线观看 - 影视资讯 - 唯爱网