این کد لبه بخشی از دوره آموزشی Developing Progressive Web Apps است که توسط تیم آموزشی Google Developers ایجاد شده است. اگر به ترتیب روی کدها کار کنید، بیشترین ارزش را از این دوره خواهید گرفت.
برای جزئیات کامل در مورد دوره، به نمای کلی توسعه برنامه های وب پیشرفته مراجعه کنید .
مقدمه
در این آزمایشگاه شما از Lighthouse برای ممیزی وب سایت برای استانداردهای برنامه پیشرو وب (PWA) استفاده خواهید کرد. همچنین قابلیت آفلاین را با API service worker اضافه خواهید کرد.
چیزی که یاد خواهید گرفت
- نحوه ممیزی سایت ها با Lighthouse
- نحوه اضافه کردن قابلیت های آفلاین به یک برنامه
آنچه شما باید بدانید
- HTML، CSS و جاوا اسکریپت اولیه
- آشنایی با ES2015 Promises
آنچه شما نیاز خواهید داشت
- کامپیوتر با دسترسی ترمینال/پوسته
- اتصال به اینترنت
- مرورگر کروم (برای استفاده از Lighthouse)
- یک ویرایشگر متن
- اختیاری: Chrome در دستگاه Android
مخزن pwa-training-labs را از github دانلود یا کلون کنید و در صورت نیاز نسخه LTS Node.js را نصب کنید.
به دایرکتوری offline-quickstart-lab/app/ بروید و یک سرور توسعه محلی راه اندازی کنید:
cd offline-quickstart-lab/app npm install node server.js
شما می توانید سرور را در هر زمان با Ctrl-c خاتمه دهید.
مرورگر خود را باز کنید و به localhost:8081/ بروید. باید ببینید که سایت یک صفحه وب ساده و ثابت است.
توجه: همه سرویسکاران را لغو ثبت کنید و تمام کشهای سرویسکار را برای لوکال هاست پاک کنید تا با آزمایشگاه تداخل نداشته باشند. در Chrome DevTools، میتوانید با کلیک کردن بر روی پاک کردن دادههای سایت از بخش پاک کردن فضای ذخیرهسازی تب Application به این مهم دست یابید.
پوشه offline-quickstart-lab/app/ را در ویرایشگر متن دلخواه خود باز کنید. app/ پوشه جایی است که آزمایشگاه را می سازید.
این پوشه شامل:
-
images/پوشه حاوی تصاویر نمونه است -
styles/main.cssشیوه نامه اصلی است -
index.htmlصفحه اصلی HTML برای سایت نمونه ما است -
package-lock.jsonوpackage.jsonوابستگی های برنامه را دنبال می کنند (تنها وابستگی ها در این مورد برای سرور توسعه محلی است) -
server.jsیک سرور توسعه محلی برای آزمایش است -
service-worker.jsفایل Service Worker است (در حال حاضر خالی است)
قبل از شروع تغییرات در سایت، اجازه دهید با Lighthouse حسابرسی کنیم تا ببینیم چه چیزی می تواند بهبود یابد.
به برنامه (در کروم) برگردید و برگه حسابرسی ابزارهای برنامهنویس را باز کنید. شما باید نماد Lighthouse و گزینه های پیکربندی را ببینید. «Mobile» را برای دستگاه انتخاب کنید، همه حسابرسی ها را انتخاب کنید، یکی از گزینه های Throttling را انتخاب کنید و گزینه Clear storage را انتخاب کنید:

روی Run audits کلیک کنید. ممیزی ها چند لحظه طول می کشد تا کامل شوند.
توضیح
پس از تکمیل ممیزی، باید گزارشی با امتیازات در Developer Tools ببینید. باید نمرات را نشان دهد، چیزی شبیه به این (نمرات ممکن است دقیقاً یکسان نباشند):
توجه: امتیازات Lighthouse یک تقریبی است و می تواند تحت تأثیر محیط شما باشد (مثلاً اگر تعداد زیادی پنجره مرورگر باز دارید). نمرات شما ممکن است دقیقاً مشابه امتیازهای نشان داده شده در اینجا نباشد.

و بخش Progressive Web App باید شبیه به این باشد:

این گزارش دارای امتیازات و معیارهایی در پنج دسته است:
- برنامه وب پیشرو
- عملکرد
- قابلیت دسترسی
- بهترین شیوه ها
- سئو
همانطور که می بینید، برنامه ما در دسته برنامه وب پیشرو (PWA) امتیاز ضعیفی کسب می کند. بیایید امتیاز خود را بهبود بخشیم!
لحظه ای به بخش PWA گزارش نگاه کنید و ببینید چه چیزی کم است.
ثبت نام کارگر خدماتی
یکی از خرابی های ذکر شده در گزارش این است که هیچ خدمتگزار ثبت نام نشده است. ما در حال حاضر یک فایل Service Worker خالی در app/service-worker.js داریم.
اسکریپت زیر را درست قبل از بسته شدن تگ </body> به پایین index.html اضافه کنید:
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('service-worker.js')
.then(reg => {
console.log('Service worker registered! 😎', reg);
})
.catch(err => {
console.log('😥 Service worker registration failed: ', err);
});
});
}
</script>توضیح
این کد پس از بارگیری صفحه، فایل خالی service-worker.js را ثبت می کند. با این حال فایل سرویس کارگر فعلی خالی است و کاری انجام نمی دهد. بیایید کد سرویس را در مرحله بعد اضافه کنیم.
منابع پیش کش
یکی دیگر از مشکلات ذکر شده در گزارش این است که برنامه در حالت آفلاین با کد وضعیت 200 پاسخ نمی دهد. برای حل این مشکل باید کارمند خدمات خود را به روز کنیم.
کد زیر را به فایل Service Worker ( service-worker.js ) اضافه کنید:
const cacheName = 'cache-v1';
const precacheResources = [
'/',
'index.html',
'styles/main.css',
'images/space1.jpg',
'images/space2.jpg',
'images/space3.jpg'
];
self.addEventListener('install', event => {
console.log('Service worker install event!');
event.waitUntil(
caches.open(cacheName)
.then(cache => {
return cache.addAll(precacheResources);
})
);
});
self.addEventListener('activate', event => {
console.log('Service worker activate event!');
});
self.addEventListener('fetch', event => {
console.log('Fetch intercepted for:', event.request.url);
event.respondWith(caches.match(event.request)
.then(cachedResponse => {
if (cachedResponse) {
return cachedResponse;
}
return fetch(event.request);
})
);
});حالا به مرورگر برگردید و سایت را رفرش کنید. کنسول را بررسی کنید تا ببینید که سرویس دهنده:
- ثبت شده است
- نصب شده است
- فعال شد
توجه: اگر قبلاً سرویسکار را ثبتنام کردهاید، یا برای فعال کردن همه رویدادها با مشکل مواجه هستید، ثبت نام هر سرویسکار را لغو کرده و صفحه را بازخوانی کنید. اگر موفق نشد، تمام نمونه های برنامه را ببندید و دوباره باز کنید.
سپس، سرور توسعه محلی را در خط فرمان خود با اجرای Ctrl + c خاتمه دهید. سایت را دوباره رفرش کنید و مشاهده کنید که با وجود آفلاین بودن سرور، بارگذاری می شود!
توجه: ممکن است یک خطای کنسول ببینید که نشان میدهد سرویسکار نمیتواند واکشی شود: An unknown error occurred when fetching the script. service-worker.js Failed to load resource: net::ERR_CONNECTION_REFUSED . این خطا به این دلیل نشان داده میشود که مرورگر نمیتواند اسکریپت Service Worker را واکشی کند (زیرا سایت آفلاین است)، اما این مورد انتظار است زیرا ما نمیتوانیم از سرویسکار برای کش کردن خود استفاده کنیم. در غیر این صورت مرورگر کاربر برای همیشه با همان سرویس دهنده گیر می کرد!
توضیح
هنگامی که سرویسکار توسط اسکریپت ثبت در index.html ثبت شد، رویداد install سرویسکار رخ میدهد. در طول این رویداد، شنونده رویداد install یک حافظه پنهان با نام را باز می کند و فایل های مشخص شده با متد cache.addAll را در حافظه پنهان ذخیره می کند. این "پیش کش" نامیده می شود زیرا در طول رویداد install اتفاق می افتد، که معمولاً اولین بار است که کاربر از سایت شما بازدید می کند.
پس از نصب یک سرویسکار، و اگر سرویسکار دیگری در حال حاضر صفحه را کنترل نکند، سرویسکار جدید «فعال میشود» (شنونده رویداد activate در سرویسکار فعال میشود) و شروع به کنترل صفحه میکند.
هنگامی که منابع توسط صفحهای درخواست میشود که یک سرویسکار فعال آن را کنترل میکند، درخواستها مانند یک پروکسی شبکه از طریق سرویسکار ارسال میشوند. برای هر درخواست یک رویداد fetch راه اندازی می شود. در سرویسکار ما، شنونده رویداد fetch حافظههای پنهان را جستجو میکند و در صورت وجود منبع ذخیرهشده به آن پاسخ میدهد. اگر منبع ذخیره نشده باشد، منبع به طور معمول درخواست می شود.
ذخیره منابع به برنامه اجازه می دهد تا با اجتناب از درخواست های شبکه به صورت آفلاین کار کند. اکنون برنامه ما می تواند در صورت آفلاین بودن با کد وضعیت 200 پاسخ دهد!
توجه: رویداد activate برای هیچ چیز غیر از ورود به سیستم در این مثال استفاده نمی شود. این رویداد برای کمک به رفع اشکالات مربوط به چرخه عمر کارگر خدمات گنجانده شد.
اختیاری : همچنین میتوانید با گسترش بخش ذخیرهسازی حافظه پنهان، منابع ذخیرهشده را در تب Application Developer Tools مشاهده کنید:

سرور توسعه را با node server.js راه اندازی مجدد کنید و سایت را به روز کنید. سپس دوباره تب Audits را در Developer Tools باز کنید و با انتخاب New Audit (علامت مثبت در گوشه سمت چپ بالا) بازرسی Lighthouse را دوباره اجرا کنید. هنگامی که ممیزی به پایان رسید، باید ببینید که امتیاز PWA ما به طور قابل توجهی بهتر است، اما همچنان میتوان آن را بهبود بخشید. ما در بخش بعدی به بهبود امتیاز خود ادامه خواهیم داد.
توجه: این بخش اختیاری است زیرا آزمایش بنر نصب برنامه وب خارج از محدوده آزمایشگاهی است. شما می توانید آن را به تنهایی با استفاده از اشکال زدایی از راه دور امتحان کنید.
امتیاز PWA ما هنوز عالی نیست. برخی از خرابیهای باقیمانده ذکر شده در گزارش این است که از کاربر خواسته نمیشود برنامه وب ما را نصب کند، و اینکه ما یک صفحه نمایش یا رنگهای برند را در نوار آدرس پیکربندی نکردهایم. ما میتوانیم این مشکلات را برطرف کنیم و با رعایت برخی معیارهای اضافی ، افزودن به صفحه اصلی را به تدریج اجرا کنیم. مهمتر از همه، ما باید یک فایل مانیفست ایجاد کنیم.
یک فایل Manifest ایجاد کنید
یک فایل در app/ به نام manifest.json ایجاد کنید و کد زیر را اضافه کنید:
{
"name": "Space Missions",
"short_name": "Space Missions",
"lang": "en-US",
"start_url": "/index.html",
"display": "standalone",
"theme_color": "#FF9800",
"background_color": "#FF9800",
"icons": [
{
"src": "images/touch/icon-128x128.png",
"sizes": "128x128"
},
{
"src": "images/touch/icon-192x192.png",
"sizes": "192x192"
},
{
"src": "images/touch/icon-256x256.png",
"sizes": "256x256"
},
{
"src": "images/touch/icon-384x384.png",
"sizes": "384x384"
},
{
"src": "images/touch/icon-512x512.png",
"sizes": "512x512"
}
]
}تصاویر ارجاع شده در مانیفست قبلاً در برنامه ارائه شده است.
سپس HTML زیر را به پایین تگ <head> در index.html اضافه کنید:
<link rel="manifest" href="manifest.json">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="application-name" content="Space Missions">
<meta name="apple-mobile-web-app-title" content="Space Missions">
<meta name="theme-color" content="#FF9800">
<meta name="msapplication-navbutton-color" content="#FF9800">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="msapplication-starturl" content="/index.html">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" sizes="128x128" href="/images/touch/icon-128x128.png">
<link rel="apple-touch-icon" sizes="128x128" href="/images/touch/icon-128x128.png">
<link rel="icon" sizes="192x192" href="icon-192x192.png">
<link rel="apple-touch-icon" sizes="192x192" href="/images/touch/icon-192x192.png">
<link rel="icon" sizes="256x256" href="/images/touch/icon-256x256.png">
<link rel="apple-touch-icon" sizes="256x256" href="/images/touch/icon-256x256.png">
<link rel="icon" sizes="384x384" href="/images/touch/icon-384x384.png">
<link rel="apple-touch-icon" sizes="384x384" href="/images/touch/icon-384x384.png">
<link rel="icon" sizes="512x512" href="/images/touch/icon-512x512.png">
<link rel="apple-touch-icon" sizes="512x512" href="/images/touch/icon-512x512.png"> به سایت برگردید. در تب Application Developer Tools، بخش Clear Storage را انتخاب کنید و روی Clear site data کلیک کنید. سپس صفحه را رفرش کنید. حال قسمت Manifest را انتخاب کنید. باید نمادها و گزینه های پیکربندی که در فایل manifest.json پیکربندی شده اند را ببینید. اگر تغییرات خود را نمی بینید، سایت را در یک پنجره ناشناس باز کنید و دوباره بررسی کنید.
توضیح
فایل manifest.json به مرورگر میگوید که چگونه برخی از جنبههای پیشرونده برنامه شما، مانند مرورگر کروم، نماد صفحه اصلی و صفحه نمایش اسپلش را استایل و قالببندی کند. همچنین میتوان از آن برای پیکربندی برنامه وب خود برای باز کردن در حالت standalone ، مانند یک برنامه بومی (به عبارت دیگر، خارج از مرورگر) استفاده کرد.
پشتیبانی هنوز برای برخی از مرورگرها تا زمان نگارش این مقاله در حال توسعه است و تگهای <meta> زیرمجموعهای از این ویژگیها را برای مرورگرهایی که هنوز پشتیبانی کامل ندارند پیکربندی میکنند.
ما مجبور شدیم اطلاعات سایت را پاک کنیم تا نسخه کش قدیمی index.html را حذف کنیم (زیرا آن نسخه پیوند مانیفست را نداشت). یک ممیزی دیگر Lighthouse را امتحان کنید و ببینید که نمره PWA چقدر بهبود یافته است!
فعال کردن درخواست نصب
گام بعدی برای نصب برنامه ما نشان دادن اعلان نصب به کاربران است. Chrome 67 به طور خودکار از کاربران درخواست میکرد، اما از Chrome 68 ، درخواست نصب باید به صورت برنامهریزی در پاسخ به اشاره کاربر فعال شود.
دکمه و بنر «نصب برنامه» را به بالای index.html (درست بعد از تگ <main> ) با کد زیر اضافه کنید:
<section id="installBanner" class="banner">
<button id="installBtn">Install app</button>
</section> سپس با افزودن سبک های زیر به styles/main.css بنر را استایل دهید:
.banner {
align-content: center;
display: none;
justify-content: center;
width: 100%;
} فایل را ذخیره کنید. در نهایت تگ اسکریپت زیر را به index.html اضافه کنید:
<script>
let deferredPrompt;
window.addEventListener('beforeinstallprompt', event => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
event.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = event;
// Attach the install prompt to a user gesture
document.querySelector('#installBtn').addEventListener('click', event => {
// Show the prompt
deferredPrompt.prompt();
// Wait for the user to respond to the prompt
deferredPrompt.userChoice
.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the A2HS prompt');
} else {
console.log('User dismissed the A2HS prompt');
}
deferredPrompt = null;
});
});
// Update UI notify the user they can add to home screen
document.querySelector('#installBanner').style.display = 'flex';
});
</script>فایل را ذخیره کنید. با استفاده از اشکال زدایی از راه دور ، برنامه را در Chrome در دستگاه Android باز کنید. وقتی صفحه بارگیری میشود، باید دکمه «نصب برنامه» را ببینید (آن را روی دسکتاپ نمیبینید، بنابراین مطمئن شوید که در حال آزمایش روی موبایل هستید). روی دکمه کلیک کنید و اعلان افزودن به صفحه اصلی باید ظاهر شود. مراحل نصب برنامه را روی دستگاه خود دنبال کنید. پس از نصب، باید بتوانید برنامه وب را در حالت مستقل (خارج از مرورگر) با ضربه زدن روی نماد صفحه اصلی تازه ایجاد شده باز کنید.
توضیح
کد HTML & CSS یک بنر و دکمه مخفی اضافه می کند که می توانیم از آن برای فعال کردن درخواست نصب به کاربران استفاده کنیم.
هنگامی که رویداد beforeinstallprompt فعال میشود، از تجربه پیشفرض (که در آن Chrome 67 و نسخههای قبلی بهطور خودکار از کاربران درخواست میکند نصب کنند) جلوگیری میکنیم و beforeinstallevent در متغیر deferredPrompt جهانی ثبت میکنیم. سپس دکمه «نصب برنامه» به گونهای پیکربندی میشود که درخواست را با متد prompt() beforeinstallevent نشان دهد. پس از انتخاب کاربر (نصب یا عدم نصب)، وعده userChoice با انتخاب کاربر ( outcome ) حل می شود. در نهایت، هنگامی که همه چیز آماده شد، دکمه نصب را نمایش می دهیم.
شما یاد گرفته اید که چگونه سایت ها را با Lighthouse ممیزی کنید، و چگونه اصول عملکرد آفلاین را پیاده سازی کنید. اگر بخش های اختیاری را تکمیل کرده باشید، نحوه نصب برنامه های وب را در صفحه اصلی نیز یاد گرفته اید!
منابع بیشتر
Lighthouse منبع باز است! میتوانید آن را فورک کنید، آزمایشهای خود را اضافه کنید و باگها را فایل کنید. Lighthouse همچنین به عنوان یک ابزار خط فرمان برای ادغام با فرآیندهای ساخت در دسترس است.
برای مشاهده تمامی کدهای دوره آموزشی PWA به کدلبورد خوش آمدگویی دوره مراجعه کنید/