تطبيقات الويب التقدمية

تطبيقات الويب التقدمية مع Vue.js

18 دقيقة الدرس 23 من 30

بناء تطبيقات الويب التقدمية مع Vue.js

توفر Vue.js دعمًا من الدرجة الأولى لتطبيقات الويب التقدمية من خلال إضافة Vue CLI PWA. يغطي هذا الدرس كيفية إنشاء وتكوين ونشر تطبيقات PWA جاهزة للإنتاج مع Vue.

إنشاء مشروع Vue PWA

تجعل Vue CLI من السهل للغاية إنشاء مشروع PWA جديد مع جميع التكوينات اللازمة.

# تثبيت Vue CLI عالميًا (إذا لم يكن مثبتًا) npm install -g @vue/cli # إنشاء مشروع جديد مع دعم PWA vue create my-vue-pwa # اختر "Manually select features" # اختر: Babel, PWA, Router, Vuex (اختياري) # أو إضافة PWA لمشروع موجود vue add pwa # هيكل المشروع: src/ main.js App.vue registerServiceWorker.js public/ index.html manifest.json img/icons/ vue.config.js

فهم @vue/cli-plugin-pwa

تم بناء إضافة Vue CLI PWA فوق Workbox وتوفر إنشاء Service Worker تلقائيًا مع إعدادات افتراضية معقولة.

// package.json - التبعيات المضافة بواسطة الإضافة { "dependencies": { "register-service-worker": "^1.7.2" }, "devDependencies": { "@vue/cli-plugin-pwa": "~5.0.0" } }
نصيحة احترافية: تتعامل إضافة Vue PWA تلقائيًا مع تسجيل Service Worker والتخزين المسبق والتخزين المؤقت في وقت التشغيل بأقل قدر من التكوين المطلوب.

تسجيل Service Worker

تنشئ Vue CLI ملف registerServiceWorker.js الذي يتعامل مع دورة حياة Service Worker.

// src/registerServiceWorker.js import { register } from 'register-service-worker'; if (process.env.NODE_ENV === 'production') { register(`${process.env.BASE_URL}service-worker.js`, { ready() { console.log( 'يتم تقديم التطبيق من ذاكرة التخزين المؤقت بواسطة Service Worker.\n' + 'لمزيد من التفاصيل، قم بزيارة https://goo.gl/AFskqB' ); }, registered() { console.log('تم تسجيل Service Worker.'); }, cached() { console.log('تم تخزين المحتوى للاستخدام في وضع عدم الاتصال.'); }, updatefound() { console.log('يتم تنزيل محتوى جديد.'); }, updated(registration) { console.log('محتوى جديد متاح؛ يرجى إعادة التحميل.'); // إرسال حدث مخصص لإشعار التحديث document.dispatchEvent( new CustomEvent('swUpdated', { detail: registration }) ); }, offline() { console.log('لم يتم العثور على اتصال بالإنترنت. التطبيق يعمل في وضع عدم الاتصال.'); }, error(error) { console.error('خطأ أثناء تسجيل Service Worker:', error); } }); }

تكوين PWA في vue.config.js

خصص سلوك PWA الخاص بك من خلال ملف تكوين vue.config.js.

// vue.config.js module.exports = { pwa: { name: 'تطبيق Vue PWA الخاص بي', themeColor: '#4DBA87', msTileColor: '#000000', appleMobileWebAppCapable: 'yes', appleMobileWebAppStatusBarStyle: 'black', // خيارات إضافة Workbox workboxPluginMode: 'GenerateSW', // أو 'InjectManifest' workboxOptions: { // قواعد التخزين المؤقت في وقت التشغيل runtimeCaching: [ { urlPattern: new RegExp('^https://api\\.example\\.com/'), handler: 'NetworkFirst', options: { networkTimeoutSeconds: 10, cacheName: 'api-cache', expiration: { maxEntries: 50, maxAgeSeconds: 5 * 60 // 5 دقائق }, cacheableResponse: { statuses: [0, 200] } } }, { urlPattern: new RegExp('\\.(?:png|jpg|jpeg|svg|gif)$'), handler: 'CacheFirst', options: { cacheName: 'image-cache', expiration: { maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60 // 30 يومًا } } }, { urlPattern: new RegExp('^https://fonts\\.googleapis\\.com/'), handler: 'StaleWhileRevalidate', options: { cacheName: 'google-fonts-stylesheets' } }, { urlPattern: new RegExp('^https://fonts\\.gstatic\\.com/'), handler: 'CacheFirst', options: { cacheName: 'google-fonts-webfonts', expiration: { maxEntries: 30, maxAgeSeconds: 60 * 60 * 24 * 365 // سنة واحدة }, cacheableResponse: { statuses: [0, 200] } } } ], // تخطي الانتظار لتفعيل Service Worker الجديد فورًا skipWaiting: true, clientsClaim: true, // استبعاد ملفات معينة من التخزين المسبق exclude: [/\.map$/, /_redirects/], // الحد الأقصى لحجم الملف للتخزين المسبق (2MB) maximumFileSizeToCacheInBytes: 2 * 1024 * 1024 }, // خيارات Manifest manifestOptions: { name: 'تطبيق الويب التقدمي بـ Vue', short_name: 'Vue PWA', description: 'تطبيق PWA قوي مبني بـ Vue.js', start_url: '.', display: 'standalone', background_color: '#ffffff', theme_color: '#4DBA87', icons: [ { src: './img/icons/android-chrome-192x192.png', sizes: '192x192', type: 'image/png' }, { src: './img/icons/android-chrome-512x512.png', sizes: '512x512', type: 'image/png' }, { src: './img/icons/android-chrome-maskable-192x192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' }, { src: './img/icons/android-chrome-maskable-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' } ] }, // مسارات الأيقونات iconPaths: { faviconSVG: 'img/icons/favicon.svg', favicon32: 'img/icons/favicon-32x32.png', favicon16: 'img/icons/favicon-16x16.png', appleTouchIcon: 'img/icons/apple-touch-icon-152x152.png', maskIcon: 'img/icons/safari-pinned-tab.svg', msTileImage: 'img/icons/msapplication-icon-144x144.png' } } };
مهم: Service Workers تعمل فقط في بيئة الإنتاج. استخدم npm run build وقدم مجلد dist لاختبار ميزات PWA: npx serve -s dist

مكون إشعار التحديث

أنشئ مكونًا لإخطار المستخدمين عند توفر إصدار جديد.

<!-- src/components/UpdateNotification.vue --> <template> <div v-if="updateExists" class="update-notification"> <p>إصدار جديد متاح!</p> <button @click="refreshApp">التحديث الآن</button> </div> </template> <script> export default { name: 'UpdateNotification', data() { return { refreshing: false, registration: null, updateExists: false }; }, created() { document.addEventListener('swUpdated', this.updateAvailable, { once: true }); navigator.serviceWorker?.addEventListener('controllerchange', () => { if (this.refreshing) return; this.refreshing = true; window.location.reload(); }); }, methods: { updateAvailable(event) { this.registration = event.detail; this.updateExists = true; }, refreshApp() { this.updateExists = false; if (!this.registration || !this.registration.waiting) return; this.registration.waiting.postMessage({ type: 'SKIP_WAITING' }); } } }; </script> <style scoped> .update-notification { position: fixed; bottom: 20px; right: 20px; background: #4DBA87; color: white; padding: 15px 20px; border-radius: 8px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); z-index: 1000; } .update-notification button { margin-left: 10px; padding: 8px 16px; background: white; color: #4DBA87; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; } </style>

Composable لكشف وضع عدم الاتصال (Vue 3)

أنشئ دالة composable لاكتشاف حالة الاتصال/عدم الاتصال في Vue 3.

// src/composables/useOnlineStatus.js import { ref, onMounted, onUnmounted } from 'vue'; export function useOnlineStatus() { const isOnline = ref(navigator.onLine); const updateOnlineStatus = () => { isOnline.value = navigator.onLine; }; onMounted(() => { window.addEventListener('online', updateOnlineStatus); window.addEventListener('offline', updateOnlineStatus); }); onUnmounted(() => { window.removeEventListener('online', updateOnlineStatus); window.removeEventListener('offline', updateOnlineStatus); }); return { isOnline }; } // الاستخدام في المكون <template> <div> <div v-if="!isOnline" class="offline-banner"> أنت حاليًا غير متصل بالإنترنت </div> <!-- محتوى المكون --> </div> </template> <script setup> import { useOnlineStatus } from '@/composables/useOnlineStatus'; const { isOnline } = useOnlineStatus(); </script>

استخدام وضع InjectManifest

لحالات الاستخدام المتقدمة، يمكنك استخدام وضع InjectManifest للحصول على تحكم كامل في Service Worker.

// vue.config.js module.exports = { pwa: { workboxPluginMode: 'InjectManifest', workboxOptions: { swSrc: 'src/service-worker.js', swDest: 'service-worker.js' } } }; // src/service-worker.js import { precacheAndRoute } from 'workbox-precaching'; import { registerRoute } from 'workbox-routing'; import { CacheFirst, NetworkFirst } from 'workbox-strategies'; // التخزين المسبق للملفات precacheAndRoute(self.__WB_MANIFEST); // استراتيجيات التخزين المؤقت المخصصة registerRoute( ({ url }) => url.pathname.startsWith('/api/'), new NetworkFirst({ cacheName: 'api-cache', networkTimeoutSeconds: 10 }) ); registerRoute( ({ request }) => request.destination === 'image', new CacheFirst({ cacheName: 'images-cache' }) ); // الاستماع لرسالة تخطي الانتظار self.addEventListener('message', (event) => { if (event.data && event.data.type === 'SKIP_WAITING') { self.skipWaiting(); } });
تمرين:
  1. أنشئ مشروع Vue جديد مع دعم PWA
  2. خصص vue.config.js باستراتيجيات تخزين مخصصة
  3. أنشئ مكون UpdateNotification
  4. نفذ useOnlineStatus composable
  5. أضف أيقونات مخصصة إلى مجلد public/img/icons
  6. قم ببناء المشروع واختبر وظيفة عدم الاتصال
  7. اختبر إشعار التحديث من خلال إجراء تغييرات وإعادة البناء
  8. افحص Service Worker والتخزين المؤقت في Chrome DevTools
أفضل ممارسة: استخدم workboxPluginMode: 'GenerateSW' لمعظم حالات الاستخدام لأنه يوفر تحسينًا تلقائيًا. استخدم InjectManifest فقط عندما تحتاج إلى منطق مخصص متقدم في Service Worker الخاص بك.