PWAها (Progressive Web Application) دسترسی به وب اپلیکیشنها را همراه با ویژگیها و تجربه کاربری برنامههای موبایل native ارائه میدهند. با استفاده از Next.js، میتوانیم انواع مختلف وب اپلیکیشن PWA بسازیم که تجربهای یکپارچه و شبیه به برنامه را در همه پلتفرمها، بدون نیاز به چندین پایگاه کد و یا تاییدیه app store ارائه میدهد.
ساخت یک وب اپلیکیشن PWA با استفاده Next.js
۱ – ایجاد مانیفست وب اپلیکیشن
Next.js پشتیبانی داخلی برای ایجاد یک مانیفست وب اپلیکیشن با استفاده از App Router ارائه میدهد. میتوانیم یک فایل مانیفست استاتیک یا داینامیک ایجاد کنیم:
به عنوان مثال، یک فایل
app/manifest.ts
app/manifest.ts
یا
app/manifest.json
app/manifest.json
میسازیم:
import type { MetadataRoute } from 'next'
export default function manifest(): MetadataRoute.Manifest {
description: 'A Progressive Web App built with Next.js',
background_color: '#ffffff',
src: '/icon-192x192.png',
src: '/icon-512x512.png',
// app/manifest.ts
import type { MetadataRoute } from 'next'
export default function manifest(): MetadataRoute.Manifest {
return {
name: 'Next.js PWA',
short_name: 'NextPWA',
description: 'A Progressive Web App built with Next.js',
start_url: '/',
display: 'standalone',
background_color: '#ffffff',
theme_color: '#000000',
icons: [
{
src: '/icon-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: '/icon-512x512.png',
sizes: '512x512',
type: 'image/png',
},
],
}
}
// app/manifest.ts
import type { MetadataRoute } from 'next'
export default function manifest(): MetadataRoute.Manifest {
return {
name: 'Next.js PWA',
short_name: 'NextPWA',
description: 'A Progressive Web App built with Next.js',
start_url: '/',
display: 'standalone',
background_color: '#ffffff',
theme_color: '#000000',
icons: [
{
src: '/icon-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: '/icon-512x512.png',
sizes: '512x512',
type: 'image/png',
},
],
}
}
این فایل باید حاوی اطلاعاتی در مورد نام، آیکونها و نحوه نمایش آن به عنوان آیکون در دستگاه کاربر باشد. این فایل شرایطی را برا کاربران فراهم میکند تا بتوانند PWA ما را روی صفحه اصلی خود نصب کنند و تجربهای شبیه به کار کردن با اپلیکیشن native را داشته باشند.
میتوانیم از ابزارهایی مانند favicon generatorها برای ایجاد مجموعه آیکونهای مختلف و قرار دادن فایلهای تولید شده در فولدر
public/
public/
استفاده کنیم.
۲ – پیادهسازی Web Push Notificationها
تمام مرورگرهای مدرن از Web Push Notificationها پشتیبانی میکنند، از جمله:
- iOS 16.4+ برای برنامههای نصب شده در home screen
- سافاری ۱۶ برای macOS 13 یا جدیدتر
- مرورگرهای مبتنی بر کروم
- فایرفاکس
این باعث میشود PWAها جایگزین مناسبی برای اپلیکیشنهای native باشند. قابل ذکر است ما میتوانیم بدون نیاز به پشتیبانی آفلاین، دستورهای نصب را راهاندازی کنیم.
Web Push Notificationها به ما این امکان را میدهند تا حتی زمانی که کاربران به طور فعال از برنامه ما استفاده نمیکنند، با آن درگیر شوند. در ادامه نحوه پیادهسازی آنها در برنامه Next.js را داریم:
ابتدا، کامپوننت main page را در
app/page.tsx
app/page.tsx
میسازیم. برای درک بهتر، آن را به بخشهای کوچکتر تقسیم میکنیم. ابتدا، برخی از importها و ابزارهای مورد نیاز خود را اضافه میکنیم. اگر Server Actionهای رفرنس شده در برنامه نداریم، فعلا ایرادی ندارد:
import { useState, useEffect } from 'react'
import { subscribeUser, unsubscribeUser, sendNotification } from './actions'
function urlBase64ToUint8Array(base64String: string) {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
const base64 = (base64String + padding)
const rawData = window.atob(base64)
const outputArray = new Uint8Array(rawData.length)
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i)
'use client'
import { useState, useEffect } from 'react'
import { subscribeUser, unsubscribeUser, sendNotification } from './actions'
function urlBase64ToUint8Array(base64String: string) {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
const base64 = (base64String + padding)
.replace(/\\-/g, '+')
.replace(/_/g, '/')
const rawData = window.atob(base64)
const outputArray = new Uint8Array(rawData.length)
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i)
}
return outputArray
}
'use client'
import { useState, useEffect } from 'react'
import { subscribeUser, unsubscribeUser, sendNotification } from './actions'
function urlBase64ToUint8Array(base64String: string) {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
const base64 = (base64String + padding)
.replace(/\\-/g, '+')
.replace(/_/g, '/')
const rawData = window.atob(base64)
const outputArray = new Uint8Array(rawData.length)
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i)
}
return outputArray
}
اکنون یک کامپوننت برای مدیریت subscribing، unsubscribing و ارسال push notificationها اضافه میکنیم.
function PushNotificationManager() {
const [isSupported, setIsSupported] = useState(false)
const [subscription, setSubscription] = useState<PushSubscription | null>(
const [message, setMessage] = useState('')
if ('serviceWorker' in navigator && 'PushManager' in window) {
async function registerServiceWorker() {
const registration = await navigator.serviceWorker.register('/sw.js', {
const sub = await registration.pushManager.getSubscription()
async function subscribeToPush() {
const registration = await navigator.serviceWorker.ready
const sub = await registration.pushManager.subscribe({
applicationServerKey: urlBase64ToUint8Array(
process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!
async function unsubscribeFromPush() {
await subscription?.unsubscribe()
async function sendTestNotification() {
await sendNotification(message)
return <p>Push notifications are not supported in this browser.</p>
<h3>Push Notifications</h3>
<p>You are subscribed to push notifications.</p>
<button onClick={unsubscribeFromPush}>Unsubscribe</button>
placeholder="Enter notification message"
onChange={(e) => setMessage(e.target.value)}
<button onClick={sendTestNotification}>Send Test</button>
<p>You are not subscribed to push notifications.</p>
<button onClick={subscribeToPush}>Subscribe</button>
function PushNotificationManager() {
const [isSupported, setIsSupported] = useState(false)
const [subscription, setSubscription] = useState<PushSubscription | null>(
null
)
const [message, setMessage] = useState('')
useEffect(() => {
if ('serviceWorker' in navigator && 'PushManager' in window) {
setIsSupported(true)
registerServiceWorker()
}
}, [])
async function registerServiceWorker() {
const registration = await navigator.serviceWorker.register('/sw.js', {
scope: '/',
updateViaCache: 'none',
})
const sub = await registration.pushManager.getSubscription()
setSubscription(sub)
}
async function subscribeToPush() {
const registration = await navigator.serviceWorker.ready
const sub = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(
process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!
),
})
setSubscription(sub)
await subscribeUser(sub)
}
async function unsubscribeFromPush() {
await subscription?.unsubscribe()
setSubscription(null)
await unsubscribeUser()
}
async function sendTestNotification() {
if (subscription) {
await sendNotification(message)
setMessage('')
}
}
if (!isSupported) {
return <p>Push notifications are not supported in this browser.</p>
}
return (
<div>
<h3>Push Notifications</h3>
{subscription ? (
<>
<p>You are subscribed to push notifications.</p>
<button onClick={unsubscribeFromPush}>Unsubscribe</button>
<input
type="text"
placeholder="Enter notification message"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<button onClick={sendTestNotification}>Send Test</button>
</>
) : (
<>
<p>You are not subscribed to push notifications.</p>
<button onClick={subscribeToPush}>Subscribe</button>
</>
)}
</div>
)
}
function PushNotificationManager() {
const [isSupported, setIsSupported] = useState(false)
const [subscription, setSubscription] = useState<PushSubscription | null>(
null
)
const [message, setMessage] = useState('')
useEffect(() => {
if ('serviceWorker' in navigator && 'PushManager' in window) {
setIsSupported(true)
registerServiceWorker()
}
}, [])
async function registerServiceWorker() {
const registration = await navigator.serviceWorker.register('/sw.js', {
scope: '/',
updateViaCache: 'none',
})
const sub = await registration.pushManager.getSubscription()
setSubscription(sub)
}
async function subscribeToPush() {
const registration = await navigator.serviceWorker.ready
const sub = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(
process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!
),
})
setSubscription(sub)
await subscribeUser(sub)
}
async function unsubscribeFromPush() {
await subscription?.unsubscribe()
setSubscription(null)
await unsubscribeUser()
}
async function sendTestNotification() {
if (subscription) {
await sendNotification(message)
setMessage('')
}
}
if (!isSupported) {
return <p>Push notifications are not supported in this browser.</p>
}
return (
<div>
<h3>Push Notifications</h3>
{subscription ? (
<>
<p>You are subscribed to push notifications.</p>
<button onClick={unsubscribeFromPush}>Unsubscribe</button>
<input
type="text"
placeholder="Enter notification message"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<button onClick={sendTestNotification}>Send Test</button>
</>
) : (
<>
<p>You are not subscribed to push notifications.</p>
<button onClick={subscribeToPush}>Subscribe</button>
</>
)}
</div>
)
}
در نهایت، یک کامپوننت دیگر میسازیم تا پیامی را برای دستگاههای iOS نشان دهد تا به آنها بگوید که این اپلیکیشن PWA را در home screen خود نصب کنند، و این پیام را فقط در صورتی نشان دهیم که برنامه قبلاً نصب نشده باشد.
function InstallPrompt() {
const [isIOS, setIsIOS] = useState(false)
const [isStandalone, setIsStandalone] = useState(false)
/iPad|iPhone|iPod/.test(navigator.userAgent) && !(window as any).MSStream
setIsStandalone(window.matchMedia('(display-mode: standalone)').matches)
return null // Don't show install button if already installed
<button>Add to Home Screen</button>
To install this app on your iOS device, tap the share button
<span role="img" aria-label="share icon">
and then "Add to Home Screen"
<span role="img" aria-label="plus icon">
export default function Page() {
<PushNotificationManager />
function InstallPrompt() {
const [isIOS, setIsIOS] = useState(false)
const [isStandalone, setIsStandalone] = useState(false)
useEffect(() => {
setIsIOS(
/iPad|iPhone|iPod/.test(navigator.userAgent) && !(window as any).MSStream
)
setIsStandalone(window.matchMedia('(display-mode: standalone)').matches)
}, [])
if (isStandalone) {
return null // Don't show install button if already installed
}
return (
<div>
<h3>Install App</h3>
<button>Add to Home Screen</button>
{isIOS && (
<p>
To install this app on your iOS device, tap the share button
<span role="img" aria-label="share icon">
{' '}
⎋{' '}
</span>
and then "Add to Home Screen"
<span role="img" aria-label="plus icon">
{' '}
➕{' '}
</span>.
</p>
)}
</div>
)
}
export default function Page() {
return (
<div>
<PushNotificationManager />
<InstallPrompt />
</div>
)
}
function InstallPrompt() {
const [isIOS, setIsIOS] = useState(false)
const [isStandalone, setIsStandalone] = useState(false)
useEffect(() => {
setIsIOS(
/iPad|iPhone|iPod/.test(navigator.userAgent) && !(window as any).MSStream
)
setIsStandalone(window.matchMedia('(display-mode: standalone)').matches)
}, [])
if (isStandalone) {
return null // Don't show install button if already installed
}
return (
<div>
<h3>Install App</h3>
<button>Add to Home Screen</button>
{isIOS && (
<p>
To install this app on your iOS device, tap the share button
<span role="img" aria-label="share icon">
{' '}
⎋{' '}
</span>
and then "Add to Home Screen"
<span role="img" aria-label="plus icon">
{' '}
➕{' '}
</span>.
</p>
)}
</div>
)
}
export default function Page() {
return (
<div>
<PushNotificationManager />
<InstallPrompt />
</div>
)
}
اکنون Server Actionهایی را که در این فایل فراخوانی میکنیم را میسازیم.
۳ – پیادهسازی Server Actionها
یک فایل جدید میسازیم تا حاوی اکشنهای ما در
app/actions.ts
app/actions.ts
باشد. این فایل ایجاد subscriptions، حذف subscriptionها و ارسال نوتیفیکیشنها را انجام میدهد.
import webpush from 'web-push'
'<mailto:your-email@example.com>',
process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!,
process.env.VAPID_PRIVATE_KEY!
let subscription: PushSubscription | null = null
export async function subscribeUser(sub: PushSubscription) {
// In a production environment, you would want to store the subscription in a database
// For example: await db.subscriptions.create({ data: sub })
export async function unsubscribeUser() {
// In a production environment, you would want to remove the subscription from the database
// For example: await db.subscriptions.delete({ where: { ... } })
export async function sendNotification(message: string) {
throw new Error('No subscription available')
await webpush.sendNotification(
title: 'Test Notification',
console.error('Error sending push notification:', error)
return { success: false, error: 'Failed to send notification' }
// app/actions.ts
'use server'
import webpush from 'web-push'
webpush.setVapidDetails(
'<mailto:your-email@example.com>',
process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!,
process.env.VAPID_PRIVATE_KEY!
)
let subscription: PushSubscription | null = null
export async function subscribeUser(sub: PushSubscription) {
subscription = sub
// In a production environment, you would want to store the subscription in a database
// For example: await db.subscriptions.create({ data: sub })
return { success: true }
}
export async function unsubscribeUser() {
subscription = null
// In a production environment, you would want to remove the subscription from the database
// For example: await db.subscriptions.delete({ where: { ... } })
return { success: true }
}
export async function sendNotification(message: string) {
if (!subscription) {
throw new Error('No subscription available')
}
try {
await webpush.sendNotification(
subscription,
JSON.stringify({
title: 'Test Notification',
body: message,
icon: '/icon.png',
})
)
return { success: true }
} catch (error) {
console.error('Error sending push notification:', error)
return { success: false, error: 'Failed to send notification' }
}
}
// app/actions.ts
'use server'
import webpush from 'web-push'
webpush.setVapidDetails(
'<mailto:your-email@example.com>',
process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!,
process.env.VAPID_PRIVATE_KEY!
)
let subscription: PushSubscription | null = null
export async function subscribeUser(sub: PushSubscription) {
subscription = sub
// In a production environment, you would want to store the subscription in a database
// For example: await db.subscriptions.create({ data: sub })
return { success: true }
}
export async function unsubscribeUser() {
subscription = null
// In a production environment, you would want to remove the subscription from the database
// For example: await db.subscriptions.delete({ where: { ... } })
return { success: true }
}
export async function sendNotification(message: string) {
if (!subscription) {
throw new Error('No subscription available')
}
try {
await webpush.sendNotification(
subscription,
JSON.stringify({
title: 'Test Notification',
body: message,
icon: '/icon.png',
})
)
return { success: true }
} catch (error) {
console.error('Error sending push notification:', error)
return { success: false, error: 'Failed to send notification' }
}
}
ارسال یک نوتیفیکیشن توسط service worker ما به کمک فرآیندی که در مرحله ۵ ایجاد خواهیم کرد، انجام میشود.
در یک محیط production، ما میخواهیم subscription را در یک پایگاه داده برای پایداری در راهاندازی مجدد سرور و مدیریت subscriptionهای چند کاربر ذخیره کنیم.
۴ – ایجاد کلیدهای VAPID
برای استفاده از Web Push API، باید کلیدهای VAPID تولید کنیم.
برای این کار، یک فایل اسکریپت میسازیم. به عنوان مثال،
generate-vapid-keys.js
generate-vapid-keys.js
:
// ./generate-vapid-keys.js
const webpush = require('web-push')
const vapidKeys = webpush.generateVAPIDKeys()
console.log('Paste the following keys in your .env file:')
console.log('-------------------')
console.log('NEXT_PUBLIC_VAPID_PUBLIC_KEY=', vapidKeys.publicKey)
console.log('VAPID_PRIVATE_KEY=', vapidKeys.privateKey)
// ./generate-vapid-keys.js
const webpush = require('web-push')
const vapidKeys = webpush.generateVAPIDKeys()
console.log('Paste the following keys in your .env file:')
console.log('-------------------')
console.log('NEXT_PUBLIC_VAPID_PUBLIC_KEY=', vapidKeys.publicKey)
console.log('VAPID_PRIVATE_KEY=', vapidKeys.privateKey)
// ./generate-vapid-keys.js
const webpush = require('web-push')
const vapidKeys = webpush.generateVAPIDKeys()
console.log('Paste the following keys in your .env file:')
console.log('-------------------')
console.log('NEXT_PUBLIC_VAPID_PUBLIC_KEY=', vapidKeys.publicKey)
console.log('VAPID_PRIVATE_KEY=', vapidKeys.privateKey)
این اسکریپت را با Node.js اجرا میکنیم تا کلیدهای VAPID خود را تولید کنیم:
node generate-vapid-keys.js
// Terminal
node generate-vapid-keys.js
// Terminal
node generate-vapid-keys.js
خروجی را کپی کرده و در فایل
.env
.env
خود قرار میدهیم.
۵ – ایجاد یک Service Worker
یک فایل
public/sw.js
public/sw.js
برای service worker خود میسازیم:
self.addEventListener('push', function (event) {
const data = event.data.json()
icon: data.icon || '/icon.png',
dateOfArrival: Date.now(),
event.waitUntil(self.registration.showNotification(data.title, options))
self.addEventListener('notificationclick', function (event) {
console.log('Notification click received.')
event.notification.close()
event.waitUntil(clients.openWindow('<https://your-website.com>'))
// public/sw.js
self.addEventListener('push', function (event) {
if (event.data) {
const data = event.data.json()
const options = {
body: data.body,
icon: data.icon || '/icon.png',
badge: '/badge.png',
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
primaryKey: '2',
},
}
event.waitUntil(self.registration.showNotification(data.title, options))
}
})
self.addEventListener('notificationclick', function (event) {
console.log('Notification click received.')
event.notification.close()
event.waitUntil(clients.openWindow('<https://your-website.com>'))
})
// public/sw.js
self.addEventListener('push', function (event) {
if (event.data) {
const data = event.data.json()
const options = {
body: data.body,
icon: data.icon || '/icon.png',
badge: '/badge.png',
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
primaryKey: '2',
},
}
event.waitUntil(self.registration.showNotification(data.title, options))
}
})
self.addEventListener('notificationclick', function (event) {
console.log('Notification click received.')
event.notification.close()
event.waitUntil(clients.openWindow('<https://your-website.com>'))
})
این service worker از تصاویر و نوتیفیکیشنهای سفارشی پشتیبانی میکند. همچنین push eventهای ورودی و کلیکهای نوتیفیکیشن را کنترل مینماید.
- میتوانیم با استفاده از ویژگیهای
icon
icon
و badge
badge
، آیکونهای سفارشی را برای نوتیفیکیشنهای خود تنظیم کنیم.
- الگوی
vibrate
vibrate
را نیز میتوانیم برای ایجاد نوتیفیکیشنهای ویبرهای سفارشی در دستگاههایی که از آن پشتیبانی میکنند، تنظیم کنیم.
- همچنین میتوانیم دادههای اضافی را با استفاده از ویژگی
data
data
به نوتیفیکیشن پیوست نماییم.
باید این موضوع را به یاد داشته باشیم که service worker خود را به طور کامل تست کنیم تا مطمئن شویم که در دستگاهها و مرورگرهای مختلف، مطابق انتظاری که داریم رفتار میکند. همینطور باید مطمئن شویم که لینک
'https://your-website.com'
'https://your-website.com'
در event listener
notificationclick
notificationclick
را به URL مناسب برای برنامه خود به روز رسانی نماییم.
۶ – افزودن به Home Screen
کامپوننت
InstallPrompt
InstallPrompt
که در مرحله ۲ تعریف کردیم، پیامی را برای دستگاههای iOS نشان میدهد تا به کاربران آنها بگوید که برنامه را در home screen خود نصب کنند.
برای اطمینان از اینکه برنامه ما میتواند روی home screen موبایل نصب شود، باید موارد زیر را داشته باشد:
- مانیفست معتبر وب اپلیکیشن (که در مرحله ۱ ساختیم)
- ارائه وب سایت از طریق HTTPS
زمانی که این معیارها برآورده شوند مرورگرهای مدرن به طور خودکار یک نوتیفیکیشن نصب را به کاربران نشان میدهند.
۷ – تست به صورت Local
برای اطمینان از اینکه میتوانیم نوتیفیکیشنها را به صورت local مشاهده نماییم، باید اطمینان حاصل کنیم که:
– ما در حال اجرا به صورت local با HTTPS هستیم:
– مرورگر ما (Chrome، Safari، Firefox) نوتیفیکیشنها را فعال کرده است:
- هنگامی که به صورت local از ما درخواست میشود، باید مجوزهای استفاده از نوتیفیکیشنها را قبول کنیم
- باید اطمینان حاصل کنیم که نوتیفیکیشنها به صورت سراسری برای کل مرورگر غیرفعال نیستند
- در نهایت، اگر هنوز نوتیفیکیشنها را نمیبینیم، باید از مرورگر دیگری برای دیباگ کردن استفاده کنیم
۸ – ایمنسازی اپلیکیشن
امنیت یک جنبه حیاتی از هر وب اپلیکیشن، به ویژه برای PWAها است. Next.js به ما اجازه میدهد تا headerهای امنیتی را با استفاده از فایل
next.config.js
next.config.js
پیکربندی کنیم. به عنوان مثال:
key: 'X-Content-Type-Options',
value: 'strict-origin-when-cross-origin',
value: 'application/javascript; charset=utf-8',
value: 'no-cache, no-store, must-revalidate',
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self'",
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
],
},
{
source: '/sw.js',
headers: [
{
key: 'Content-Type',
value: 'application/javascript; charset=utf-8',
},
{
key: 'Cache-Control',
value: 'no-cache, no-store, must-revalidate',
},
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self'",
},
],
},
]
},
}
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
],
},
{
source: '/sw.js',
headers: [
{
key: 'Content-Type',
value: 'application/javascript; charset=utf-8',
},
{
key: 'Cache-Control',
value: 'no-cache, no-store, must-revalidate',
},
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self'",
},
],
},
]
},
}
کاری که هر گزینه انجام میدهد عبارت است از:
- Headerهای سراسری(بر روی همه routeها اعمال میشود):
- Headerهای مخصوص Service Worker:
Content-Type: application/javascript; charset=utf-8
Content-Type: application/javascript; charset=utf-8
: اطمینان حاصل میکند که service worker به درستی به عنوان جاوااسکریپت تفسیر شده است.
Cache-Control: no-cache, no-store, must-revalidate
Cache-Control: no-cache, no-store, must-revalidate
: از کش شدن service worker جلوگیری میکند و اطمینان میدهد که کاربران همیشه آخرین نسخه را دریافت میکنند.
Content-Security-Policy: default-src 'self'; script-src 'self'
Content-Security-Policy: default-src 'self'; script-src 'self'
: یک خطمشی امنیتی سختگیرانه برای service worker اجرا میکند و فقط به اسکریپتهایی از منبع یکسان اجازه اجرا میدهد.
جمعبندی
در این مقاله با مفهوم وب اپلیکیشنهای PWA آشنا شدیم و سعی کردیم تا روش ساخت آنها با استفاده از Next.js را بررسی کنیم.
به طور کلی، وب اپلیکیشنهای PWA به ما اجازه میدهند تا:
- به روز رسانیها را فوراً و بدون انتظار برای تأیید app store اجرا کنیم
- اپلیکیشنهای چند پلتفرمی را با یک پایگاه کد واحد بسازیم
- ویژگیهای native مانند نصب home screen و push notificationها را ارائه دهیم
دیدگاهها: