نحوه ساخت یک اپ PWA از صفر با استفاده از HTML ،CSS و جاوااسکریپت
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 10 دقیقه

نحوه ساخت یک اپ PWA از صفر با استفاده از HTML ،CSS و جاوااسکریپت

وب اپ پیشرونده ( PWA ) روشی است برای اینکه حس کار کردن با یک برنامه بومی موبایل را به برنامه‌های وب بیاوریم. با کمک PWA می‌توانیم برنامه‌های وب را به ویژگی‌های یک برنامه موبایل بومی تجهیز کنیم تا دسترس‌پذیری و تجربه کاربری وب اپ خود را افزایش دهیم.

در این مقاله قصد دارم یک وب اپ پیشرونده را با استفاده از HTML، CSS و جاوااسکریپت بسازم. و این‌ها هم موضوعاتی هستند که مورد بحث قرار می‌دهم:

  • وب اپ پیشرونده چیست؟
  • Markup (نشانه گذاری)
  • استایل‌دهی
  • نمایش اطلاعات به وسیله جاوااسکریپت
  • Web App Manifest
  • Service Worker چیست؟
  • کش کردن داده‌ها
  • واکشی داده‌ها
  • رجیستر کردن Service Worker
  • افکار نهایی
  • قدم‌هایی بعدی

پس بیایید با یک سوال مهم شروع کنیم: PWA چیست؟

وب اپ پیشرونده چیست؟

وب اپ پیشرونده یک برنامه وب است که با استفاده از قابلیت‌های وب مدرن ، یک تجربه کاربری مانند برنامه‌های بومی موبایل را به کاربران ارائه می‌دهد. و اما در نهایت، این فقط یک وبسایت معمولی است که بر روی مرورگر اجرا می‌شود با برخی از قابلیت‌های جدید. و به شما این توانایی‌ها را نیز می‌دهد:

  • بر روی موبایل نصب می‌شود
  • به صورت آفلاین هم می‌توان از آن استفاده کرد
  • دسترسی به دوربین
  • دسترسی به اعلانات موبایل
  • و امکان همگام‌سازی در پس زمینه

و قابلیت‌های دیگر.

با این وجود برای اینکه بتوانیم یک وبسایت معمولی را به وب اپ پیشرونده تبدیل کنیم. باید با اضافه کردن یک فایل manifest و یک  service worker آن را کمی تنظیم کنیم.

در مورد این شرایط جدید نگران نباشید در زیر آن‌ها را توضیح خواهم داد.

اما ابتدا ما باید وبسایت ساده خود را بسازیم. پس بیاید از نشانه گذاری شروع کنیم.

Markup (نشانه گذاری)

فایل HTML ما نسبتا ساده است. همه چیز را داخل تگ main قرار می‌دهیم.

درون فایل index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link rel="stylesheet" href="css/style.css" />
    <title>Dev'Coffee PWA</title>
  </head>
  <body>
    <main>
      <nav>
        <h1>Dev'Coffee</h1>
        <ul>
          <li>Home</li>
          <li>About</li>
          <li>Blog</li>
        </ul>
      </nav>
      <div class="container"></div>
    </main>
    <script src="js/app.js"></script>
  </body>
</html>

و یک نوار ناوبری با تگ nav ایجاد می‌کنیم. سپس یک div با کلاس container اضافه می‌کنیم که قرار است کارت‌های ما که بعدا با جاوااسکریپت می‌سازیم درون آن قرار بگیرند.

حالا که این قسمت را انجام دادیم، اجازه دهید به کد خود استایل بدهیم.

استایل‌دهی

در اینجا ، طبق معمول ، با وارد کردن فونت‌های مورد نیاز خود شروع می‌کنیم. سپس برای جلوگیری از رفتار پیش فرض برخی از تگ‌ها، تنظیم مجدد را انجام خواهیم داد.

درون فایل css/style.css

@import url("https://fonts.googleapis.com/css?family=Nunito:400,700&display=swap");
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  background: #fdfdfd;
  font-family: "Nunito", sans-serif;
  font-size: 1rem;
}
main {
  max-width: 900px;
  margin: auto;
  padding: 0.5rem;
  text-align: center;
}
nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
ul {
  list-style: none;
  display: flex;
}

li {
  margin-right: 1rem;
}
h1 {
  color: #e74c3c;
  margin-bottom: 0.5rem;
}

سپس ، ما حداکثر عرض تگ main را به 900px محدود می‌کنیم تا در یک صفحه بزرگ خوب به نظر برسد.

برای navabr، می‌خواهم لوگو در سمت چپ و لینک‌ها سمت راست باشند. بنابراین برای تگ nav، پس از تبدیل آن به یک flex container، ما از justify-content: space-between; برای تراز کردن آن‌ها استفاده می‌کنیم.

درون فایل css/style.css

.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  grid-gap: 1rem;
  justify-content: center;
  align-items: center;
  margin: auto;
  padding: 1rem 0;
}
.card {
  display: flex;
  align-items: center;
  flex-direction: column;
  width: 15rem auto;
  height: 15rem;
  background: #fff;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
  border-radius: 10px;
  margin: auto;
  overflow: hidden;
}
.card--avatar {
  width: 100%;
  height: 10rem;
  object-fit: cover;
}
.card--title {
  color: #222;
  font-weight: 700;
  text-transform: capitalize;
  font-size: 1.1rem;
  margin-top: 0.5rem;
}
.card--link {
  text-decoration: none;
  background: #db4938;
  color: #fff;
  padding: 0.3rem 1rem;
  border-radius: 20px;
}

ما چندین کارت خواهیم داشت. بنابراین عنصر container به صورت شبکه نمایش داده خواهد شد. و با ((grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr حالا می‌توانیم کارت‌های خود را به گونه‌ای تنظیم کنیم که در صورت وجود فضای کافی از عرض 15rem استفاده کند و گرنه از 1fr استفاده کند.

و برای اینکه آن‌ها زیبا به نظر برسند، افکت سایه بر روی کلاس card را دو برابر می‌کنیم. و از object-fit: cover برای .card--avatar  استفاده می‌کنیم تا از کشش تصویر جلوگیری شود.

حالا خیلی بهتر به نظر می‌رسد. اما ما هنوز داده‌ای برای نشان دادن نداریم.

بیایید آن را در بخش بعدی اضافه کنیم

نمایش اطلاعات به وسیله جاوااسکریپت

توجه کنید که من از تصاویر بزرگ استفاده کردم که مدتی طول می‌کشد تا بارگیری شود. این به بهترین نحو قدرت service worker را به شما نشان می‌دهد.

همان طور که قبلا گفتم. کلاس container کارت‌های ما را نگه می‌دارد. بنابراین، باید آن را انتخاب کنیم.

درون فایل js/app.js

const container = document.querySelector(".container")
const coffees = [
  { name: "Perspiciatis", image: "images/coffee1.jpg" },
  { name: "Voluptatem", image: "images/coffee2.jpg" },
  { name: "Explicabo", image: "images/coffee3.jpg" },
  { name: "Rchitecto", image: "images/coffee4.jpg" },
  { name: " Beatae", image: "images/coffee5.jpg" },
  { name: " Vitae", image: "images/coffee6.jpg" },
  { name: "Inventore", image: "images/coffee7.jpg" },
  { name: "Veritatis", image: "images/coffee8.jpg" },
  { name: "Accusantium", image: "images/coffee9.jpg" },
]

سپس، ما یک آرایه کارت با نام و تصاویر ایجاد می‌کنیم.

درون فایل js/app.js

const showCoffees = () => {
  let output = ""
  coffees.forEach(
    ({ name, image }) =>
      (output += `
              <div class="card">
                <img class="card--avatar" src=${image} />
                <h1 class="card--title">${name}</h1>
                <a class="card--link" href="#">Taste</a>
              </div>
              `)
  )
  container.innerHTML = output
}

document.addEventListener("DOMContentLoaded", showCoffees)

با استفاده از کد بالا، می‌توانیم اطلاعات آرایه را با استفاده از حلقه، درون فایل HTML نمایش دهیم. و برای اینکه همه کارها انجام شود، منتظر می‌مانیم تا محتوای (DOM (Document Object Model بارگیری را تمام کرده تا متد showCoffees را اجرا کنیم.

ما کار‌های زیادی انجام داده‌ایم، اما اکنون، ما فقط یک وبسایت سنتی داریم. پس بیایید آن را در بخش بعدی با معرفی برخی ویژگی‌های PWA تغییر دهیم.

Web App Manifest

وب Manifest یک فایل JSON ساده است که به مرورگر درباره برنامه وب شما اطلاعات می‌دهد. این تعریف می‌کند که هنگام نصب روی دستگاه تلفن همراه یا دسکتاپ چگونه باید رفتار کند. و برای نمایش اعلان افزودن به صفحه اصلی، وب Manifest لازم است.

اکنون که می‌دانیم یک وب Manifest چیست، بیایید یک فایل جدید به نام manifest.json (شما باید همین نام را برای آن بگذارید) درون پوشه اصلی پروژه بسازید. سپس کد زیر را به آن اضافه کنید.

درون فایل manifest.json

{
  "name": "Dev'Coffee",
  "short_name": "DevCoffee",
  "start_url": "index.html",
  "display": "standalone",
  "background_color": "#fdfdfd",
  "theme_color": "#db4938",
  "orientation": "portrait-primary",
  "icons": [
    {
      "src": "/images/icons/icon-72x72.png",
      "type": "image/png", "sizes": "72x72"
    },
    {
      "src": "/images/icons/icon-96x96.png",
      "type": "image/png", "sizes": "96x96"
    },
    {
      "src": "/images/icons/icon-128x128.png",
      "type": "image/png","sizes": "128x128"
    },
    {
      "src": "/images/icons/icon-144x144.png",
      "type": "image/png", "sizes": "144x144"
    },
    {
      "src": "/images/icons/icon-152x152.png",
      "type": "image/png", "sizes": "152x152"
    },
    {
      "src": "/images/icons/icon-192x192.png",
      "type": "image/png", "sizes": "192x192"
    },
    {
      "src": "/images/icons/icon-384x384.png",
      "type": "image/png", "sizes": "384x384"
    },
    {
      "src": "/images/icons/icon-512x512.png",
      "type": "image/png", "sizes": "512x512"
    }
  ]
}

در پایان، این فقط یک فایل JSON با برخی ویژگی‌های اجباری و اختیاری است.

Name: وقتی که مرورگر splash screen (صفحه شروع) را راه‌اندازی می‌کند، این نامی است که روی صفحه نمایش مشاهده می‌شود. 

short_name: این نامی است که زیر آیکون برنامه بر روی صفحه اصلی موبایل نمایش داده می‌شود.

start_url: این صفحه زمانی که کاربر برنامه را باز می‌کند به کاربر نشان داده می‌شود.

Display: این به مرورگر می‌گوید چگونه برنامه را نمایش دهد. چندین حالت وجود دارد minimal-ui, fullscreen, browser و غیره. در اینجا ما از standalone استفاده می‌کنیم تا هر چیزی که مربوط به مرورگر است را مخفی کند.

background_color: وقتی که مرورگر splash screen (صفحه شروع) را راه‌اندازی می‌کند، با این کد رنگ پس‌زمینه را مشخص می‌کنیم. 

theme_color: زمانی که برنامه را باز می‌کنیم، رنگ پس‌زمینه نوار وضعیت خواهد بود.

Orientation: این به مرورگر می‌گوید هنگام اجرای برنامه در جهتی باشد.

Icons: وقتی که مرورگر splash screen (صفحه شروع) را راه‌اندازی می‌کند، این آیکون بر روی صفحه، نمایش داده خواهد شد. در این قسمت من برای همه اندازه‌ها استفاده کردم. اما شما می‌توانید از یک یا دو سایز استفاده کنید. این به شما بستگی دارد.

حالا که ما یک وب Manifest داریم باید آن را به فایل HTML خود پیوند دهیم.

درون فایلindex.html (تگ head)

<link rel="manifest" href="manifest.json" />
<!-- ios support -->
<link rel="apple-touch-icon" href="images/icons/icon-72x72.png" />
<link rel="apple-touch-icon" href="images/icons/icon-96x96.png" />
<link rel="apple-touch-icon" href="images/icons/icon-128x128.png" />
<link rel="apple-touch-icon" href="images/icons/icon-144x144.png" />
<link rel="apple-touch-icon" href="images/icons/icon-152x152.png" />
<link rel="apple-touch-icon" href="images/icons/icon-192x192.png" />
<link rel="apple-touch-icon" href="images/icons/icon-384x384.png" />
<link rel="apple-touch-icon" href="images/icons/icon-512x512.png" />
<meta name="apple-mobile-web-app-status-bar" content="#db4938" />
<meta name="theme-color" content="#db4938" />

همانطور که مشاهده می‌کنید، ما فایل manifest.json را درون تگ head لینک دادیم.

و همچنین چند لینک دیگر اضافه کردیم تا IOS نیز پشتیبانی کند از آیکون، رنگ نوار وضعیت و theme-color.

حالا دیگر می‌توانیم کارهای نهایی را انجام دهیم و service worker را تعریف کنیم.

Service Worker چیست؟

توجه داشته باشید که PWA فقط در https اجرا می‌شود چون service worker به requestها دسترسی دارد و می‌تواند آن را کنترل کند. بنابراین امنیت لازم است.

service worker یک اسکریپت است که در پس‌زمینه مرورگر در یک رشته جداگانه اجرا می‌شود. این به آن معنی است که در یک مکان متفاوت اجرا می‌شود و کاملا از صفحه وب شما جدا است. به همین دلیل است که نمی‌تواند عنصر DOM شما را دستکاری کند.

با این حال، بسیار قدرتمند است. service worker می‌تواند درخواست‌های شبکه را رهگیری و کنترل کند، حافظه کش را مدیریت کند تا پشتیبانی آفلاین را فعال کرده یا اعلان برای کاربران ارسال کند.

بیاید اولین service worker خود را در پوشه اصلی پروژه با نام serviceWorker.js ایجاد کنیم ( نام آن هر چیزی می‌تواند باشد ). اما باید آن را در پوشه اصلی قرار دهید تا دامنه آن به یک پوشه محدود نشود.

Cache کردن داده‌ها

درون فایل serviceWorker.js

const staticDevCoffee = "dev-coffee-site-v1"
const assets = [
  "/",
  "/index.html",
  "/css/style.css",
  "/js/app.js",
  "/images/coffee1.jpg",
  "/images/coffee2.jpg",
  "/images/coffee3.jpg",
  "/images/coffee4.jpg",
  "/images/coffee5.jpg",
  "/images/coffee6.jpg",
  "/images/coffee7.jpg",
  "/images/coffee8.jpg",
  "/images/coffee9.jpg",
]

self.addEventListener("install", installEvent => {
  installEvent.waitUntil(
    caches.open(staticDevCoffee).then(cache => {
      cache.addAll(assets)
    })
  )
})

این کد ابتدا ممکن است ترسناک به نظر برسد اما فقط جاوااسکریپت ساده است ( پس جای نگرانی نیست ).

ما نام staticDevCoffee را برای حافظه Cache خود انتحاب کردیم که قرار است assets در آن ذخیره شود. و برای انجام این کار، نیاز داریم که یک listener را به self متصل کنیم. Self خود service worker است. این به ما این امکان را می‌دهد که به رویدادهای چرخه حیات گوش دهیم و کاری که می‌خواهیم، انجام دهیم.

service worker دارای چندین چرخه حیات است که یکی از آن‌ها رویداد نصب است. این اتفاق زمانی می‌افتد که یک service worker نصب باشد. و به ازای هر service worker فقط یک بار اجرا می‌شود.

وقتی که رویداد نصب اجرا شد، callback را اجرا می‌کنیم که به ما دسترسی به رویداد object را بدهد.

Cache کردن چیزی در مرورگر می‌تواند کمی زمان ببرد تا به پایان برسد چون غیر‌همزمان است.

پس برای مدیریت کردن آن نیاز است ()waitUntil استفاده کنید، همان طور که ممکن است حدس زده باشید، صبر می‌کند تا کار تمام شود.

پس از آماده کردن API کش، می‌توانیم متد open را اجرا کنیم و با فرستادن نام آن به عنوان آرگومان به (caches.open(staticDevCoffee حافظه Cache خود را ایجاد کنیم.

سپس یک promise را برمی‌‌گرداند، که به ما کمک می‌کند تا assets را در Cache ذخیره کنیم با (cache.addAll(assets.

اکنون ما با موفقیت assets را در مرورگر ذخیره کردیم. دفعه بعد که صفحه را بارگذاری می‌کنیم، اگر آفلاین باشیم، service worker این درخواست را اداره خواهد کرد و اطلاعات را از Cache واکشی می‌کند.

حالا بیایم حافظه Cache خود را واکشی کنیم.

واکشی داده‌ها

درون فایل serviceWorker.js

self.addEventListener("fetch", fetchEvent => {
  fetchEvent.respondWith(
    caches.match(fetchEvent.request).then(res => {
      return res || fetch(fetchEvent.request)
    })
  )
})

در اینجا، ما از رویداد fetch استفاده می‌کنیم تا داده‌های خود را پس بگیریم. Callback به ما امکان دسترسی به fetchEvent را می‌دهد. سپس به respondWith() متصل می‌کنیم تا از پاسخ پیش‌فرض مرورگر جلوگیری شود. در عوض این یک promise را برمی‌گرداند چون عمل واکشی ممکن است مقداری طول بکشد.

و وقتی که cache آماده شد، ما (caches.match(fetchEvent.request را اعمال می‌کنیم. این بررسی می‌کند که آیا چیزی در حافظه cache با fetchEvent.request مطابقت دارد یا نه. به هر حال، fetchEvent.request فقط آرایه‌ای از assetsها است.

بعد از آن، یک promise را برمی‌گرداند. و در نهایت، ما می‌توانیم نتیجه را در صورتی که وجود داشته باشد یا واکشی اولیه انجام نشده باشد، برگردانیم. 

حالا، assets را می‌توانیم به وسیله service worker کش یا واکشی کنیم تا سرعت لود تصاویر را مقداری افزایش دهیم.

و از همه مهم‌تر، این کار امکان استفاده از برنامه به صورت آفلاین را نیز فراهم می‌کند. 

اما service worker که به تنهایی نمی‌تواند این کار را انجام دهد. ما نیاز داریم که آن را با پروژه خود رجیستر کنیم.

رجیستر کردن Service Worker

درون فایل js/app.js

if ("serviceWorker" in navigator) {
  window.addEventListener("load", function() {
    navigator.serviceWorker
      .register("/serviceWorker.js")
      .then(res => console.log("service worker registered"))
      .catch(err => console.log("service worker not registered", err))
  })
}

در اینجا، با چک کردن اینکه آیا service worker توسط مرورگر فعلی پشتیبانی می‌شود، شروع می‌کنیم (چون هنوز توسط همه مرورگرها پشتیبانی نمی‌شود).

سپس، به رویداد بارگیری صفحه (load) گوش می‌دهیم برای رجیستر کردن service worker ما با فرستادن اسم فایل serviceWorker.js به navigator.serviceWorker.register() به عنوان یک پارامتر عمل رجیستر کردن را انجام می‌دهیم.

با انجام این کار، اکنون وبسایت سنتی ما تبدیل به یک اپ PWA شده است.

بررسی نهایی

در طول این مقاله، ما دیدیم که PWA می‌تواند چقدر شگفت‌انگیز باشد. با افزودن یک فایل manifest و service worker، چقدر می‌شود تجربه کاربری را نسبت به یک وبسایت معمولی بهبود بخشید. این به این دلیل است که PWA سریع، امن، قابل‌اعتماد، و مهم‌تر از همه، از حالت آفلاین پشتیبانی می‌کنند.

در حال حاضر فریمورک‌های زیادی برای انجام تنظیمات service worker وجود دارد. اما دانستن نحوه اجرای آن با Vanilla جاوااسکریپت می‌تواند به شما کمک کند که PWA را بهتر درک کنید.

و حتی شما می‌توانید cache کردن assets به صورت پویا و محدود کردن سایز cache، خود را فراتر هم ببرید. 

خیلی ممنون از شما که این مقاله رو مطالعه کردید.

اگر به PWA علاقه‌مند شدید و قصد فرا گرفتن این تکنولوژی را دارید حتما از دوره‌ی آموزشی PWA وبسایت راکت استفاده کنید.

منبع

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
4.67 از 3 رای

/@Amiirbazzii
امیرحسین بَزی
طراح رابط کاربری و برنامه نویس فرانت اند

یک طراح گرافیک علاقمند به React JS

دیدگاه و پرسش

برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

امیرحسین بَزی

طراح رابط کاربری و برنامه نویس فرانت اند