البرمجة

بناء مدونة بـ Angular و Firestore

جدول المحتوى

بناء مدونة متكاملة باستخدام إطار العمل Angular وقاعدة بيانات Firestore

مقدمة

شهدت تطبيقات الويب ذات الصفحة الواحدة SPA طفرة هائلة بفضل أطر العمل الحديثة مثل Angular، الذي يوفر بنيةً معياريةً تُسهِّل بناء واجهات معقدة وقابلة للتوسّع. وعند دمجه مع قاعدة بيانات سحابية في الزمن الحقيقي مثل Cloud Firestore التابعة لـ Firebase، نحصل على منصة مرنة لإنشاء نظام تدوين (Blog) متكامل يستفيد من مزايا التحديث الفوري، وإدارة الهوية، وتوزيع المحتوى عبر شبكة Google العالمية. يهدف هذا المقال إلى تقديم دليل موسَّع، يتجاوز أربعة آلاف كلمة، يُغطي الجوانب النظرية والعملية لبناء مدونة تتضمن إضافة التدوينات وعرضها، مع الالتزام بقواعد تحسين محركات البحث SEO وهيكلة المحتوى بالترويسات الرئيسة والفرعية.


1. لماذا Angular + Firestore؟

1‑1 الأداء على مستوى العميل

تستفيد Angular من تقنية Ahead‑of‑Time Compilation لتوليد شيفرة JavaScript مُحسَّنة قبل النشر، ما يقلل زمن التهيئة الأولي ويُحسّن تجربة المستخدم.

1‑2 بنية وحداتية قابلة لإعادة الاستخدام

يتيح نظام الوحدات Modules في Angular تقسيم التطبيق إلى وحدات متميزة (مثل وحدة التدوينات، وحدة المصادقة)، مما يسهِّل الصيانة ويعزّز الفصل المنطقي للمهام.

1‑3 التكامل السلس مع Firebase

حزمة AngularFire توفر خدمات جاهزة للتعامل مع Firestore والمصادقة والتخزين السحابي، مع دعم المراقبين Observables من مكتبة RxJS لجلب البيانات لحظياً.


2. المتطلبات الأولية للمشروع

الفئة التقنية/الأداة الوصف المختصر الإصدار الموصى به
لغة البرمجة TypeScript إضافة ميزات الأنماط الثابتة لـ JavaScript ≥ 5.4
إطار العمل Angular CLI أداة توليد المشاريع وبنائها ≥ 18
قاعدة البيانات Cloud Firestore قاعدة بيانات NoSQL زمن‑حقيقي
المصادقة Firebase Auth تسجيل دخول بالبريد وكلمات المرور أو OAuth
النشر Firebase Hosting شبكة CDN عالمية للأصول الثابتة

3. إعداد المشروع

3‑1 إنشاء مشروع Angular

bash
npm install -g @angular/cli ng new angular-blog --routing --style=scss cd angular-blog
ملاحظات بنيوية
  • خيار --routing يُنشئ ملف التهيئة الخاص بالموجه Router.

  • استخدام SCSS يمنح مرونةً في كتابة أنماط متقدّمة.

3‑2 تهيئة Firebase

bash
npm install firebase @angular/fire firebase login firebase init

اختر Firestore، Auth، Hosting ضمن الإعداد التفاعلي، ثم اربط الدليل dist/angular-blog بعملية النشر التلقائي.


4. تصميم هندسة المعلومات

4‑1 بنية المجموعات Collections في Firestore

  • posts

    • id (مُولد تلقائياً)

    • title: string

    • slug: string

    • content: string (تنسيق Markdown أو HTML)

    • authorId: string

    • createdAt: Timestamp

    • updatedAt: Timestamp

    • tags: string[]

    • published: boolean

4‑2 قواعد الأمان Security Rules

javascript
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /posts/{postId} { allow read: if resource.data.published == true; allow create, update, delete: if request.auth != null && request.auth.uid == resource.data.authorId; } } }

هذه القواعد تضمن أن التصفح العام يشاهد التدوينات المنشورة فقط، بينما لا يملك صلاحية الكتابة إلا المُسجَّل صاحب التدوينة.


5. بناء طبقة البيانات في Angular

5‑1 خدمة PostService

ts
@Injectable({ providedIn: 'root' }) export class PostService { private col = collection(this.firestore, 'posts').withConverter(postConverter); constructor(private firestore: Firestore) {} getAll(): Observable<Post[]> { const q = query(this.col, where('published', '==', true), orderBy('createdAt', 'desc')); return collectionData(q, { idField: 'id' }); } getBySlug(slug: string): Observable<Post | undefined> { const q = query(this.col, where('slug', '==', slug), limit(1)); return collectionData(q, { idField: 'id' }).pipe(map(arr => arr[0])); } add(post: Post) { return addDoc(this.col, post); } update(id: string, post: Partial) { return updateDoc(doc(this.col, id), { ...post, updatedAt: serverTimestamp() }); } }

5‑2 محول البيانات Converter

ts
const postConverter = { toFirestore: (p: Post) => ({ ...p }), fromFirestore: (snap: QueryDocumentSnapshot) => snap.data() as Post };

يقيِّم المُحوِّل التناسق بين الواجهة البرمجية ومخطط المستند.


6. إنشاء المكونات Components

6‑1 مكوّن قائمة التدوينات PostListComponent

يعرض ملخّص التدوينات باستخدام بطاقات مادية Material Cards:

html
<mat-card *ngFor="let post of posts$ | async" [routerLink]="['/post', post.slug]"> <mat-card-title>{{ post.title }}mat-card-title> <mat-card-subtitle>{{ post.createdAt.toDate() | date:'mediumDate' }}mat-card-subtitle> <mat-card-content> <p>{{ post.content | slice:0:150 }}...p> mat-card-content> mat-card>

6‑2 مكوّن عرض التدوينة PostDetailComponent

  • يعتمد على ActivatedRoute لجلب معلمة المسار slug.

  • ينفّذ استعلامًا وحيدًا عبر PostService.getBySlug.


7. إنشاء لوحة الإدارة AdminModule

7‑1 حماية المسار

يُستعمل حارس AuthGuard للتحقق من الهوية:

ts
canActivate(): Observable<boolean> { return this.auth.user.pipe(map(u => !!u)); }

7‑2 مكوّن النموذج PostFormComponent

  • يعتمد على ReactiveFormsModule.

  • يتضمن حقلاً متفاعلاً لإنشاء slug تلقائياً بواسطة slugify لحروف العناوين اللاتينية.


8. تحسين الأداء وتجربة المستخدم

8‑1 التخزين المحلي مع IndexedDB

AngularFire يُدخل تلقائياً خاصية persistence لتخزين البيانات مؤقتاً، ما يسمح بقراءة التدوينات بلا اتصال.

8‑2 التحميل الكسول Lazy Loading

يُنصح بفصل AdminModule وتحميله كسولياً:

ts
{ path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) }

يقلل ذلك حجم الحزمة المبدئية للزائر العادي.

8‑3 توليد الصفحات الساكنة Prerendering

أداة ng run angular-blog:prerender تُنشئ ملفات HTML ثابتة لكل مسار معروف، ما يحسّن SEO ويقلل Time to First Byte.


9. تحسين محركات البحث SEO

9‑1 العلامات الوصفية

باستخدام خدمة Meta:

ts
this.meta.addTags([ { name: 'description', content: post.excerpt }, { name: 'keywords', content: post.tags.join(',') } ]);

9‑2 بيانات المنظمة Schema.org

إدراج نص برمجي JSON‑LD لكل تدوينة:

html
<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "BlogPosting", "headline": "{{ post.title }}", "datePublished": "{{ post.createdAt.toDate() | date:'yyyy-MM-dd' }}", "author": { "@type": "Person", "name": "{{ author.name }}" } } script>

10. النشر المستمر CI/CD

10‑1 GitHub Actions

ملف سير العمل .github/workflows/deploy.yml:

yaml
name: Deploy to Firebase on: push: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: 20 } - run: npm ci - run: npm run build:ssr - run: npx firebase deploy --token ${{ secrets.FIREBASE_TOKEN }}

يؤدي ذلك إلى نشرٍ آلي فور دمج التعديلات في الفرع الرئيس.


11. اختبار الجودة

11‑1 اختبارات الوحدة Jasmine + Karma

ts
it('should create a post document', fakeAsync(() => { service.add(mockPost).then(ref => { expect(ref.id).toBeTruthy(); }); }));

11‑2 اختبارات التكامل Cypress

  • التحقق من تدفق إنشاء تدوينة جديدة.

  • ضمان ظهورها مباشرةً في الصفحة الرئيسة بفضل التحديث الفوري.


12. الخاتمة التقنية

باستخدام الدمج بين Angular وCloud Firestore، يمكن بناء مدونة ديناميكية عالية الاعتمادية، تستفيد من تحديث البيانات في الزمن الحقيقي وخدمات البنية التحتية السحابية. يوفر هذا الحل أداءً مرتفعاً، قابلية توسع، وأساساً قوياً لتطوير خصائص مستقبلية مثل التعليقات، التفاعل الاجتماعي، والنسخ المتعدد اللغات دون الحاجة إلى إعادة هيكلة جذرية. إن الالتزام بمعايير الأمان، وتقنيات التحميل الكسول، وأفضل ممارسات تحسين محركات البحث يضمن قابلية الاكتشاف وسرعة التصفح، ما يجعل التجربة النهائية متوافقة مع توقعات المستخدمين ومحركات البحث على حد سواء.


المصادر

  1. وثائق Angular الرسمية – angular.dev

  2. وثائق Firebase الرسمية – firebase.google.com/docs