часто спрашивают в интервьюservice worker
содержания, но полной статьи в интернете нет
Введение
W3C
организация уже в2014
год5
предложено в месяцService Worker
такойHTML5 API
, который в основном используется для постоянного автономного кэширования.
в браузереjs
В одном основном потоке одновременно может выполняться только одно действие. Если часть кода требует слишком много времени, она всегда будет занимать основной поток браузера, что приведет к снижению производительности. Исходя из этого вопроса,W3C
придумалweb Worker
, передать задачи, которые занимают слишком много времениweb worker
, то черезpost Message
Сообщите основному потоку, что основной поток проходитonMessage
получил ответ.
ноweb Worker
Это временно, и результат каждой операции не может сохраняться долго, в следующий раз, когда будет сложная операция, ее нужно пересчитывать заново. Чтобы решить эту проблему,Service Worker
, относительноweb worker
Добавлена возможность автономного кэширования.
Service Worker
В основном имеют следующие особенности и функции:
- Автономный кеш
- сообщение
- запрос на перехват
как использоватьService Worker
регистр
Создаватьhtml
, присоединяйтесь к регистрации внизуservice Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js', {scope: '/'})
.then(() => {
console.log('Service Worker registration successful')
})
.catch((err) => {
console.log('Service worker registration failed')
})
})
}
Этот кодекс сначала судитservice Worker
Поддерживается ли он, то звоните егоregister
метод.
здесьscope
То, что написано на официальном вендане, это сделатьService Worker
Смущает подкаталог контролируемого контента, например мой каталог такой
scope
написано как./config
,Такservice-worker
будет только блокироватьconfig
в каталогеfetch
события, но упомянутые нижеcache.addAll
все еще кэшируется/
последующийindex.html
Содержание. также смservice worker
Он не обслуживает ни одной страницы, поэтому необходимо обратить внимание наservice worker
глобальные переменные, определенные в .service worker
в основном на основеpromise
Операция, когда регистрация завершена, генерируются обратные вызовы успеха и отказа, и результат можно увидеть в конце
Установить
зарегистрированservice Worker
после,service worker
будет установлен, запускаяinstall
событие, вinstall
Некоторые ресурсы могут кэшироваться в событии следующим образом:
// 监听service worker的install事件
this.addEventListener('install', (event) => {
// 如果监听到了service worker已经安装成功的话
// 就会调用event.waitUtil回调函数
event
.waitUntil(
// 安装成功后调用CacheStorage缓存,使用之前先通过caches.open()
// 打开对应的缓存空间
caches.open('my-test-cache-v1')
.then((cache) => {
// 通过cache缓存对象的addAll方法添加
return cache.addAll([
'/',
'/index.html'
])
})
)
})
Во-первых, следитьinstall
событие, называемоеwaitUntil
, этот метод в основном используется для продления жизни события, оно должно пройти вPromise
, необходимо дождаться внутреннего входящегоPromise
сталиresolve
. В основном здесь, чтобы продлитьservice worker
изinstalling
цикл, который достигается после завершения кэширования ресурсов.installed
Жизненный цикл.
Доступ к конкретному содержимому кэша можно получить черезApplication
изCache Storage
смотреть
Service Worker
Использовать кешированный код
запрос на перехват
Service Worker
Есть запросы на функции перехватывают, отправьте страницуHTTP
при запросе,service worker
в состоянии пройтиfetch
Событие перехватывает запрос и дает свой ответ, поэтому для безопасности нужно использоватьhttps
, чтобы написать конкретное содержимое ниже:
this.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((response) => {
// 如果 service worker有自己的放回,就直接返回,减少一次http请求
if (response) {
return response;
}
// 如果service worker没有返回,那就直接请求真实远程服务
var request = event.request.clone();
return fetch(request)
.then((res) => {
// 请求失败,直接返回失败的结果
if (!res || res.status !== 200) {
return res;
}
// 请求成功的话,将请求缓存起来
var responseClone = res.clone;
caches
.open('my-test-cache-v1')
.then((cache) => {
cache.put(event.request, respondClone)
})
return res;
})
})
)
})
слушай сначалаfetch
мероприятие, затем позвонитеevent.respondWith
, использование этой функции иwaitUntil
Аналогично, когда входящийPromise resolved
После этого соответствующийresponse
вернуться в браузер. а такжеcache
Сравните данные, чтобы увидеть, есть ли кешированный контент, если да, используйте кешированный контент, если нет, запросите удаленную службу. Эта часть кеша запросов требует внимания, потому чтоService Worker
Все запросы будут перехвачены, поэтому вам нужно обратить внимание, чтобы определить, какой контент нужно кэшировать, а какой нет, напримерajax
Нет необходимости в кэшировании. когда мы вернемсяindex.html
, ты можешь видетьindex.html
Непосредственно изservice worker
получено в
service worker
возобновить
Когда мы меняем стратегию кэширования,service worker
Как его обновить, в основном существуют следующие стратегии
-
service worker
документURL
Обновить -
service worker
содержимое файла изменено - Пользователь неактивен
24
Часы могут быть автоматически обновлены
Изменятьservice worker
изURL
Посмотрите, осуществимо ли это таким образом, при первом посещении, вsw1.js
Будуindex.html
Кэш, на этот раз мы меняемindex.html
Содержимое отображаемой страницы не изменилось, его необходимо изменить в данный момент.service worker
, мы можем выбратьsw1.js
изменить наsw2.js
, что означает перерегистрациюservice worker
, чтобы кэшировать новые файлы следующим образом:
service worker
документurl
Но нашел в браузере,index.html
не изменилось, потому что пользователь посетил сайт из-заsw1.js
Роль извлечения из кешаindex.html
Ссылка по-прежнемуv1
, а не то, на что мы ссылались после обновленияv2
, то кто-то сказал, что он напрямую не кешируетсяhtml
Если контента будет недостаточно, то приложение потеряет функцию автономного использования.
Изменятьservice worker
содержание документа
еслиsw.js
Содержимое обновляется, при посещении страницы сайта браузер получает новый файл и сравнивает его побайтно./sw.js
Если файл отличается, он подумает, что есть обновление, поэтому установит новый файл и запуститinstall
файл, а старый, который уже активен в это времяservice worker
еще на ходу, новыйservice worker
После завершения установки вы войдетеwaiting
условие. пока все открытые страницы не будут закрыты, старыйservice worker
автообновление, новоеservice worker
Это вступит в силу на следующей странице, которую вы снова откроете.
Например, вsw.js
Добавьте номер версии вservice worker
Обновить
var version = '0.0.1';
// 跳过等待,直接进入active
this.addEventListener('install', funciton (event) {
event.waitUntil(self.skipWaiting())
})
this.addEventListener('activate', function (event) {
event.waitUntil(
Promise.all([
// 更新客户端
self.clients.claim(),
// 清理旧版本
caches.keys().then((cacheList) => {
return Promise.all(
cacheList.map((cacheName) => {
if (cacheName !== 'my-test-cache-v1') {
return caches.delete(cacheName)
}
})
)
})
])
)
})
Сначала мы звонимself.skipWaiting
просто пропуститьinstalling
этап, взять на себя старыйservice worker
, если вы не выполните этот шаг, вы найдете
service worker
, вам нужно закрыть текущую страницу после новойservice worker
чтобы вступить в силу, так что звоните сюдаskipWaiting
перепрыгниinstalling
, непосредственно взять на себя старыйservice worker
, мониторactivate
, чтобы разобраться со старыми кэшами.Но есть проблема с этим, если есть следующий сценарий:
- страница
index.html
уже установленоold_sw
- Пользователь открывает эту страницу и все сетевые запросы проходят через
old_sw
процесс, страница загружается - потому что
service worker
Он имеет характеристики асинхронной установки.Как правило, когда браузер бездействует, он выполняет предложение.navigator.serviceWorker.register
. В это время браузер обнаружилnew_sw
, поэтому установка заставила его ждать - Однако из-за
new_sw
существуетinstall
этап имеетself.skipWaiting()
, поэтому браузер принудительно завершает работуold_sw
,Позволятьnew_sw
Активируйте и управляйте страницей прямо сейчас - если пользователь
index.html
Если есть сетевой запрос на последующие операции, он будетnew_sw
иметь дело с Очевидно, та же самая страница, первая половина созданаold_sw
контроль, в то время как вторая половина контролируетсяnew_sw
контроль. Это может привести к непоследовательному поведению между ними и неизвестной ошибке.
Ручное обновлениеservice worker
Как и выше, используйте номер версии для обновления
var version = '1.0.1'
navigator.serviceWorker.register('/sw.js')
.then((reg) => {
if(localStorage.getItem('sw_version') !== version) {
reg.update()
.then(() => {
localStorage.setItem('sw_version', version)
})
}
})
автоматическое обновление
Service Worker
В дополнение к тому, что обновление инициируется браузером, применяется специальная стратегия кэширования: если файл был24
часы не обновляются, когдаUpdate
Принудительное обновление при срабатывании. Это означает худший случайService Worker
Будет обновляться раз в сутки.
какhtml
кэшировать
упомянутый в приведенном выше примере дляservice worker
Для обновлений ресурсы могут кэшироваться в базе данных кэша. Однако в приведенном выше примере есть проблема: хотя мы говорим, что ресурсы кэшируются, отображение страницы — это измененное содержимое, когда требуется второе посещение. потому чтоhtml
В отличие от других статических ресурсов, которые имеют дайджест файла, поэтому необходимоhtml
Файлы обрабатываются особым образом.
Поскольку старые ресурсы все еще извлекаются из кеша в первый раз, у меня есть несколько идей для этой проблемы:html
Файл формата специально обрабатывается, при наличии сети новые ресурсы будут браться с сервера.html
Стратегия кэширования файлов обычно использует согласованное кэширование, r если нет сети, используйте кэшированныйhtml
Таким образом, страница, которую вы посещаете каждый раз, обновляется, а также достигается эффект автономного использования. Конкретная идея состоит в том, чтобыfetch
Это чистое суждение?html
Формат:
this.addEventListener('fetch', function (event) {
event.respondWith(
caches.match(event.request).then(function (response) {
var isHtml = /\.html/.test(reponse ? response.url : '');
var onLine = navigator.onLine;
// 如果没网,就全部使用缓存内容
if (!onLine) {
return response;
}
// 如果有网并且不是html,而且response存在,就返回response
if (!isHtml && response) {
return response;
}
// ...
})
Это гарантирует, что каждый раз, когда есть сетьhtml
все в курсе.
сообщение
У push-уведомлений очень широкие сценарии применения:
- Новые продукты на полках, push-уведомления пользователям, нажмите, чтобы перейти на страницу сведений о продукте
- Пользователь давно не заходил на сайт, а пуш информирует об обновлении сайта за это время
Используя push-уведомления, мы можем сделать наше приложение похожим на
Native App
Как улучшить пользовательский опыт
Получить авторизацию
Прежде чем подписаться на сообщение, вам необходимо получить разрешение пользователя на использование push-уведомлений.Конкретный код выглядит следующим образом:
navigator.serviceWorker.register('./sw.js')
.then((reg) => {
res.pushManager.getSubscription().then((subscription) => {
// 如果用户没有订阅
if (!subscription) {
subscribeUser(reg)
} else {
console.log('you have subscribed our notification')
}
})
})
Если вы уже подписались, следующее всплывающее окно больше не появится, если подписки нет, оно будет называтьсяsubscribeUser
Подключиться к пуш-сервису
В качестве источника сообщения сервер поручает службе push отправлять сообщения в браузеры, которые подписаны на сообщения, поэтому серверу необходимо сохранить уникальный идентификатор браузера.
используется здесьweb-push
Сгенерируйте открытый ключ и закрытый ключ, и открытый ключ будет отправлен в браузер черезservice worker
Создайте уникальный идентификатор и отправьте его на сервер. Сервер использует этот уникальный идентификатор для отправки сообщений в браузер.
function subscribeUser(registration) {
const applicationServerPublicKey = 'BKzIIoV8RgBqSlOZ5GMle3OY6rZoB-aaoRxldWN8jn5MZOXbtH5tFTchxDRW1jTSLTCOdNPfyk4Yszx0Lk1Clts';
const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
// 订阅
registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey,
})
.then((subscription) => {
$.post({
type: 'post',
url: 'http://localhost:3000/add',
data: {
subscription: JSON.stringify(subscription)
},
success: (res) => {
console.log(res)
}
})
})
.catch((err) => {
console.log('Failed to subscribe the user: ', err)
})
}
applicationServerPublicKey
только что использовалweb-push
сгенерированный открытый ключ, затем вызовитеpushManager.subscribe
Сгенерируйте уникальный идентификатор браузера и передайте его серверной части. этоsubscription
Содержимое длинное в следующем формате
{"endpoint":"https://fcm.googleapis.com/fcm/send/eAWELgsiTME:APA91bGZ4UwYtr26b0JE8K4sTNNFN8Z8GJ07QgDZHJP9aAqeMsjqiJJaaXd4Ype62vm5v4EjRnD0MuSD5ouBLYy6aT6nU5tWFpp5DjSjPmt_bh-h2Nm5pLo9-xY8H83Q8MHTynY7onKk","expirationTime":null,"keys":{"p256dh":"BLpOkRk1lLRXG8kMP3Yc4D6SUmz3aagln-ysP0lslwJsPA7SQhkmeytSFRCLZKBToBwMe3qRaUAMcJ0R3B1ZND4","auth":"pWaweBbyQqi5lNDR0Rqqew"}}
После получения этой информации сервер может использовать этот идентификатор для отправки сообщений в указанный браузер в будущем.
отправка на стороне сервера
Здесь тоже нужна помощьweb-push
Отправьте сообщение, потому что для отправки сообщения требуется помощь службы Google.FCM
услуга, которая недоступна из-за сети собственной сетиFCM
Служить. Поэтому я не могу использовать браузер Google здесь.Проверив много информации, я обнаружил, чтоfirefox
Такого же эффекта можно добиться, поэтому рекомендуется использоватьfirefox
Для реализации не будет проблем со стеной.
const webpush = require('web-push');
// push的数据
const payload = {
title: '一篇新的文章',
body: '点开看看吧',
icon: 'xx', // 图片链接
data: {
url: 'www.baidu.com'
}
}
const vapidKeys = {
publicKey: 'BKzIIoV8RgBqSlOZ5GMle3OY6rZoB-aaoRxldWN8jn5MZOXbtH5tFTchxDRW1jTSLTCOdNPfyk4Yszx0Lk1Clts',
privateKey: 'm5rk4Cann9l5pp7TiLPuNmL2Ho_zmIvgM3wz07EZSSs'
}
const pushSubScription = {
"endpoint": "https://updates.push.services.mozilla.com/wpush/v2/gAAAAABeaki7zwcdJ8r-2PZhwjyeCkHN3GaFAI4NQP8awz3e5svu0xDP6Peanq7iNTRd6S8weseu8JGpJDmLF1V2CcSZRExeWfLt0p5ksuNvCQmYnC4Bwy6wBzUGt-yQRAQMdq9_RKsEnadYfWAQt6LHENfaUr0gKcJJcj1Jb6vGfel-eqjEmjE",
"keys": {
"auth": "QyYLx2m29E-3a5kXzqdIDg",
"p256dh": "BEX1qgwC7MIRw-Vck7wsQPw5M8CIhkQ6thqs5ZwmPkXYy1zF-7sXvKE9hxeZtlm1rHd5lpvpjJf3q26rJje8zUc"
}
}
webpush
.sendNotification(pushSubScription, JSON.stringify(payload), {
vapidDetails: {
subject: 'mailto:18223306087@163.com',
...vapidKeys,
},
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err)
})
Во-первых, клиент должен сначала получить предварительно переданный намsubscription
и созданные ранееpublickKey
а такжеprivateKey
, а затем позвонитеwebpush.sendNotification
Проактивно push-сообщения
service Worker
мониторpush
После того, как сервер передаст информацию клиенту, нам нужноpush
События отслеживаются, а затем отображается эффект
self.addEventListener('push', function (event) {
console.log('push');
// var notificationData = event.data.json();
// var title = notificationData.title;
const title = 'push works';
const options = {
body: 'push is working',
icon: 'resource/logo.png',
badge: 'resource/logo.png'
}
event.waitUntil(self.registration.showNotification(title, options));
})
this.addEventListener('notificationclick', function(event) {
event.notification.close();
event.waitUntil(
clients.openWindow('https://baidu.com')
)
})
Здесь у нас естьpush
Отслеживайте, а затем получайте содержимое сообщения, активно отправленного сервером. Здесь просто создайте содержимое для простоты.
notificationclick
Событие, это в основном обратный вызов, созданный при нажатии на вышеуказанный контент. Например, в приведенном выше примере, если вы нажмете на это уведомление, вы перейдете на страницу Baidu.Таким образом, вы можете увидеть эффект.На самом деле, многие иностранные веб-сайты использовали эту технологию, но из-за стены эту технологию нельзя продвигать в Китае.
страница связи
service worker
не может работать напрямуюDOM
, Но можетpostMessage
Методы иWeb
Страница общается и позволяет странице работатьDOM
.
использоватьpostMessage
послать запрос
service worker
отправить данные: вsw.js
Чтобы отправить сообщение на страницу, используйтеclient.postMessage
, пример кода выглядит следующим образом:
this.clients.matchAll()
.then(function (clients){
if (clients && clients.length) {
clients.forEach((client) => {
// 发送数据
client.postMessage('sw update')
})
}
})
Данные отправки страницы: используются на главной страницеnavigator.serviceWorker.controller.postMessage()
отправить данные
if (navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage('home update')
}
Получить данные
существуетservice worker
Примите информацию с главной страницы в следующем примере:
self.addEventListener('message', function (event) {
console.log(event.data); // home update
});
Принять на главной страницеservice worker
Примером отправленной информации выглядит следующим образом:
navigator.serviceWorker.addEventListener('message', function (event) {
console.log(event.data)
});
использованная литература
Документация по PWA
understanding-service-worker-scope
Внимательно относитесь к обновлениям Service Worker
Добавьте push-уведомления в веб-приложения