Բոլոր դասերը
FrontendՄիջին12 րոպե ընթերցումԻմ ստեկում

PWA-ներ և service worker-ներ

Տեղադրվող վեբ հավելվածներ, որոնք արագ են բեռնվում և աշխատում են offline. manifest, service worker, քեշավորում։

  • Manifest
  • Service worker
  • Offline
  • Install
  • Push
Քայլ 1/4
Էջի ամեն request անցնում է service worker-ի՝ դռնապանի միջով։ Online ռեժիմում նա բերում է ցանցից և պատճենը պահում cache-ում։

30 վայրկյանում

Progressive Web App-ը (PWA) կայք է, որը կարելի է տեղադրել հավելվածի պես, բացվում է իր սեփական պատուհանում և շարունակում է աշխատել վատ ցանցի կամ ցանցի բացակայության դեպքում։ Դա հնարավոր է դարձնում երկու բաղադրիչ. web app manifest-ը, որը նկարագրում է հավելվածի անունը, պատկերակները և գույները, և service worker-ը՝ ֆոնային script, որը կանգնած է էջի և ցանցի միջև և որոշում է, թե ինչ մատուցել քեշից։ Պահում եք մեկ կոդային բազա և մեկ URL, և վրան ավելացնում հավելվածի նման վարքագիծ։

Ինչու եմ օգտագործում

  • Իմ սեփական պորտֆոլիոն կառուցել եմ որպես տեղադրվող PWA, ուստի manifest-ի, պատկերակների և service worker-ի մանրամասներով անցել եմ ինքս, ոչ թե միայն կարդացել դրանց մասին։
  • Ինձ դուր է գալիս, որ PWA-ն սովորական կայքին ավելացնում է հավելվածի նման վարքագիծ՝ առանց app store-երի, առանձին կոդային բազաների կամ review-ի ուշացումների։
  • Լավ service worker-ը կրկնակի այցելությունները դարձնում է արագ և օգտատերերին տալիս բարեկամական offline էջ՝ բրաուզերի սխալի էկրանի փոխարեն։
  • Սա գործնական, ցածր գնով եղանակ է մոբայլում ընկալվող որակը բարելավելու համար, որտեղ ցանցերը դանդաղ են կամ անհուսալի։

Ինչպես է աշխատում

01Գաղափարը՝ կայք, որն իրեն պահում է հավելվածի պես

PWA-ն նոր տեխնոլոգիա չէ, այլ բրաուզերի հնարավորությունների հավաքածու, որոնք օգտագործվում են միասին։ HTTPS-ով, վավեր manifest ունեցող կայքը կարելի է տեղադրել home screen-ին կամ desktop-ին, գործարկել առանձին պատուհանում՝ առանց բրաուզերի ինտերֆեյսի, և այն երևում է app switcher-ում։ Service worker-ն ավելացնում է offline աջակցություն, քեշավորման շնորհիվ ավելի արագ բեռնում և ընտրովի push ծանուցումներ ու ֆոնային առաջադրանքներ։ Service worker-ը պատկերացրեք որպես խելացի դռնապան. էջի յուրաքանչյուր հարցում անցնում է նրա միջով, և նա որոշում է՝ բերել ցանցից, պատասխանել իր քեշից, թե երկուսն էլ։ «Progressive» նշանակում է, որ այն նրբորեն է նահանջում. բրաուզերները, որոնք չեն աջակցում որևէ հնարավորություն, միևնույն է ստանում են սովորական կայք։

  • Պահանջում է HTTPS (զարգացման համար localhost-ը թույլատրված է)։
  • Manifest. ինքնությունն ու տեսքը տեղադրված վիճակում։
  • Service worker. ցանցային proxy, քեշ և ֆոնային աշխատանք։

02Web app manifest-ը

Manifest-ը JSON ֆայլ է, որին հղում է անում էջը։ Հիմնական դաշտերն են. name և short_name (ցուցադրվում է պատկերակի տակ), start_url (որտեղ է բացվում հավելվածը), scope (որ URL-ներն են պատկանում հավելվածին), display (standalone՝ հավելվածի պատուհանի համար), theme_color և background_color (վերնագրի գոտի և splash screen) և icons։ Տրամադրեք առնվազն 192x192 և 512x512 PNG պատկերակներ։ Maskable պատկերակները, որոնք նշված են purpose «maskable»-ով, իրենց կարևոր բովանդակությունը պահում են կենտրոնական անվտանգ գոտում (լայնության 80%-ի շրջան), ուստի Android-ը կարող է դրանք կտրել շրջանի, squircle-ի կամ այլ ձևերի՝ առանց լոգոն կտրելու։ Ավելացրեք id, որպեսզի բրաուզերը կարողանա նույնականացնել հավելվածը, նույնիսկ եթե start_url-ը փոխվի, իսկ screenshots-ը և description-ը՝ ավելի հարուստ տեղադրման պատուհանի համար։

json
{
  "id": "/",
  "name": "Example Portfolio",
  "short_name": "Portfolio",
  "start_url": "/?source=pwa",
  "scope": "/",
  "display": "standalone",
  "theme_color": "#0f172a",
  "background_color": "#0f172a",
  "icons": [
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
    { "src": "/icons/maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
  ]
}

03Service worker-ի կյանքի ցիկլը

Service worker-ը JavaScript ֆայլ է, որն աշխատում է իր սեփական thread-ում, առանց DOM-ի հասանելիության, և կարող է ապրել էջից ավելի երկար։ Էջը գրանցում է այն. բրաուզերը ներբեռնում է այն և արձակում install, որտեղ սովորաբար pre-cache եք անում offline էջը և հիմնական ռեսուրսները։ Հետո այն սպասում է, մինչև ոչ մի բաց tab չօգտագործի հին worker-ը, և արձակում activate, որտեղ ջնջում եք հին քեշերը։ Այդ պահից այն ստանում է fetch իրադարձություններ իր scope-ի ներսում յուրաքանչյուր հարցման համար։ Երբ deploy եք անում փոխված worker ֆայլ, բրաուզերը հայտնաբերում է բայթային տարբերությունը, տեղադրում նոր տարբերակը և պահում այն սպասման մեջ, մինչև հին tab-երը փակվեն։ skipWaiting()-ը և clients.claim()-ը ստիպում են նոր տարբերակին անմիջապես ստանձնել կառավարումը, ինչը հարմար է, բայց կարող է հին էջերը խառնել նոր քեշավորված ռեսուրսների հետ, ուստի օգտագործեք դրանք գիտակցաբար։

js
// In the page (only in production builds)
if ("serviceWorker" in navigator) {
  window.addEventListener("load", () => {
    navigator.serviceWorker.register("/sw.js", { scope: "/" })
      .catch((err) => console.error("SW registration failed", err));
  });
}

04Քեշավորման ռազմավարություններ

fetch handler-ի ներսում յուրաքանչյուր տեսակի հարցման համար ընտրում եք ռազմավարություն։ Cache-first-ը մատուցում է քեշից և ցանց է գնում միայն բացակայության դեպքում. իդեալական է fingerprint արված ստատիկ ռեսուրսների (app.3f9a.js) և երբեք չփոխվող font-երի համար։ Network-first-ը փորձում է ցանցը և offline վիճակում վերադառնում քեշին. իդեալական է HTML էջերի և API տվյալների համար, որոնք պետք է թարմ լինեն։ Stale-while-revalidate-ը ակնթարթորեն պատասխանում է քեշից և ֆոնում թարմացնում քեշը. լավ է avatar-ների և ոչ կրիտիկական տվյալների համար։ Network-only-ն այն բաների համար է, որոնք երբեք չպետք է քեշավորվեն, ինչպես authenticated մուտացիաները և վճարումները։ Բովանդակային կայքերի համար տարածված, անվտանգ լռելյայն տարբերակը network-first-ն է navigation-ների համար՝ offline fallback էջով, և cache-first-ը՝ hash արված ստատիկ ֆայլերի համար։

  • Cache-first. hash արված JS/CSS, font-եր, պատկերակներ։
  • Network-first. HTML navigation-ներ, թարմ API ընթերցումներ։
  • Stale-while-revalidate. նկարներ, ոչ կրիտիկական JSON։
  • Network-only. POST հարցումներ, auth, checkout։

05Network-first service worker offline էջով

Այս ձեռքով գրված worker-ը install-ի ժամանակ pre-cache է անում offline էջը, activate-ի ժամանակ հեռացնում հին քեշերը և էջերի navigation-ները մշակում network-first. փորձում է ցանցը, պահում հաջողված էջերի պատճենը և ցանցի ձախողման դեպքում վերադառնում քեշավորված էջին կամ offline էջին։ Ոչ GET հարցումները և այլ origin-ները մնում են անձեռնմխելի։ Բարձրացրեք քեշի տարբերակը, երբ offline էջը կամ հիմնական ֆայլերը փոխվում են։

js
const CACHE = "site-v3";
const OFFLINE_URL = "/offline";
 
self.addEventListener("install", (e) => {
  e.waitUntil(caches.open(CACHE).then((c) => c.addAll([OFFLINE_URL, "/icons/icon-192.png"])));
});
self.addEventListener("activate", (e) => {
  e.waitUntil(caches.keys().then((keys) =>
    Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))));
});
self.addEventListener("fetch", (e) => {
  const req = e.request;
  if (req.method !== "GET" || new URL(req.url).origin !== location.origin) return;
  if (req.mode !== "navigate") return;
  e.respondWith(fetch(req).then((res) => {
    const copy = res.clone();
    if (res.ok) caches.open(CACHE).then((c) => c.put(req, copy));
    return res;
  }).catch(async () => (await caches.match(req)) || caches.match(OFFLINE_URL)));
});

06PWA-ներ Next.js-ում

Next.js App Router-ը manifest-ի համար ունի առաջնակարգ աջակցություն. app/manifest.ts ֆայլը, որը վերադարձնում է MetadataRoute.Manifest օբյեկտ, մատուցվում է որպես /manifest.webmanifest և հղվում ավտոմատ։ Theme color-ը գնում է viewport export-ի մեջ։ Service worker-ը ստատիկ ֆայլ է public/sw.js-ում, որը մատուցվում է կայքի արմատից, որպեսզի նրա scope-ը ընդգրկի ամբողջ հավելվածը, և գրանցվում է փոքր client կոմպոնենտից։ Ավելացրեք offline route (app/offline/page.tsx), որը կախված չէ տվյալներից, որպեսզի այն կարելի լինի pre-cache անել։ sw.js-ը մատուցեք Cache-Control: no-cache-ով, որպեսզի բրաուզերները արագ ստուգեն թարմացումները. Next.js-ի փաստաթղթերը սա ցույց են տալիս next.config-ի headers()-ի միջոցով։ Ավելի մեծ հավելվածների համար Serwist-ը (next-pwa-ի սպասարկվող հաջորդը, կառուցված Workbox-ի գաղափարների վրա) build-ից գեներացնում է precache manifest-ներ։

ts
// app/manifest.ts
import type { MetadataRoute } from "next";
 
export default function manifest(): MetadataRoute.Manifest {
  return {
    name: "Example Portfolio",
    short_name: "Portfolio",
    start_url: "/",
    display: "standalone",
    background_color: "#0f172a",
    theme_color: "#0f172a",
    icons: [
      { src: "/icons/icon-192.png", sizes: "192x192", type: "image/png" },
      { src: "/icons/icon-512.png", sizes: "512x512", type: "image/png" },
      { src: "/icons/maskable-512.png", sizes: "512x512", type: "image/png", purpose: "maskable" },
    ],
  };
}

07Տեղադրում, push և պլատֆորմների տարբերություններ

Chromium բրաուզերները (Chrome, Edge, Samsung Internet) ցույց են տալիս տեղադրման առաջարկ կամ մենյուի կետ, երբ կայքը HTTPS-ով է և ունի manifest name-ով, icons-ով, start_url-ով և համապատասխան display ռեժիմով. կարող եք բռնել beforeinstallprompt իրադարձությունը՝ ձեր սեփական տեղադրման կոճակը ցույց տալու համար։ Safari-ն iOS-ում և iPadOS-ում տեղադրում է Share, ապա Add to Home Screen ճանապարհով, և iOS 16.4-ից ի վեր տեղադրված վեբ հավելվածները կարող են ստանալ Web Push։ Safari-ն macOS-ում աջակցում է Add to Dock։ Push-ին պետք է service worker, օգտատիրոջ թույլտվություն՝ խնդրված օգտատիրոջ գործողությունից հետո, VAPID key-եր և սերվեր, որն ուղարկում է հաղորդագրությունները։ Background Sync-ը և Periodic Background Sync-ը միայն Chromium-ում են, ուստի դրանց վերաբերվեք որպես բարելավումների։ Firefox desktop-ը native կերպով PWA-ներ չի տեղադրում։ Թեստավորեք բոլոր թիրախային պլատֆորմներում։

Օգտագործեք, երբ

  • Բովանդակային կայքեր, պորտֆոլիոներ և բլոգներ, որոնք օգուտ են քաղում ակնթարթային կրկնակի այցելություններից և բարեկամական offline էջից։
  • Ներքին գործիքներ և դաշտային հավելվածներ, որոնք օգտագործվում են անհուսալի ցանցերում, որտեղ կարևոր է offline ընթերցումը կամ հերթագրված ուղարկումները։
  • Պրոդուկտներ, որոնք ուզում են տեղադրվող, հավելվածի նման փորձառություն՝ առանց native հավելվածների և store review-երի ծախսի։
  • E-commerce և մեդիա կայքեր, որտեղ մոբայլում ավելի արագ բեռնումը ուղղակիորեն ազդում է կոնվերսիաների վրա։

Մի՛ օգտագործեք, երբ

  • Հավելվածներ, որոնց պետք է սարքի խորը ինտեգրացիա (Bluetooth iOS-ում, ֆոնային տեղորոշում, տեսախցիկի ընդլայնված կառավարում), որը բրաուզերները չեն տրամադրում։
  • Երբ app store-երի միջոցով հայտնաբերելիությունը բիզնեսի հիմնական պահանջ է, և store-ի համար փաթեթավորված build-ը ընդունելի չէ։
  • Խիստ անհատականացված, միշտ թարմ տվյալներ, որտեղ ագրեսիվ քեշավորումը ցույց կտա հնացած կամ մեկ այլ օգտատիրոջ բովանդակություն։

Այլընտրանքներ

  • Native հավելվածներ (Swift, Kotlin)

    Սարքի լիարժեք հասանելիություն և ներկայություն store-երում. առանձին կոդային բազաներ և թողարկման ցիկլեր։

  • React Native / Expo

    Ընդհանուր JavaScript կոդային բազա iOS-ի և Android-ի համար՝ native UI-ով և store-երով տարածմամբ։

  • Capacitor

    Գոյություն ունեցող վեբ հավելվածը փաթեթավորում է native shell-ի մեջ app store-երի համար՝ սարքի API-ների plugin-ներով։

  • Պարզ HTTP քեշավորում + CDN

    Հաճախ բավական է միայն արագության համար. offline աջակցություն կամ տեղադրում չկա։

Լավագույն պրակտիկաներ

  1. Service worker-ը մատուցեք կայքի արմատից Cache-Control: no-cache-ով, որպեսզի թարմացումները արագ ընդունվեն։
  2. HTML-ի համար օգտագործեք network-first, իսկ cache-first-ը՝ միայն fingerprint արված ռեսուրսների համար, որպեսզի օգտատերերը երբեք չմնան հին էջերի վրա։
  3. Տարբերակավորեք քեշերը և activate-ում ջնջեք հները. երբեք թույլ մի տվեք քեշերին աճել անսահման։
  4. Տրամադրեք և՛ սովորական, և՛ maskable պատկերակներ և թեստավորեք դրանք maskable-icon preview գործիքով։
  5. Երբեք մի քեշավորեք authenticated կամ անհատականացված պատասխանները ընդհանուր cache key-ով. ոչ GET հարցումները ամբողջովին բաց թողեք։
  6. Service worker-ը գրանցեք միայն production build-երում, որպեսզի զարգացման ժամանակ խուսափեք շփոթեցնող քեշավորումից։
  7. Աուդիտ արեք Chrome DevTools-ով (Application վահանակ) և Lighthouse-ով, և offline ռեժիմը թեստավորեք իրական սարքերի վրա։

Տարածված սխալներ

  • HTML-ը cache-first քեշավորելը, ինչի պատճառով deploy-ից հետո օգտատերերը շարունակում են տեսնել կայքի հին տարբերակը։
  • sw.js-ը երկար քեշի header-ներով կամ ենթաթղթապանակից մատուցելը, ինչը հետաձգում է թարմացումները կամ սահմանափակում նրա scope-ը։
  • skipWaiting()-ը կուրորեն կանչելը, ինչի պատճառով հին էջը հանկարծ աշխատում է նոր քեշավորված ռեսուրսների հետ և կոտրվում։
  • Ոչ maskable լոգոն որպես maskable պատկերակ օգտագործելը, որը Android-ում վատ է կտրվում։
  • Push ծանուցումների թույլտվություն խնդրելը էջի առաջին բեռնման ժամանակ, ինչը օգտատերերը ռեֆլեքսորեն մերժում են։

Խորը վերլուծություն

08Scope, navigation preload և արտադրողականություն

Service worker-ը կառավարում է իր scope-ի ներսում գտնվող էջերը, որը լռելյայն այն թղթապանակն է, որտեղից այն մատուցվում է. ահա թե ինչու sw.js-ն ապրում է արմատում։ Էջը կառավարվում է միայն worker-ի ակտիվացումից հետո, ուստի ամենաառաջին այցելությունը երբեք չի ընդհատվում, եթե չկանչեք clients.claim()։ Network-first-ի թաքնված ծախսերից մեկը գործարկումն է. երբ worker-ը պարապ է, բրաուզերը պետք է այն boot անի՝ նախքան հարցումը կարող է դուրս գալ, ինչը ավելացնում է տասնյակ միլիվայրկյաններ։ Navigation preload-ը լուծում է սա. բրաուզերը navigation հարցումը սկսում է worker-ի boot-ին զուգահեռ, իսկ fetch handler-ն օգտագործում է event.preloadResponse։ fetch handler-ը պահեք արագ և խուսափեք դատարկ fetch listener-ից, որը overhead է ավելացնում առանց օգուտի։

js
self.addEventListener("activate", (e) => {
  e.waitUntil(self.registration.navigationPreload?.enable());
});
 
self.addEventListener("fetch", (e) => {
  if (e.request.mode !== "navigate") return;
  e.respondWith((async () => {
    try {
      return (await e.preloadResponse) || (await fetch(e.request));
    } catch {
      return (await caches.match(e.request)) || caches.match("/offline");
    }
  })());
});

09Թարմացման անվտանգ հոսքեր

Թարմացման ամենաանվտանգ pattern-ն է. նոր worker-ը տեղադրվում է և սպասում, էջը լսում է սպասող worker-ին և ցույց տալիս «հասանելի է նոր տարբերակ» banner, երբ օգտատերը համաձայնում է, էջն ուղարկում է հաղորդագրություն, worker-ը կանչում է skipWaiting(), և էջը վերաբեռնվում է controllerchange-ի ժամանակ։ Սա խուսափում է իրավիճակից, երբ աշխատող էջին հանկարծ սպասարկում է worker, որի քեշը պահում է ռեսուրսների տարբեր տարբերակներ։ Network-first HTML-ով և hash արված ռեսուրսներով բովանդակային կայքի համար ավտոմատ skipWaiting-ը սովորաբար նորմալ է, որովհետև հին ռեսուրսները session-ի ընթացքում չեն ջնջվում, եթե նախորդ քեշը որոշ ժամանակ պահեք։ Միշտ ներառեք kill switch. worker deploy անելը, որն ինքն իրեն ապագրանցում է և մաքրում քեշերը, արտակարգ վերականգնումն է, եթե վատ worker է թողարկվել։

js
// Emergency "kill switch" sw.js
self.addEventListener("install", () => self.skipWaiting());
self.addEventListener("activate", (e) => {
  e.waitUntil((async () => {
    for (const key of await caches.keys()) await caches.delete(key);
    await self.registration.unregister();
    for (const c of await self.clients.matchAll()) c.navigate(c.url);
  })());
});

10Պահոցի սահմաններ, eviction և offline տվյալներ

Cache API-ն, IndexedDB-ն և origin-ի մյուս պահոցները կիսում են բրաուզերի կառավարած քվոտա. Chromium-ը origin-ին թույլ է տալիս մինչև ազատ սկավառակի մեծ մասը, Safari-ն ավելի խիստ է և կարող է ջնջել այն կայքերի պահոցը, որոնք որոշ ժամանակ չեն օգտագործվել։ Լռելյայն պահոցը «best effort» է և կարող է մաքրվել ճնշման տակ. navigator.storage.persist()-ը խնդրում է մշտական պահոց, որը բրաուզերները տրամադրում են ըստ ներգրավվածության կամ տեղադրման։ Օգտագործումը ստուգեք navigator.storage.estimate()-ով։ Քեշավորված պատասխաններից դուրս offline տվյալների համար, ինչպես սևագրերը կամ հերթագրված ձևերի ուղարկումները, օգտագործեք IndexedDB (idb-ի նման փոքր wrapper-ով) և հերթագրված հարցումները նորից ուղարկեք կապի վերականգնման ժամանակ՝ Chromium-ում Background Sync-ով, իսկ այլուր՝ online իրադարձության fallback-ով։ Նախագծեք կոնֆլիկտների համար. հերթագրված գրառումները կարող են հնացած լինել, մինչև հասնեն սերվերին։

11Անվտանգության և գաղտնիության թակարդներ

Service worker-ները հզոր են, ուստի բրաուզերները պահանջում են HTTPS և նույն origin-ի script-եր։ Հիմնական ռիսկերը ինքնահասցված են։ Միայն URL-ով key արված պատասխանների քեշավորումը կարող է պահել մի օգտատիրոջ անհատականացված էջը և ցույց տալ այն հաջորդ օգտատիրոջը ընդհանուր սարքի վրա. երբեք մի քեշավորեք authenticated HTML կամ API պատասխաններ ընդհանուր քեշում և logout-ի ժամանակ մաքրեք քեշերը։ Cross-origin opaque պատասխանները (no-cors) ծանր են հաշվվում քվոտայի դեմ և չեն կարող ստուգվել սխալների համար, ուստի խուսափեք դրանք քեշավորելուց։ XSS բագը, որը կարող է worker գրանցել, կպահպանի հարձակվողի կոդը, ուստի օգնում են խիստ Content Security Policy-ն և worker-src դիրեկտիվը։ Վերջապես, կոտրված worker-ը կարող է օգտատերերին զրկել թարմացումներից, ահա թե ինչու են կարևոր sw.js-ի կարճ HTTP քեշավորումը և kill switch-ը։

Իրական իրավիճակներ

01

Վերադիզայնից հետո օգտատերերը դեռ տեսնում են հին կայքը

Խնդիր

Մեծ վերադիզայնից հետո որոշ օգտատերեր օրերով տեսնում են հին layout-ը։ Մեկ տարի առաջ ավելացվել էր service worker՝ cache-first-ով բոլոր հարցումների համար։

Ինչպես լուծել

Թողարկեք նոր sw.js, որը navigation-ները փոխում է network-first-ի, օգտագործում է նոր քեշի անուն, activate-ում ջնջում է բոլոր հին քեշերը և կանչում skipWaiting գումարած clients.claim, որպեսզի անմիջապես ստանձնի կառավարումը։ Համոզվեք, որ վեբ սերվերը sw.js-ի համար ուղարկում է Cache-Control: no-cache, այլապես բրաուզերները հին worker-ը ավելի երկար են պահում։ Եթե հին worker-ը լուրջ կոտրված է, նախ deploy արեք kill-switch worker, որը մաքրում է քեշերը և ապագրանցվում, հետո հաջորդ թողարկումում նորից ներդրեք ճիշտ worker։ Ստուգեք DevTools-ում Application, Service workers բաժնում՝ օգտագործելով update on reload։

02

Android-ի պատկերակը փոքրիկ է և տուփի մեջ

Խնդիր

PWA-ն Android-ում տեղադրելուց հետո home screen-ի պատկերակը երևում է որպես փոքր լոգո սպիտակ շրջանի ներսում՝ ի տարբերություն շուրջը գտնվող native հավելվածների։

Ինչպես լուծել

Manifest-ն ունի միայն սովորական պատկերակներ, ուստի Android-ը դրանք փոքրացնում է անվտանգ գոտու մեջ սպիտակ ֆոնի վրա։ Ստեղծեք առանձին maskable պատկերակ. ամբողջ մակերեսը լցնող 512x512 քառակուսի՝ բրենդի ֆոնի գույնով, և լոգոն կենտրոնական 80% շրջանի ներսում։ Ավելացրեք այն manifest-ում purpose «maskable»-ով՝ սովորական պատկերակները պահելով այլ օգտագործումների համար։ Ստուգեք այն maskable icon preview գործիքով և Chrome DevTools-ի manifest վահանակում, հետո նորից տեղադրեք հավելվածը սարքի վրա, քանի որ launcher-ները քեշավորում են պատկերակները։

03

Դաշտային աշխատակիցները վատ կապի դեպքում կորցնում են ձևի տվյալները

Խնդիր

Տեսուչները լրացնում են երկար ձևեր նկուղներում, որտեղ ազդանշան չկա։ Երբ offline են ուղարկում, հարցումը ձախողվում է, և տվյալները կորչում են։

Ինչպես լուծել

Ձևի սևագիրը պահեք IndexedDB-ում յուրաքանչյուր փոփոխության ժամանակ, որպեսզի ոչինչ չկորչի, նույնիսկ եթե tab-ը փակվի։ Ուղարկելիս փորձեք ցանցը. եթե ձախողվում է, ուղարկումն ավելացրեք outbox-ում IndexedDB-ում client-ի գեներացրած UUID-ով և ցույց տվեք որպես սպասող։ Outbox-ը նորից ուղարկեք կապի վերականգնման ժամանակ. Chromium-ում օգտագործեք Background Sync, իսկ այլուր՝ fallback online իրադարձության և հավելվածի գործարկման ժամանակ։ Սերվերը UUID-ին վերաբերվում է որպես idempotency key, ուստի երկու անգամ ուղարկված ձևը պահվում է մեկ անգամ։ Օգտատիրոջը ցույց տվեք յուրաքանչյուր սպասող տարրի հստակ կարգավիճակը։

Արագ ուղեցույց

Manifest-ի հղում (պարզ HTML)
<link rel="manifest" href="/manifest.webmanifest">
Next.js manifest ֆայլ
app/manifest.ts, որը export է անում MetadataRoute.Manifest
Service worker գրանցել
navigator.serviceWorker.register("/sw.js", { scope: "/" })
Maskable պատկերակի գրառում
{ "src": "/icons/maskable-512.png", "sizes": "512x512", "purpose": "maskable" }
Header sw.js-ի համար
Cache-Control: no-cache, no-store, must-revalidate
Նոր worker-ն ակտիվացնել հիմա
self.skipWaiting(); self.clients.claim();
Ստուգել պահոցի օգտագործումը
await navigator.storage.estimate()
Խնդրել մշտական պահոց
await navigator.storage.persist()
Debug Chrome-ում
DevTools > Application > Manifest / Service workers / Cache storage

Իմ իրական նախագծերում

  • Իմ սեփական պորտֆոլիո կայքը, կառուցված Next.js-ով, տեղադրվող PWA է՝ web app manifest-ով։
  • Այն ունի maskable պատկերակներ, որպեսզի հավելվածի պատկերակը ճիշտ տեսք ունենա Android-ի տարբեր պատկերակների ձևերում։
  • Նրա service worker-ն օգտագործում է network-first ռազմավարություն և կապի բացակայության դեպքում վերադառնում է առանձին offline էջին։

Հարցեր և պատասխաններ

Q1Ի՞նչն է կայքը դարձնում PWA։

Տեխնիկապես՝ երեք բան. այն մատուցվում է HTTPS-ով, ունի web app manifest, որը նկարագրում է նրա անունը, պատկերակները, մեկնարկային URL-ը և display ռեժիմը, և սովորաբար ունի service worker, որը մշակում է քեշավորումը և offline վարքագիծը։ Դրանցով բրաուզերները կարող են առաջարկել տեղադրում, բացել այն առանձին պատուհանում և պահել օգտագործելի վատ ցանցերում։ Checklist-ից դուրս լավ PWA-ն ադապտիվ է, արագ է կրկնակի այցելությունների ժամանակ և offline վիճակը մշակում է նրբորեն՝ հստակ հաղորդագրությամբ, բրաուզերի սխալի փոխարեն։ Այն մնում է սովորական կայք URL-ով, ուստի ինդեքսավորվում է, կիսվում է և deploy-ի ժամանակ ակնթարթորեն թարմացվում։

Q2Ինչպե՞ս է թարմացվում service worker-ը, և ինչո՞ւ են օգտատերերը երբեմն տեսնում հին բովանդակություն։

Navigation-ի ժամանակ բրաուզերը ստուգում է service worker script-ի բայթային փոփոխությունները՝ առնվազն 24 ժամը մեկ։ Եթե այն փոխվել է, նոր worker-ը տեղադրվում է, բայց սպասում է, մինչև հին worker-ի կառավարած բոլոր tab-երը փակվեն, հետո ակտիվանում։ Օգտատերերը հին բովանդակություն են տեսնում, երբ HTML-ը մատուցվում է cache-first, երբ sw.js-ն ինքը քեշավորված է HTTP header-ներով, կամ երբ սպասող worker-ը երբեք չի ակտիվանում, որովհետև tab-ը բաց է մնում։ Լուծումներն են network-first էջերի համար, no-cache header-ներ sw.js-ի վրա և կամ «հասանելի է նոր տարբերակ, վերաբեռնել» առաջարկ, որը գործարկում է skipWaiting, կամ skipWaiting-ի զգույշ օգտագործում clients.claim-ի հետ։

Q3Ո՞րն է տարբերությունը Cache API-ի և HTTP քեշավորման միջև։

HTTP քեշավորումը կառավարվում է պատասխանի header-ներով, ինչպես Cache-Control-ը և ETag-ը, և բրաուզերը ավտոմատ որոշում է, թե երբ նորից օգտագործել պատասխանը։ Cache API-ն ծրագրավորվող պահոց է, որը service worker-ները և էջերը կառավարում են կոդով. դուք եք որոշում, թե ինչ դնել այնտեղ, երբ կարդալ և երբ ջնջել՝ անկախ header-ներից։ Դրանք շերտավորվում են. service worker-ի fetch()-ը միևնույն է անցնում է HTTP քեշով։ Գործնականում HTTP քեշավորումը ապահովում է ստատիկ ռեսուրսների արագության մեծ մասը, իսկ Cache API-ն ավելացնում է offline աջակցություն և custom ռազմավարություններ, ինչպես network-first-ը offline fallback-ով։

Q4Ի՞նչ են maskable պատկերակները և ինչո՞ւ են կարևոր։

Android launcher-ները պատկերակները ցուցադրում են տարբեր ձևերով, ինչպես շրջաններ, squircle-ներ կամ կլորացված քառակուսիներ՝ կախված սարքից։ Սովորական պատկերակը փոքրացվում է սպիտակ ֆոնի վրա կամ անհարմար կտրվում։ Maskable պատկերակը նախագծված է այնպես, որ նրա կարևոր բովանդակությունը լինի կենտրոնական անվտանգ գոտում՝ պատկերակի 80% տրամագծով շրջանում, և ամբողջ քառակուսին լցնում է ֆոնի գույնով, ուստի launcher-ը կարող է այն մաքուր կտրել ցանկացած ձևի։ Այն հայտարարում եք manifest-ում purpose «maskable»-ով, սովորաբար սովորական «any» պատկերակի կողքին, որն օգտագործվում է այլ կոնտեքստներում։

Q5Կարո՞ղ են PWA-ները push ծանուցումներ ուղարկել iPhone-ում։

Այո, iOS-ի և iPadOS-ի 16.4-ից ի վեր, բայց միայն այն վեբ հավելվածների համար, որոնք օգտատերն ավելացրել է home screen-ին։ Հոսքն օգտագործում է ստանդարտ Push API-ն և Notifications API-ն service worker-ով, և թույլտվությունը պետք է խնդրվի ի պատասխան օգտատիրոջ գործողության, օրինակ՝ կոճակի սեղմման։ Սերվերում ձեզ պետք են VAPID key-եր և web-push-ի նման գրադարան՝ subscription endpoint-ներին հաղորդագրություններ ուղարկելու համար։ Android-ում և desktop Chromium-ում push-ն աշխատում է բրաուզերում՝ առանց տեղադրման։ Հարգեք օգտատերերին. խնդրեք միայն այն ժամանակ, երբ արժեքը պարզ է, և թույլ տվեք նրանց կառավարել նախընտրությունները։

Q6Արդյո՞ք յուրաքանչյուր կայք պետք է PWA լինի։

Ոչ ամեն կայքի է պետք տեղադրում, բայց գրեթե ամեն կայք օգուտ է քաղում դրա մի փոքր մասից։ Լավ պատկերակներով manifest-ը քիչ արժե և բարելավում է կայքի տեսքը, երբ այն կիսվում կամ ամրացվում է։ Պարզ service worker-ը offline էջով և ստատիկ ռեսուրսների քեշավորմամբ օգնում է վատ կապ ունեցող օգտատերերին։ Լիարժեք offline-first վարքագիծը, background sync-ը և push-ը արժե միայն այն դեպքում, երբ օգտատերերը հաճախ են վերադառնում կամ աշխատում են անհուսալի պայմաններում։ Ռիսկը քեշավորման սխալներն են, որոնք մատուցում են հնացած կամ մասնավոր բովանդակություն, ուստի մինիմալ, լավ թեստավորված worker-ը ավելի լավ է, քան հավակնոտ, բագերով լի մեկը։

Սենիոր մակարդակի հարցեր

S1Ինչպե՞ս կնախագծեիք offline-first ճարտարապետություն տվյալներ մուտքագրելու PWA-ի համար։

Լոկալ բազան կդիտարկեի որպես UI-ի հիմնական աղբյուր. IndexedDB-ն պահում է գրառումները և սպասող մուտացիաների outbox, յուրաքանչյուրը՝ client-ի գեներացրած UUID-ով և timestamp-ով։ UI-ը կարդում և գրում է լոկալ և ցույց տալիս համաժամեցման կարգավիճակը։ Sync engine-ը online վիճակում ուղարկում է outbox-ը՝ UUID-ները օգտագործելով որպես idempotency key-եր, և cursor-ից սկսած քաշում է սերվերի փոփոխությունները։ Կոնֆլիկտներին պետք է բացահայտ policy յուրաքանչյուր entity-ի համար. last-write-wins՝ պարզ դաշտերի համար, դաշտի մակարդակով merge կամ ձեռքով լուծման համար նշում այնտեղ, որտեղ տվյալները կարևոր են։ Service worker-ը precache է անում app shell-ը և մշակում navigation-ները. տվյալների շերտը ապրում է հավելվածում, ոչ թե worker-ում, որպեսզի թեստավորելի լինի։ Մշտական պահոց է խնդրվում, և սերվերը վալիդացնում է ամեն ինչ, որովհետև client-ները կարող են հնացած կամ կեղծված լինել։

S2Ե՞րբ կօգտագործեիք Workbox կամ Serwist ձեռքով գրված service worker-ի փոխարեն։

Ձեռքով գրված worker-ը նորմալ է փոքր, պարզ policy-ի համար. precache անել offline էջը, network-first navigation-ների համար, cache-first hash արված ռեսուրսների համար։ Դա մի քանի տասնյակ տող է և հեշտ է աուդիտ անել։ Երբ ձեզ պետք է build-ից գեներացված precache manifest revision hash-երով, բազմաթիվ runtime route-եր ժամկետի և չափի սահմաններով, broadcast թարմացումներ, background sync հերթեր և navigation preload, գրադարանն արդարանում է, որովհետև այս մանրամասներում հեշտ է նուրբ սխալներ թույլ տալ։ Workbox-ը դրանք տրամադրում է որպես մոդուլներ. Serwist-ը սպասարկվող fork է՝ Next.js-ի հետ լավ ինտեգրացիայով։ Փոխզիջումը ևս մեկ կախվածություն և աբստրակցիա է, որը պետք է հասկանալ debug անելիս։ Սկսում եմ պարզից և անցնում, երբ routing աղյուսակը աճում է մի քանի կանոնից ավելի։

S3Ինչպե՞ս են PWA-ներն ազդում SEO-ի և Core Web Vitals-ի վրա։

PWA-ն միևնույն է կայք է, ուստի ինդեքսավորումը կախված է սերվերի վերադարձրած HTML-ից, ոչ թե service worker-ից. crawler-ները սովորաբար չեն օգտագործում ձեր worker-ը։ Սերվերում ռենդեր արված կամ ստատիկ էջերը մնում են կենսական SEO-ի համար։ Դաշտային Core Web Vitals-ի համար service worker-ն օգնում է կրկնակի այցելություններին. քեշավորված ռեսուրսները բարելավում են LCP-ն, իսկ app shell-ը կարող է navigation-ները դարձնել գրեթե ակնթարթային։ Բայց վատ գրված worker-ը կարող է վնասել. worker-ի boot-ը latency է ավելացնում յուրաքանչյուր navigation-ի, եթե navigation preload-ը միացված չէ, իսկ network-first-ը դանդաղ timeout-ներով կարող է հետաձգել էջերը անկայուն ցանցերում։ Ես չափում եմ իրական օգտատերերի տվյալները՝ բաժանված առաջին և կրկնակի այցելությունների, միացնում եմ navigation preload և worker-ի fetch տրամաբանությունը պահում մինիմալ։

Բառարան

PWA
Progressive Web App. կայք manifest-ով և service worker-ով, որը կարելի է տեղադրել և որն աշխատում է offline։
Web app manifest
JSON ֆայլ, որը տեղադրման համար նկարագրում է հավելվածի անունը, պատկերակները, գույները, մեկնարկային URL-ը և display ռեժիմը։
Service worker
Ֆոնային script, որը ընդհատում է էջի ցանցային հարցումները և կարող է քեշավորել, փոփոխել կամ պատասխանել դրանց։
Scope
URL ուղին, որը կառավարում է service worker-ը. լռելյայն այն թղթապանակը, որտեղից մատուցվում է worker ֆայլը։
Maskable icon
Պատկերակ, որի բովանդակությունը կենտրոնական անվտանգ գոտում է, որպեսզի պլատֆորմները կարողանան այն կտրել ցանկացած ձևի։
Cache API
Բրաուզերի պահոցի API՝ կոդից request-response զույգեր պահելու և ստանալու համար։
Network-first
Քեշավորման ռազմավարություն, որը փորձում է ցանցը և offline վիճակում վերադառնում քեշավորված բովանդակությանը։
Standalone display
Manifest-ի ռեժիմ, որում տեղադրված հավելվածը բացվում է իր սեփական պատուհանում՝ առանց բրաուզերի հասցեի տողի։
VAPID
Key-եր, որոնք Web Push հաղորդագրություններ ուղարկելիս ձեր սերվերը նույնականացնում են բրաուզերի push ծառայությունների առջև։

Պրոդուկտի գաղափար ունե՞ք։Եկեք ստեղծենք այն։

Պատմեք ձեր գաղափարի, պրոդուկտի կամ թիմի մասին։ Սովորաբար պատասխանում եմ մեկ աշխատանքային օրվա ընթացքում։