کاربر فروشگاه PWA را باز میکند، قیمت قدیمی از Cache میبیند، سفارش را در اینترنت ناپایدار ثبت میکند و پیام «موفق» میگیرد؛ اما درخواست هرگز به سرور نرسیده است. چند دقیقه بعد Service Worker تازه وسط Checkout فعال میشود و صفحه با Bundle ناسازگار میشکند. این تجربه سریع و App-like نیست؛ یک قرارداد State ناقص است.
UX موفق PWA یعنی کاربر در هر لحظه بداند چه دادهای تازه است، چه کاری فقط محلی انجام شده، چه چیزی در صف است، چه زمانی نیاز به اتصال/ورود/بهروزرسانی دارد و چگونه بازیابی کند. این راهنما Journey و State contract، نصب، Offline، Sync/Conflict، Cache، Update، Push، Performance، Accessibility، RTL، Payment، Security، Test و Measurement را برای تجربهای Progressive و قابلاعتماد طراحی میکند.
PWA چیست؛ از منظر تجربه کاربر
PWA یک Binary یا UI kit واحد نیست؛ Web appای است که قابلیتهای Web را بهصورت Progressive به تجربه معمول وب اضافه میکند—مثل نصب، Service Worker، Offline behavior و Push—هرجا Browser/OS/Policy اجازه دهد. URL، Link، Back/Forward، Refresh، Selection، Zoom و دسترسپذیری وب همچنان داراییاند؛ تقلید سطحی از Native نباید آنها را بشکند.
راهنمای MDN درباره Installability Manifest را مبنای تجربه نصب میداند و توضیح میدهد Service Worker الزام ذاتی نصب در همه شرایط نیست، هرچند بسیاری از PWAها از آن برای Offline استفاده میکنند. Criteria و Promotion بین Browserها متفاوتاند. پس «PWA نصبپذیر است» و «PWA آفلاین کار میکند» دو Acceptance جدا هستند.
مرز Intent با معماری و سئوی PWA
این مقاله مالک تجربه کاربر در Capability/Network/Data/Sync/Update/Permission state است. برای Product fit، انتخاب Web/PWA/Native، Manifest/Service Worker/Cache/Push architecture و Operations به راهنمای تصمیم و اجرای PWA مراجعه کنید. برای Crawl/Render/Index/Canonical و JavaScript SEO، مقاله سئوی فنی PWA مالک تخصصی است.
از Job و Failure شروع کنید، نه قابلیت
برای هر Journey بنویسید:
- کاربر و Job: مشاهده، ثبت، پرداخت، ویرایش یا پیگیری چیست؟
- Criticality: از دسترفتن/تکرار کار چه زیانی دارد؟
- Data: عمومی، شخصی، حساس، مالی و عمر Freshness؟
- Network: Fast/slow/flaky/captive/offline و هزینه دیتا؟
- Device/Browser/mode: tab یا standalone، حافظه/فضا/باتری؟
- Failure: stale، timeout، duplicate، conflict، auth expiry، update mismatch؟
- Recovery: retry، edit، discard، reconcile، support یا online-only؟
- Outcome/Guardrail: task success/time، loss/duplicate/complaint؟
«همهچیز Offline-first» نسخه نیست. Catalog read، Draft form و Payment سه Risk profile متفاوت دارند. Architecture را براساس Journey tier کنید.
State inventory ستون فقرات UX است
| محور | Stateهای نمونه | سؤال UI |
|---|---|---|
| Capability | supported/unsupported/permission-blocked | Fallback چیست؟ |
| Mode | browser/installed/embedded | Navigation و instruction فرق دارد؟ |
| Network | online/degraded/flaky/offline/unknown | کدام Action امن است؟ |
| Data | fresh/stale/partial/missing/sensitive | آخرین Sync و Source چیست؟ |
| Mutation | draft/queued/sending/confirmed/failed/conflict | آیا موفقیت Server تأیید شده؟ |
| Auth | signed-in/expired/revoked/step-up-required | کاربر کارش را از دست میدهد؟ |
| Version | current/update-ready/incompatible | چه زمانی Refresh امن است؟ |
برای Ownership، lifetime، persisted/server/offline state و conflict، از راهنمای مدیریت State استفاده کنید. UI state را از network/cache implementation استنتاج پنهان نکنید.
Progressive Enhancement یعنی Baseline کامل
کاربر بدون نصب، Push، Background Sync یا حتی Service Worker باید Core journey متناسب با Scope را در Browser انجام دهد. سپس Capability را با Feature detection اضافه کنید. User agent string و فهرست ثابت Browser کافی نیست؛ Policy، mode و permission نیز اثر دارند.
const capability = {
serviceWorker: "serviceWorker" in navigator,
push: "PushManager" in window,
notifications: "Notification" in window,
backgroundSync: "serviceWorker" in navigator && "SyncManager" in window,
installPrompt: false // فقط پس از دریافت event واقعی
};Unsupported را Error ننامید. دکمه بیکار نشان ندهید؛ قابلیت را Hide یا با مسیر جایگزین توضیح دهید. Progressive Enhancement باید در Test matrix اثبات شود.
App-like به معنی شکستن قرارداد وب نیست
Link باید URL واقعی و قابل Open-in-new-tab/share داشته باشد. Back باید مسیر ذهنی را برگرداند، نه کل State را پاک کند. Refresh/deep link روی هر Route حیاتی کار کند. Scroll restoration، focus، selection، text zoom و password manager را حفظ کنید. Bottom navigation فقط وقتی مناسب است که Information architecture و safe area/keyboard را رعایت کند.
Gesture مخفی را تنها راه Action نکنید. Swipe/drag باید alternative دکمه/منو داشته باشد. Browser chrome و installed display mode فضاهای متفاوتاند؛ viewport/safe-area و virtual keyboard را روی دستگاه واقعی تست کنید.
Manifest یک قرارداد هویت و Launch است
id، name/short_name، start_url، scope، display، theme/background color و Iconهای maskable را با Product identity و Routing هماهنگ کنید. Start URL را برای Analytics به صفحهای بیربط یا Session شکن تبدیل نکنید. Icon باید در cropهای مختلف قابلشناسایی و نام در فارسی/لاتین قابلفهم باشد.
Orientation lock و fullscreen را فقط با نیاز واقعی Job استفاده کنید. تغییر display mode روی Browserها یکسان نیست؛ Launch/Back/Auth callback/deep link و multi-account را در ماتریس واقعی بررسی کنید.
نصب باید بعد از مشاهده ارزش پیشنهاد شود
Prompt در اولین Page view Context و Trust ندارد. پس از Journey موفق، بازگشت چندباره، ساخت Draft یا درخواست قابلیت نصبشده، ارزش را توضیح دهید: دسترسی سریع، Draft offline یا اعلان وضعیت سفارش—فقط اگر واقعاً ارائه میشود.
راهنمای Installation prompt در web.dev میگوید beforeinstallprompt روی همه Browserها پشتیبانی نمیشود و از Specification اصلی Manifest به Incubator رفته است. Event را Capture کنید، CTA را فقط هنگام وجود آن و با تعامل کاربر فراخوانی کنید؛ نتیجه accepted/dismissed را ثبت و مزاحمت را Cooldown کنید.
let deferredInstall;
addEventListener("beforeinstallprompt", (event) => {
event.preventDefault();
deferredInstall = event;
showInstallCTA({ reason: "user_value_seen" });
});
async function installFromUserAction() {
if (!deferredInstall) return showPlatformSpecificHelp();
await deferredInstall.prompt();
const choice = await deferredInstall.userChoice;
recordInstallChoice(choice.outcome);
deferredInstall = undefined;
}راهنمای نصب را Platform-aware و قابلحذف کنید
روی iOS/iPadOS مسیر Share→Add to Home Screen با Dialog برنامهنویسیشده Chromium یکسان نیست. متن Menu ممکن است با زبان/نسخه تغییر کند؛ Screenshot تاریخدار و instruction کوتاه بدهید، اما فقط وقتی Device/Mode مرتبط است. کاربر نصبشده را دوباره دعوت نکنید و نصب را شرط Checkout/Content نگذارید.
display-mode و launch context میتوانند Signal باشند، نه حقیقت مطلق همه محیطها. Analytics install funnel را با eligible→shown→clicked→accepted/assisted→first standalone launch→retained use بسنجید؛ صرف Install Outcome ارزشی نمیسازد.
Offline یک طیف محصول است
| سطح | رفتار | مناسب |
|---|---|---|
| Fallback | صفحه راهنما/Retry، بدون Error browser | حداقل همه PWAها |
| Read cached | نمایش داده قبلی با زمان/وضعیت stale | محتوا و Catalog مشروط |
| Local draft | حفظ فرم/کار تا اتصال | متن، گزارش، سبد مشروط |
| Queued mutation | ارسال بعدی با ID/Retry/Conflict | Action idempotent و قابلبازیابی |
| Full offline workflow | چند Step و Sync/Reconcile | فقط با ارزش و بودجه عملیات بالا |
| Online-only | توضیح صریح و حفظ context | Payment/حساسیت یا Freshness بحرانی |
Offline-first یک Label نیست؛ Data contract، Storage budget، conflict policy، security و test cost دارد. سطح را برای هر Journey/Entity انتخاب کنید.
Online بودن Browser موفقیت Request را تضمین نمیکند
navigator.onLine فقط Hint است؛ Captive portal، DNS، ISP، timeout یا outage API میتواند با «Online» هم Request را شکست دهد. Banner جهانی را از eventهای online/offline بهعنوان Signal بسازید، اما نتیجه هر Action را از Response/timeout/reconciliation تعیین کنید.
Stateهای «اتصال ضعیف»، «داده قدیمی»، «عملیات در صف» و «سرور پاسخ نمیدهد» را یکی نکنید. پیام باید علت قابلدانستن، اثر و Action بعدی را بگوید؛ ادعای علت قطعی بدون Evidence نکنید.
Freshness را در UI آشکار کنید
Stale catalog میتواند قابلاستفاده باشد؛ Stale قیمت/موجودی در Checkout خطرناک است. برای هر Entity، source of truth، TTL، stale-while-revalidate policy، last successful sync و must-revalidate point تعریف کنید. «آخرین بهروزرسانی ۱۰:۳۲» از «آفلاین هستید» مفیدتر است.
داده cached را با badge/label قابلفهم نشان دهید و Refresh دستی بدهید. قبل از Commit حساس، قیمت/موجودی/permission را Server validate و تفاوت را با Diff و انتخاب کاربر حل کنید.
Cache strategy بخشی از UX و Correctness است
- App shell fingerprinted: cache-first/versioned؛
- Navigation: network-first با offline fallback متناسب؛
- تصویر عمومی: stale-while-revalidate با quota/eviction؛
- API عمومی: policy براساس Freshness/identity؛
- داده شخصی/مالی: cache opt-in حداقلی، tenant/user key و logout purge؛
- Mutation: هرگز پاسخ synthetic موفق بدون server confirmation.
Cache Storage، HTTP cache، query cache و persisted state قراردادهای متفاوت دارند. برای Identity/Freshness/Invalidation/Failure به راهنمای معماری Cache مراجعه کنید. نام Cache نسخهدار/Origin-safe و quota failure/eviction را تست کنید.
Draft محلی و صف ارسال را جدا کنید
Draft یعنی کار هنوز Commit نشده و کاربر میتواند ویرایش/حذف کند. Queued mutation یعنی Intent ارسال ثبت شده اما Server تأیید نکرده است. Label و Controls متفاوت لازماند. هر Mutation یک client operation ID، payload schema version، created time، actor/tenant و retry state میخواهد.
type PendingOperation = {
operationId: string;
kind: "SAVE_REPORT";
schemaVersion: 3;
entityId: string;
baseVersion: number;
payload: EncryptedDraft;
state: "queued" | "sending" | "failed" | "conflict";
attempts: number;
nextRetryAt?: string;
};Background Sync را Progressive enhancement بدانید؛ در نبود capability، Foreground retry و دکمه ارسال دستی لازم است. Retry باید bounded/backoff باشد و خطای Auth/Validation/Conflict را کور تکرار نکند.
Idempotency و Reconciliation مانع موفقیت دروغیناند
Operation ID باید در Server dedupe شود. Timeout وضعیت Unknown است، نه Failed قطعی؛ Inquiry یا Reconciliation تعیین میکند ثبت شده یا نه. UI سه State «در انتظار تأیید»، «تأیید شد» و «نیاز به اقدام» را نشان دهد. Duplicate tap را Disable/feedback کنید، اما شبکه تکراری را فقط با UI حلشده فرض نکنید.
برای Payment/Order، Queue عمومی خطرناک است. ایجاد Intent، callback/inquiry، currency/amount، expiry و ownership باید Contract اختصاصی داشته باشد. Offline Payment را وعده ندهید مگر Product/PSP واقعاً آن را پشتیبانی و reconcile کند.
Conflict را به Last-write-wins پنهان نسپارید
برای Note شخصی شاید merge یا latest مناسب باشد؛ برای موجودی، رزرو و گزارش چندکاربره نیست. Base version/ETag یا Domain rule بگیرید. UX Conflict باید نسخه محلی/سرور، تفاوت و انتخاب Merge/Keep server/Save copy را متناسب با Task ارائه کند. اطلاعات کاربر را بیهشدار دور نریزید.
Optimistic UI باید Reversible باشد
Like کمریسک را میتوان فوراً نمایش و در Failure برگشت داد؛ Refund یا Place order نیاز به Pending state روشن دارد. Optimism را با Risk tier انتخاب کنید. Toast «انجام شد» پیش از confirmation برای Action مالی ممنوع؛ از «در حال ثبت» و سپس outcome قطعی استفاده کنید.
Feedback duration، placement، focus و screen-reader announcement را در Design system ثبت کنید. راهنمای میکرواینتراکشن از State تا Feedback Trigger/Rule/State/Recovery را پوشش میدهد.
Update تجربه محصول است، نه فقط Deploy
Service Worker جدید معمولاً پس از نصب در waiting میماند تا Clientهای نسخه قبل کنار بروند. راهنمای Update در web.dev هشدار میدهد skipWaiting() میتواند Worker جدید را روی صفحهای با Asset/State نسخه قدیم مسلط و ناسازگاری بسازد.
Update policy را براساس compatibility طراحی کنید:
- Patch سازگار: فعالسازی در Navigation/close یا Refresh اختیاری؛
- Schema/API ناسازگار: version negotiation و migration؛
- Security critical: پیام واضح، حفظ Draft، زمانبندی Reload امن؛
- Checkout/Form باز: هرگز Refresh اجباری و از دستدادن State؛
- Multi-tab: controller/version coordination و telemetry.
Banner «نسخه جدید آماده است» باید Action update later/now، تغییر مهم و recovery داشته باشد. Cache cleanup را فقط برای namespace خود انجام دهید.
Push را پس از Value proposition درخواست کنید
در اولین Load، Permission prompt ندهید. راهنمای Permission UX در web.dev درخواست Contextual پس از ارزش روشن، Custom pre-prompt و Settings/opt-out را توصیه میکند. Native prompt را فقط پس از gesture و انتخاب مثبت کاربر فراخوانی کنید.
Notification contract شامل Purpose/topic، urgency، frequency cap، quiet hours، sensitive preview، deep link، expiry، dedupe، preference، unsubscribe و measurement است. Marketing، Transactional و Security را جدا کنید. Push delivery قطعی نیست؛ داخل App نیز inbox/status داشته باشید.
Web Push روی iOS را با شرط Home Screen طراحی کنید
اعلام رسمی WebKit پشتیبانی Web Push را از iOS/iPadOS ۱۶.۴ برای Web app افزودهشده به Home Screen توضیح میدهد. این Snapshot را قانون همه Deviceها نکنید؛ Version/mode/capability را Test کنید و Push را شرط Core journey نگذارید.
اگر capability غایب است، Email/SMS/in-app یا manual refresh را براساس Permission/Cost/Risk ارائه دهید. Instruction نصب صرفاً برای گرفتن Push بدون ارزش پایدار، Dark pattern است.
Loading و Performance باید State-aware باشند
Skeleton فقط وقتی Shape پایدار است؛ برای انتظار کوتاه میتواند Noise بسازد. Progress determinate برای Upload/Download و «در حال همگامسازی X مورد» از Spinner بیپایان بهتر است. Cancel/Retry و حفظ work را برای عملیات طولانی بدهید.
LCP/INP/CLS را در Browser و standalone، cold/warm cache، Service Worker versions و شبکه/دستگاه ایران با RUM بسنجید. Service Worker میتواند Startup/Fetch overhead یا stale bug بسازد؛ وجود آن Performance را تضمین نمیکند. روش تفصیلی در راهنمای Core Web Vitals و RUM آمده است.
دسترسپذیری در Stateهای غیرعادی حیاتیتر است
WCAG 2.2 در ۴.۱.۳ میخواهد Status message بدون جابهجایی Focus برای فناوری کمکی قابلتشخیص باشد. Offline، queued، synced، update-ready، conflict و permission result را با semantic live region مناسب اعلان کنید؛ هر تغییر جزئی را assertive نکنید.
- Keyboard/focus order/visible focus و Focus restoration پس از Dialog/Update؛
- Target size و alternative برای Drag/Swipe؛
- Zoom/reflow/text spacing و safe keyboard؛
- Contrast/non-color status و Reduced motion؛
- Label/error/help مرتبط و draft recovery؛
- Screen reader test روی Offline/Conflict/Permission، نه فقط happy path.
فرایند کامل در راهنمای طراحی فراگیر و WCAG پوشش داده شده است.
فارسی و RTL را با داده واقعی تست کنید
عنوان/short name/Icon label، Notification، Install instruction، Error و Timestamp فارسی را روی device واقعی ببینید. متن مختلط SKU/URL/شماره/تومان-ریال و bidi isolation، نیمفاصله، ی/ک، ارقام، جمع/Plural، date/calendar/timezone و truncation را Fixture کنید. دکمه Back/Next را صرفاً Mirror تصویری نکنید؛ معنای جهت را بسنجید.
Font offline subset باید Coverage فارسی و fallback بدون CLS داشته باشد. Cache کردن Font مجوز License را عوض نمیکند.
Form، Auth و Payment در اتصال ناپایدار
Form را autosave draft کنید، اما Password/OTP/Card/حساس را بیدلیل Persist نکنید. Auth expiry هنگام Sync باید Draft را حفظ و پس از Re-auth ادامه دهد. OTP timer با Server time/expiry و resend contract هماهنگ باشد. Submit تکراری با Idempotency و lock منطقی کنترل شود.
Checkout نیاز به price/inventory revalidation و Payment نیاز به status inquiry دارد. راهنمای UX موبایل فروشگاه و Payment recovery این Funnel حساس را تخصصیتر پوشش میدهد.
Privacy و Security در Storage/Cache/Push
- Data classification و حداقل Storage؛ داده شخصی را عمومی cache نکنید.
- Cache/IndexedDB key شامل tenant/user و logout/revocation purge باشد.
- Encryption-at-rest در Browser را مرز کامل ننامید؛ XSS همان session میتواند دسترسی داشته باشد.
- CSP/Trusted Types/dependency integrity و input/output safety را حفظ کنید.
- Push payload حساس را در Lock screen آشکار نکنید.
- Subscription token را credential-like نگهداری و revoke/rotate کنید.
- Service Worker scope و fetch route allowlist محدود باشد.
«دستگاه شخصی» فرض نکنید؛ shared device، multiple account و logout offline را تست کنید. Clear data و export/delete policy را توضیح دهید.
Navigation و SEO باید با هم کار کنند
هر صفحه عمومی مهم URL/Title/Heading/Canonical/HTTP response قابلفهم دارد. App shell نباید برای ۴۰۴/۵۰۰/offline پاسخ ۲۰۰ یکسان بسازد. Deep link از Search/Push/Share باید محتوای درست، Auth continuation و fallback بدهد. Router navigation و server route map را هماهنگ کنید.
Browser/Device/Mode matrix بسازید
| محور | نمونه Cohort | سناریو |
|---|---|---|
| Browser/OS | Android Chromium، iOS Safari/Home Screen، Desktop | Install/Push/Back/Share |
| Mode | tab/standalone/multi-tab | Auth/deep link/update |
| Network | cold/warm، slow/flaky/offline/captive | read/write/retry/reconcile |
| Storage | normal/low quota/evicted/private | cache/draft/logout |
| Version | N، N-۱، long-stale | schema/API/cache migration |
| Accessibility | keyboard/screen reader/zoom/reduced motion | status/conflict/permission |
Feature detection را با real-device test جایگزین نکنید؛ هر دو لازماند. Emulator محدودیت storage/OS prompt/installation lifecycle را کامل بازتولید نمیکند.
Failure injection و Testهای ضروری
- قطع شبکه پیش/حین/پس از Submit و بازگشت دیرهنگام Response؛
- Duplicate tap/request، timeout با Server success و reconcile؛
- Auth expiry/revocation در Queue؛
- Conflict همزمان دو Device و clock skew؛
- Cache miss/corruption/quota eviction و wrong-user data؛
- Update waiting، skipWaiting mismatch و tabهای چندنسخه؛
- Push denied/revoked/expired/deep-link invalid؛
- RTL/Font offline/long text/keyboard/zoom/screen reader؛
- Payment callback duplicate/unknown/late و refund state.
Test باید Outcome و Recovery را assert کند، نه فقط وجود DOM. Synthetic، contract/integration، real device و RUM مکملاند.
Measurement از Install فراتر میرود
Metric tree:
- Eligibility/prompt: eligible، shown، clicked، accepted/dismissed؛
- Activation: first standalone launch، core journey completion؛
- Reliability: offline fallback success، queued/confirmed/failed/conflict، data loss/duplicate؛
- Freshness: stale exposure و time-to-fresh؛
- Update: waiting age، update accept، reload loss/error؛
- Push: permission by context، delivery/open/action/disable/complaint؛
- Outcome: task success/time، retained value، support/return؛
- Guardrail: crash، wrong-user data، privacy/security، battery/data complaint.
Browser و installed cohort خودانتخاباند؛ retention بهتر installed کاربران الزاماً اثر نصب نیست. Holdout/eligibility-aware comparison و qualitative research لازم است. Eventها را با consent/minimization و بدون PII اضافی طراحی کنید.
سناریوی ایران: سفارش در شبکه Flaky
فروشگاه Catalog و تصاویر اخیر را cached با «آخرین بهروزرسانی» نشان میدهد؛ سبد بهصورت Draft محلی حفظ میشود، اما قبل از Checkout قیمت/موجودی revalidate میشود. اگر شبکه قطع باشد، کاربر میتواند سبد را نگه دارد، نه اینکه Payment جعلی بگیرد.
- پس از بازگشت اتصال، Server تغییر قیمت/موجودی را با Diff برمیگرداند.
- Order creation operation ID دارد؛ timeout به Pending confirmation میرود.
- PSP callback/inquiry نتیجه را reconcile و Duplicate را حذف میکند.
- SMS failure Order را rollback نمیکند؛ status داخل App باقی است.
- Update آماده در Checkout defer و پس از خروج امن اعمال میشود.
- RUM بین ISP/device/browser/mode و cold/warm cache تفکیک میشود.
برنامه ۳۰/۶۰/۹۰ روزه
روز ۱ تا ۳۰: Journey و State Contract
Job/criticality/data/network/failure را برای سه Journey اصلی ثبت، Baseline task/performance/error را بگیرید و capability/mode/state matrix بسازید. Offline/Install/Push promise را محدود و Acceptance بنویسید.
روز ۳۱ تا ۶۰: Pilot و Failure Test
یک Read cache، یک Draft/Queue کمریسک، install prompt Contextual و update UX بسازید. Idempotency/Reconciliation، accessibility/RTL و network/storage/version failure suite را اجرا و RUM marker اضافه کنید.
روز ۶۱ تا ۹۰: Scale، Hold یا Rollback
Task success، data loss/duplicate، stale exposure، sync conflict، update error، Push disable و support را تحلیل کنید. فقط Journey ارزشمند و قابلاعتماد را گسترش دهید؛ Capability کمپشتیبانی یا پرریسک را Hold/Fallback و Regression را Rollback کنید.
چکلیست پذیرش UX در PWA
- Core journey بدون نصب/Push و با Progressive Enhancement کار میکند.
- Browser/installed، network، freshness، mutation، auth و version state قرارداد دارند.
- Install فقط پس از Value و capability واقعی پیشنهاد و dismissal محترم شمرده میشود.
- Offline level برای هر Journey مشخص و online-only صادقانه توضیح داده شده است.
- Cached data زمان/Source/Freshness و action Refresh دارد.
- Draft/Queue، ID، bounded retry، idempotency، conflict و reconciliation دارند.
- Update وسط Task داده را از دست نمیدهد و version compatibility آزموده شده است.
- Push Contextual، preference-based، قابللغو و دارای in-app fallback است.
- Keyboard/screen reader/zoom/reduced-motion/RTL و status message تست شدهاند.
- Real-device matrix، failure injection، RUM، Guardrail و rollback وجود دارد.
سؤالات متداول درباره تجربه کاربری PWA
۱. آیا PWA باید کاملاً آفلاین کار کند؟
نه. حداقل باید Error عمومی Browser را با fallback مفید جایگزین کند. Read cache، Draft، Queue یا full offline براساس Job، Freshness، Correctness، Security و هزینه عملیات انتخاب میشوند؛ Payment معمولاً revalidation/online confirmation میخواهد.
۲. بهترین زمان نمایش پیام نصب PWA چه موقع است؟
پس از مشاهده ارزش—مثلاً Journey موفق یا بازگشت کاربر—و فقط وقتی مسیر نصب در همان Browser/Mode موجود است. Prompt در اولین Load، تکرار پس از dismissal یا شرطکردن محتوا/Checkout تجربه بد است.
۳. آیا beforeinstallprompt روی همه مرورگرها کار میکند؟
خیر؛ non-standard و عمدتاً Chromium-based است. Event واقعی را feature-detect کنید و برای محیطهای دیگر instruction/fallback مرتبط بدهید. روی iOS مسیر Add to Home Screen متفاوت است.
۴. چگونه از ثبت تکراری سفارش در اینترنت ضعیف جلوگیری کنیم؟
با operation/idempotency key سمت Client و Server، وضعیت Pending پس از timeout، Inquiry/Reconciliation و UI تأییدشده. Disable کردن دکمه بهتنهایی duplicate شبکه/Callback را حل نمیکند.
۵. آیا نصب PWA باعث Retention بهتر میشود؟
ممکن است با Fit خوب کمک کند، اما کاربران نصبکننده خودانتخاباند. Install count یا مقایسه خام cohort علت را ثابت نمیکند؛ core-task success، reliability و retained value را با design کنترلشده و research بسنجید.
موضوعات مکمل برای توسعه این خوشه
۱. PWA State UX kit فارسی
Component و copy آماده برای offline/degraded/stale/queued/syncing/confirmed/failed/conflict/update/permission با ARIA، RTL و Design token میتواند کیفیت تیمها را بالا ببرد.
۲. آزمایشگاه Sync، Conflict و Reconciliation
Service Worker/IndexedDB queue، operation ID، ETag/base version، retry/backoff، auth expiry، duplicate/late response و multi-device conflict یک Test harness عملی میسازد.
۳. ماتریس واقعی نصب و Push PWA در دستگاههای ایران
Browser/OS/mode/version، install path، Web Push، Background Sync، storage eviction، شبکه ISP و RUM تاریخدار خلأ تصمیم مهمی است.
جمعبندی
تجربه خوب PWA با Icon و Fullscreen ساخته نمیشود؛ با صداقت State ساخته میشود. Capability را Progressive اضافه کنید، نصب و Permission را پس از ارزش بخواهید، Offline را برای هر Journey سطحبندی و Freshness را آشکار کنید. Draft را از Queue، Pending را از Success و Timeout را از Failure قطعی جدا کنید. Update، Cache، Push و Payment را به lifecycle و recovery مجهز و Accessibility/RTL را در Failure state تست کنید. PWA زمانی موفق است که در شبکه و دستگاه واقعی کار کاربر را حفظ، عدمقطعیت را توضیح و مسیر بازیابی بدهد—چه نصب شده باشد، چه فقط در یک Tab وب باز باشد.






