PWA — React da Progressive Web App

~20 daq · React · Ekotizim va Amaliyot

Muammodan boshlaylik

Siz vazifalar ro'yxati ilovasini yasadingiz. Foydalanuvchilar so'raydi:

  • "Telefonda ilova sifatida o'rnatsa bo'ladimi? Har safar brauzer ochib, manzil yozish noqulay"
  • "Metroda internet yo'q — ilova ochilmayapti"
  • "Har safar ochganda yuklanishi sekin"

Mobil ilova (React Native) yasash — alohida loyiha, App Store va Google Play ga joylash, ko'rib chiqish jarayonlari. Ko'p hollarda bu ortiqcha.

PWA (Progressive Web App) — oddiy veb-saytga ilova imkoniyatlarini qo'shish: o'rnatish, offlayn ishlash, tez yuklanish. Bir xil React kod — brauzerda ham, "ilova" sifatida ham.


PWA nimadan iborat

Uch qism:

Qism Vazifasi
HTTPS Majburiy (faqat localhost istisno)
Web App Manifest Ilova haqida: nomi, ikonkalar, ranglar, qanday ochilishi
Service Worker Fonda ishlaydigan skript: keshlash, offlayn, yangilanishlar

Shu uchtasi bo'lsa — brauzer "Ilovani o'rnatish" ni taklif qiladi, va o'rnatilgan ilova o'z ikonkasi bilan, brauzer paneli siz, alohida oynada ochiladi.


vite-plugin-pwa

Service Worker ni qo'lda yozish murakkab. Vite uchun tayyor plagin:

npm install -D vite-plugin-pwa
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { VitePWA } from 'vite-plugin-pwa';

export default defineConfig({
  plugins: [
    react(),
    VitePWA({
      registerType: 'prompt',
      includeAssets: ['favicon.svg', 'apple-touch-icon.png'],
      manifest: {
        name: 'Vazifalar — kundalik rejalashtiruvchi',
        short_name: 'Vazifalar',
        description: "Kunlik vazifalarni rejalashtirish va kuzatish",
        lang: 'uz',
        start_url: '/',
        display: 'standalone',
        background_color: '#ffffff',
        theme_color: '#10b981',
        icons: [
          { src: '/icons/192.png', sizes: '192x192', type: 'image/png' },
          { src: '/icons/512.png', sizes: '512x512', type: 'image/png' },
          { src: '/icons/512-maskable.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
        ],
      },
    }),
  ],
});

npm run build dan keyin plagin avtomatik yaratadi:

  • manifest.webmanifest va uni index.html ga ulaydi
  • sw.js — barcha build fayllarini (JS, CSS, HTML, ikonkalar) oldindan keshlovchi Service Worker
  • Service Worker ni ro'yxatdan o'tkazish kodi

Manifest maydonlari

Maydon Ma'nosi
name / short_name To'liq nom (o'rnatish oynasida) / qisqa nom (ikonka ostida)
start_url Ilova ochilganda qaysi sahifa
display standalone — brauzer paneli siz, ilova kabi; browser — oddiy tab
theme_color Holat paneli va sarlavha rangi
background_color Yuklanish ekrani (splash) foni
icons Kamida 192×192 va 512×512; maskable — Android ning dumaloq/kvadrat shakllariga moslashadigan ikonka

[!TIP] Ikonkalarni bitta katta rasmdan yaratish uchun @vite-pwa/assets-generator vositasi bor. Maskable ikonka uchun muhim kontent markazdagi xavfsiz zonada (taxminan 80%) bo'lishi kerak — aks holda Android uni kesib qo'yadi.


Service Worker qanday ishlaydi

       Brauzer                Service Worker              Tarmoq
          │                        │                        │
          │── GET /app.js ───────▶│                        │
          │                        │── keshda bormi? ──┐    │
          │                        │◀── ha ─────────────┘    │
          │◀── keshdan (0 ms) ─────│                        │
          │                        │                        │
          │── GET /api/vazifalar ─▶│──────────────────────▶│
          │                        │◀───────────────────────│
          │◀───────────────────────│   (+ keshga nusxa)     │

Service Worker — brauzer va tarmoq o'rtasidagi proksi. U har bir so'rovni ushlaydi va qaror qiladi: keshdan berish, tarmoqqa yuborish yoki ikkalasi.

Natija:

  • Ikkinchi ochilishdan boshlab ilova bir zumda yuklanadi — fayllar keshdan
  • Internet bo'lmasa ham ochiladi — interfeys keshda

API so'rovlarini keshlash

Standart holatda faqat ilova fayllari keshlanadi. API ma'lumotlari uchun — runtimeCaching:

VitePWA({
  workbox: {
    runtimeCaching: [
      {
        urlPattern: ({ url }) => url.pathname.startsWith('/api/'),
        handler: 'NetworkFirst',
        options: {
          cacheName: 'api',
          networkTimeoutSeconds: 3,
          expiration: { maxEntries: 100, maxAgeSeconds: 60 * 60 * 24 },
        },
      },
      {
        urlPattern: ({ request }) => request.destination === 'image',
        handler: 'CacheFirst',
        options: {
          cacheName: 'rasmlar',
          expiration: { maxEntries: 200, maxAgeSeconds: 60 * 60 * 24 * 30 },
        },
      },
    ],
  },
});
Strategiya Qanday ishlaydi Qachon
CacheFirst Avval kesh; yo'q bo'lsa — tarmoq Rasmlar, shriftlar — o'zgarmaydigan resurslar
NetworkFirst Avval tarmoq; ishlamasa — kesh API ma'lumotlari — yangi bo'lishi muhim, lekin offlayn ham kerak
StaleWhileRevalidate Darhol keshdan + fonda yangilash Tez-tez o'zgarmaydigan ma'lumotlar: kategoriyalar, profil
NetworkOnly Faqat tarmoq To'lov, autentifikatsiya

[!WARNING] Shaxsiy ma'lumotlarni (buyurtmalar, xabarlar) Service Worker keshiga qo'yishda ehtiyot bo'ling: foydalanuvchi tizimdan chiqqandan keyin ham ular keshda qoladi va umumiy kompyuterda boshqa odam ko'rishi mumkin. Chiqishda keshni tozalang (caches.delete('api')) yoki shaxsiy API larni keshlamang.


Yangilanishlar

PWA ning eng nozik qismi. Siz yangi versiya deploy qildingiz — lekin foydalanuvchi eski versiyani ko'radi, chunki u keshdan yuklanmoqda.

registerType: 'prompt' bilan — foydalanuvchiga xabar ko'rsatish:

// src/components/YangilanishXabari.jsx
import { useRegisterSW } from 'virtual:pwa-register/react';

function YangilanishXabari() {
  const {
    needRefresh: [yangilanishBor, setYangilanishBor],
    updateServiceWorker,
  } = useRegisterSW();

  if (!yangilanishBor) return null;

  return (
    <div role="status" className="yangilanish-banner">
      <p>Yangi versiya mavjud.</p>
      <button onClick={() => updateServiceWorker(true)}>Yangilash</button>
      <button onClick={() => setYangilanishBor(false)}>Keyinroq</button>
    </div>
  );
}
  • prompt — foydalanuvchi o'zi tanlaydi; forma to'ldirayotgan odamning ishi yo'qolmaydi
  • autoUpdate — avtomatik yangilanadi; oddiyroq, lekin sahifa kutilmaganda qayta yuklanishi mumkin

[!INFO] Server da sw.js va index.html fayllari keshlanmasligi kerak (Cache-Control: no-cache) — aks holda brauzer yangi Service Worker borligini bilmaydi. Hash li JS/CSS fayllar esa uzoq muddat keshlanadi (72-dars).


Offlayn holatni ko'rsatish

import { useSyncExternalStore } from 'react';

function onlaynObuna(callback) {
  window.addEventListener('online', callback);
  window.addEventListener('offline', callback);
  return () => {
    window.removeEventListener('online', callback);
    window.removeEventListener('offline', callback);
  };
}

function useOnlayn() {
  return useSyncExternalStore(onlaynObuna, () => navigator.onLine, () => true);
}

function OfflaynBanner() {
  const onlayn = useOnlayn();
  if (onlayn) return null;
  return <div role="status">Internet aloqasi yo'q. Ma'lumotlar oxirgi saqlangan holatda.</div>;
}

TanStack Query offlayn holatni o'zi kuzatadi: aloqa uzilganda so'rovlarni to'xtatadi va tiklanganda qayta yuboradi (54-dars, refetchOnReconnect).


Development da tekshirish

Service Worker standart holatda faqat production build da ishlaydi:

npm run build
npm run preview

Chrome DevTools → Application tabi:

  • Manifest — xatolar va ogohlantirishlar, ikonkalar
  • Service Workers — holati, "Update on reload", "Offline" belgisi
  • Cache Storage — nimalar keshlangan

Lighthouse → PWA tekshiruvi ham foydali.


Cheklovlar

  • iOS (Safari) — PWA ni qo'llab-quvvatlaydi, lekin cheklovlar bilan: o'rnatish faqat "Ulashish → Uy ekraniga qo'shish" orqali (avtomatik taklif yo'q), push bildirishnomalar faqat o'rnatilgan ilovada va iOS ning yangi versiyalarida
  • Ba'zi qurilma imkoniyatlari (Bluetooth, NFC, fon rejimida uzoq ishlash) — cheklangan yoki faqat ba'zi brauzerlarda
  • Do'konlarda (App Store) ko'rinmaydi — lekin Google Play ga TWA (Trusted Web Activity) orqali joylash mumkin

Nima uchun shunday?

Nima uchun Service Worker sahifadan alohida, fonda ishlaydi — keshlashni oddiy JavaScript bilan qilsa bo'lmaydimi?

Chunki sahifaning JavaScript i sahifa yuklangandan keyin ishlay boshlaydi — va sahifani yuklash uchun internet kerak. Offlayn holatda sahifa umuman ochilmaydi, demak uning kodi ham ishga tushmaydi. Service Worker esa brauzerda sahifadan mustaqil ro'yxatdan o'tgan va sahifa so'rovidan oldin ishga tushadigan skript. U "sahifani qayerdan olish kerak?" degan savolga javob beradi — tarmoq ishlamasa ham. Bu mobil ilovalar modelini veb ga olib keladi: ilova kodi qurilmada saqlanadi, tarmoq esa faqat ma'lumot uchun kerak.


Xulosa

Eslab qoling:

  • PWA = HTTPS + Web App Manifest + Service Worker → o'rnatish, offlayn, tez yuklanish
  • vite-plugin-pwa — manifest va Service Worker ni avtomatik yaratadi
  • Manifest: name, short_name, start_url, display: 'standalone', theme_color, ikonkalar 192/512 + maskable
  • Service Worker — brauzer va tarmoq o'rtasidagi proksi; ilova fayllari oldindan keshlanadi
  • API keshlash — runtimeCaching: CacheFirst, NetworkFirst, StaleWhileRevalidate; shaxsiy ma'lumotlarga ehtiyot
  • Yangilanishlar — registerType: 'prompt' + useRegisterSW; sw.js va index.html keshlanmasin
  • Tekshirish — production build, DevTools → Application; iOS cheklovlarini hisobga oling

Keyingi dars

88-dars: React bilan real loyiha arxitekturasi — papkalar tuzilishi