introduce
The Service Worker (hereinafter referred to as sw) essentially acts as a proxy server to create an effective offline experience by caching network request resources and intercepting and acting appropriately on network requests.
Sw is a worker registered with a specified source and path, it has no way to access the DOM, and sw is designed to be completely asynchronous so it does not block the main thread or other threads. However, there is no way to use XHR and localstorage apis, so you need to use Cache or IndexedDB apis.
For security reasons, the SW supports only HTTPS and Locahost hosting.
Use previous browser Settings
- Firefox Nightly: visit
about:config
And set thedom.serviceWorkers.enabled
The value is true; Restart the browser.- Chrome Canary: visit
chrome://flags
And openexperimental-web-platform-features
; Restart the browser (note: some features are not supported by default in Chrome);- Opera: visit
opera://flags
And openServiceWorker support
; Restart the browser.
Sw main event
- Install: sw Is triggered when the installation is triggered and usually caches resource files at this event.
- Activate: Triggered when sw is activated, usually in this event to update the resource cache.
- Fetch: SW fires when there is an HTTP request, usually intercepting the request at this event and doing the appropriate action.
Let’s start with examples
The project structure
Serviceworker | - index. The CSS page style. | - the index structure of the HTML page | - index. | js page logic - sw. Js sw logicCopy the code
File code Description
Index. HTML, simply build music search playing page, provide an input box and search button on the page to search for music, and display the searched music, click play to assign url value to audio.
<body>
<div>
<input type="text" id="tmusic">
<button id="sbtn">Search</button>
<audio id="player" src="" controls></audio>
<p id="history"></p>
</div>
<p id="restxt"></p>
<script src="./index.js"></script>
</body>
Copy the code
Index.css, which simply makes the page style, but here defines the background image of the button, referencing web resources
button {
color: #fff;
width: 100px;
height: 30px;
background-color: transparent;
border: none;
background-image: url('https://assets.asaki-m.com/button_1.png');
background-size: cover;
}
button:active {
background-image: url('https://assets.asaki-m.com/button_2.png');
}
Copy the code
Index.js, which encapsulates a register function, only registers sw in the Window load event to avoid loading sw.js and affecting the rest of the page’s load rendering.
The registration returns a Promise object, and then and catch are used to determine whether the sw was successfully registered.
function register() {
if ('serviceWorker' in navigator) {
// Register the service worker, scope is the scope of the work, default is the current directory
// Register returns a Promise object
navigator.serviceWorker
.register('/browser/serviceworker/sw.js', {
scope: '/browser/serviceworker/'
})
.then((reg) = > {
if (reg.installing) {
console.log('Service worker installing')}else if (reg.waiting) {
console.log('Service worker installed')}else if (reg.active) {
console.log('Service worker active')
}
})
.catch((err) = > {
console.log('register failed with: ' + err)
})
}
}
Copy the code
The waitUtil function ensures that the code in the waitUtil function will be executed before the sw is installed. Resources are not added to cache objects after the SOFTWARE installation is complete.
self.addEventListener('install'.function (evt) {
// waitUtil ensures that the code inside is executed before installation
evt.waitUtil(
// Cache resources to Caches named v1
caches.open('v1').then((cache) = > {
// Cache all required static resources
return cache.addAll([
'/browser/serviceworker/assetsImg/button_1.png'.'/browser/serviceworker/assetsImg/button_2.png'])}})))Copy the code
The HTTP request is then intercepted in the FETCH event and the appropriate action is taken. RespondWith (); respondWith(); respondWith();
When there is no cache in the cache, the fetch is used to initiate the request. This fetch is used to initiate the request instead of XMLHttpRequest. If the request is successful, the cache is updated and the new response is stored in the cache. The next time the request is accessed, it is read directly from the cache.
self.addEventListener('fetch'.function (evt) {
// The fetch event is raised when the request is initiated
evt.respondWith(
caches.match(evt.request).then(function (response) {
If the sw has already saved the response to the request, return the response directly, reducing the HTTP request
if(response ! = =undefined) {
return response
}
// There is no request to initiate
return fetch(evt.request).then((httpRes) = > {
if(! httpRes || httpRes ! = =200) {
// If the request fails, an error message is returned
return httpRes
}
// Make a copy of the response
const httpResClone = httpRes.clone()
// And save to the installation cache object
caches.open('v1').then((cache) = > {
cache.put(evt.request, httpResClone)
})
return httpRes
})
})
)
})
Copy the code
At this point, the simple implementation of sw offline function is complete, but some of the APIS used still want to see how to use MDN, rather than using Demo. sw-mdn
rendering
First visit: search the moon in the input box, click Search, render the data to the right, then click the first play, and click the player to play music, then the static resources and network requests of this visit will be cached by the SW.
Second access: the process is the same as the first one, but the resource is cached by sw, so it can be read from the cache, like the uncached is inaccessible.
extension
In the FETCH event, you can redirect to a custom error page if you don’t want to return a request error message, but this is done by caching the error page when Install is installed.
For example, here’s an example where I want to redirect to an error page like this:
self.addEventListener('install'.function (evt) {
evt.waitUtil(
caches.open('v1').then((cache) = > {
// Cache all required static resources
return cache.addAll([
'sw-test/404/index.html'
])
})
)
})
self.addEventListener('fetch'.function (evt) {
evt.respondWith(
caches.match(evt.request).then(function (response) {
return fetch(evt.request).then((httpRes) = > {
if(! httpRes) {// If the request fails, a 404 page in the cache is returned
return caches.match('sw-test/404/index.html')}return httpRes
})
})
)
})
Copy the code
Compare the Install event with the FETCH event:
The advantage of Install is that it only needs to be accessed once and can be accessed offline the second time. The disadvantage is that the urls that need to be cached are inserted into the script cache, which reduces the maintainability of the code.
The advantage of FETCH is that there is no need to change the compilation process. Requests sent by other scripts can be intercepted, even if modified, they will still be intercepted. The disadvantage is that the cache can only be cached after the request response is sent once.
Some other usage scenarios:
With SW you can also implement additional operations for certain requests, such as redirecting to another request for the same data if the request fails
It can also achieve A pre-loading effect. For example, when visiting page A, the subsequent B page will be cached. When users visit page B, they will feel smooth and fast, which is equivalent to optimizing the experience.
The renewal of the sw
When the software needs to be updated, the browser reads the modified sw.js file and compares it with the previous one. This triggers install to install the new version of sw.js. If no sw is used, the old SW will be replaced by the new SW.
But this new version of the sw updates, the page hasn’t been updated, obviously this is not logical, so will skip the waiting state, enter the activated state, execute skipWaiting () method to skip the wait and enter activate, then update the client, clear the old version of the cache.
self.addEventListener('install'.(evt) = > {
evt.waitUtil(evt.skipWaiting())
})
self.addEventListener('activate'.(evt) = > {
evt.waitUtil(
Promise.all([
// Update the client
self.clients.claim(),
// Clean up the old version cache
caches.keys().then(cachelist= > {
return Promise.all(
cachelist.map(cache= > {
if(cache ! = ='currentVersionName') {
return caches.delete(cache)
}
})
)
})
])
)
})
Copy the code
Caches are not removed without manual removal, just like Locastorage, which requires manual removal.
Another option is to call the update method when the sw is registered, for example
navigator.serviceWorker
.register('/browser/serviceworker/sw.js', {
scope: '/browser/serviceworker/'
})
.then((reg) = > {
// register success
reg.update().then(() = > {
// do something
})
})
.catch((err) = > {
console.log('register failed with: ' + err)
})
Copy the code
Open source SW library
- offline-plugin
- workbox
Refer to the article
www.wenjiangs.com/doc/iv0jiyd…
Juejin. Cn/post / 699690…
Developer.mozilla.org/zh-CN/docs/…