كيفية بناء ونشر معرض أعمال باستخدام Vue.js وAxios وواجهة GitHub REST API وNetlify

دقائق القراءة: 9
غلاف يوضح بناء معرض أعمال باستخدام Vue.js وAxios وواجهة GitHub REST API مع النشر على Netlify

مقدمة: لماذا هذا المشروع مهم للمطورين؟

إذا كنت تبحث عن مشروع عملي يجمع بين الواجهة الأمامية، استهلاك الواجهات البرمجية، والتنزيل السحابي، فهذا الدليل يمنحك مسارًا تطبيقيًا متكاملًا. سنعتمد على Vue.js لبناء الواجهة، وAxios لجلب البيانات، وواجهة GitHub REST API لعرض المشاريع الحقيقية، ثم نختم بنشر التطبيق على Netlify.

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

ما الذي ستتعلمه في هذا الدليل؟

  • تثبيت Vue.js والبدء باستخدامه بطريقة بسيطة.
  • فهم بنية التطبيق في Vue مثل الكائن data، والدوال methods، والخصائص المحسوبة computed.
  • التعامل مع التوجيه عبر Vue Router لبناء تطبيق صفحة واحدة SPA.
  • إجراء طلبات HTTP بواسطة Axios.
  • سحب بيانات المشاريع من GitHub REST API.
  • نشر المشروع وربط التحديثات المستمرة عبر Netlify.

المتطلبات الأساسية قبل البدء

لا تحتاج إلى خبرة متقدمة في Vue.js، لكن يُفضّل أن تمتلك معرفة أولية بـ HTML وCSS وJavaScript. هذه الأساسيات ستساعدك على فهم تركيب القوالب، ربط البيانات، وكيفية التعامل مع الأحداث داخل التطبيق.

تثبيت Vue.js بأبسط طريقة

يمكنك استخدام Vue.js داخل المشروع بطريقتين أساسيتين:

  • من خلال مدير الحزم NPM.
  • أو عبر رابط CDN مباشر.

في المشاريع التعليمية والسريعة، يكون استخدام CDN أكثر سهولة لأنه يختصر خطوات الإعداد.

إضافة Vue عبر CDN

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>

أما في بيئة الإنتاج، فيُفضَّل الاعتماد على نسخة مضغوطة لتقليل الحجم وتحسين الأداء.

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>

تثبيت Vue عبر NPM

npm install vue

إذا كنت تبني تطبيقًا أكبر أو تخطط لتقسيم المشروع إلى مكونات متعددة وملفات مستقلة، فخيار NPM سيكون أنسب على المدى البعيد.

إنشاء أول تطبيق باستخدام Vue

بعد ربط المكتبة، يمكنك إنشاء نسخة تطبيق جديدة عبر الدالة new Vue(). عادةً ما تُخزَّن هذه النسخة في متغير مثل app.

let app = new Vue({
  el: '#app',
  data: {}
})

الخاصية el تحدد العنصر الجذري الذي سيتحكم فيه Vue. لذلك عليك أولًا وضع عنصر في الصفحة مثل:

<div id="app"></div>

إضافة البيانات داخل data

الميزة الأقوى في Vue هي الربط التفاعلي للبيانات. أي تغيير على القيم داخل data ينعكس مباشرة على الواجهة.

let app = new Vue({
  el: '#app',
  data: {
    title: 'John Doe Portfolio',
    projects: [
      { title: 'portfolio', languages: ['HTML', 'CSS', 'VueJS'] },
      { title: 'blog', languages: ['HTML', 'CSS', 'PHP'] }
    ]
  }
})

بهذه البنية يمكنك الوصول إلى القيم داخل التطبيق عبر app.title أو app.projects.

العمل مع القوالب في Vue

يعتمد Vue على صيغة الشوارب {{ }} لعرض البيانات داخل الصفحة.

<div id="app">
  <h1>{{ title }}</h1>
</div>

عند تنفيذ هذا المثال ستظهر قيمة الخاصية title داخل وسم <h1>.

عرض HTML خام باستخدام v-html

إذا احتجت إلى عرض محتوى يحتوي على وسوم HTML داخل النص، فيمكنك استخدام التوجيه v-html. لكن انتبه: لا تستخدمه مع محتوى يرسله المستخدم مباشرة، لأن ذلك قد يفتح بابًا لثغرات XSS.

<div v-html="titleHTML"></div>

أهم توجيهات Vue التي ستحتاجها

التوجيه v-bind

يُستخدم لربط خصائص العناصر بقيم ديناميكية.

<div :class="dynamicClass" :id="dynamicId">محتوى ديناميكي</div>

التوجيه v-if وv-else

يُستخدم للتحكم في إظهار العناصر أو إخفائها وفق شرط منطقي.

<h2 v-if="age >= 18">مسموح بالمشاهدة</h2>
<p v-else>العمر غير مناسب</p>

التوجيه v-for

يفيد في تكرار العناصر عند عرض القوائم والمصفوفات.

<li v-for="project in projects" :key="project.title">
  {{ project.title }}
</li>

التوجيه v-on

هذا التوجيه مسؤول عن التقاط الأحداث مثل النقر والإرسال والكتابة. وله صيغة مختصرة هي @.

<button @click="likeProject(index)">إعجاب</button>

التعامل مع إدخال المستخدم عبر v-model

التوجيه v-model ينشئ ربطًا ثنائي الاتجاه بين عناصر النماذج والبيانات داخل التطبيق. هذه من أكثر مزايا Vue عملية في بناء النماذج.

<textarea v-model="tweet"></textarea>
<p>{{ tweet }}</p>

كل ما يكتبه المستخدم في الحقل سينعكس مباشرة على قيمة tweet، والعكس صحيح.

بناء مشروع مصغر: صندوق تغريدات بسيط

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

هيكل البيانات

let app = new Vue({
  el: '#app',
  data: {
    tweet: '',
    tweets: [],
    max_length: 200
  },
  methods: {
    submitData() {
      if (this.tweet.length <= this.max_length) {
        this.tweets.unshift(this.tweet)
        this.tweet = ''
      }
    }
  }
})

القالب الخاص بالنموذج

<form v-on:submit.prevent="submitData">
  <textarea v-model="tweet"></textarea>
  <button type="submit">إرسال</button>
</form>
<ul>
  <li v-for="text in tweets">{{ text }}</li>
</ul>

هذا المثال يوضح كيف يجتمع v-model مع v-on وv-for لبناء واجهة تفاعلية بسيطة وفعالة.

الخصائص المحسوبة computed ولماذا هي أفضل أحيانًا من methods

إذا كنت تريد حساب نصوص أو قيم مشتقة من البيانات دون إعادة تنفيذها بلا داعٍ، فإن computed هو الخيار المثالي. فهي تُخزّن النتيجة مؤقتًا وتُعيد حسابها فقط عند تغيّر القيم المعتمدة عليها.

computed: {
  maxCharsText() {
    return `Max: ${this.tweet.length} of ${this.max_length} characters`
  }
}

بدلًا من كتابة منطق طويل داخل القالب، يمكنك ببساطة استخدام {{ maxCharsText }}.

الانتقال إلى المشروع الأساسي: معرض أعمال احترافي

بعد فهم الأساسيات، ننتقل الآن إلى بناء مشروع أكثر فائدة: صفحة معرض أعمال تعرض مشاريعك الفعلية من GitHub، مع إمكانية التنقل بين الصفحات داخل تطبيق واحد.

المكوّنات التي سنعتمد عليها

  • Vue.js لبناء الواجهة.
  • Axios لإجراء طلبات HTTP.
  • Vue Router لإدارة التنقل بين الصفحات.
  • GitHub REST API لجلب المستودعات.
  • Netlify للنشر والاستضافة.

ما هي GitHub REST API ولماذا نستخدمها؟

واجهة GitHub REST API تتيح لك جلب معلومات عامة عن المستودعات، المالك، الروابط، تواريخ التحديث، اللغات البرمجية، وغيرها. بدلًا من إدخال المشاريع يدويًا داخل الموقع، يمكن سحبها مباشرة من حسابك.

أحد أشهر المسارات المتاحة هو:

/users/{username}/repos

هذا المسار يعيد قائمة بالمستودعات العامة الخاصة بمستخدم محدد.

إضافة Axios وإجراء أول طلب

تثبيت Axios عبر CDN

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

جلب المشاريع من GitHub

let app = new Vue({
  el: '#app',
  data: {
    projects: [],
    perPage: 20,
    page: 1
  },
  mounted() {
    axios.get(`https://api.github.com/users/fabiopacifici/repos?per_page=${this.perPage}&page=${this.page}`)
      .then(response => {
        this.projects = response.data
      })
      .catch(error => {
        console.log(error)
      })
  }
})

هنا نستخدم الخطاف mounted كي يتم تنفيذ الطلب بعد تركيب الواجهة على الصفحة. وبمجرد وصول الاستجابة، تُخزَّن البيانات في المصفوفة projects.

عرض النتائج داخل الصفحة

<div v-for="project in projects" :key="project.id">
  <h2>{{ project.full_name }}</h2>
  <img width="50" :src="project.owner.avatar_url" alt="صورة صاحب المستودع">
  <a :href="project.html_url" target="_blank" rel="noopener noreferrer">عرض المشروع</a>
</div>

بهذه الطريقة تحصل على قائمة حية من مشاريعك دون الحاجة إلى تحديث الصفحة يدويًا كلما أضفت مستودعًا جديدًا.

استخدام Vue Router لبناء تطبيق صفحة واحدة

من غير العملي أن يكون معرض الأعمال صفحة وحيدة فقط. لذلك سنستخدم Vue Router لتقسيم التطبيق إلى واجهتين رئيسيتين: صفحة رئيسية، وصفحة للمشاريع.

تضمين المكتبة

<script src="https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"></script>

إنشاء الروابط والمكان المخصص للعرض

<header>
  <nav>
    <router-link to="/">Home</router-link>
    <router-link to="/projects">Projects</router-link>
  </nav>
</header>
<router-view></router-view>

تعريف المكونات والمسارات

const Home = { template: '<div>My Portfolio</div>' }
const Projects = { template: '<div>Projects</div>' }

const routes = [
  { path: '/', component: Home },
  { path: '/projects', component: Projects }
]

const router = new VueRouter({ routes })

new Vue({ router }).$mount('#app')

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

بناء صفحة المشاريع بشكل عملي

المرحلة التالية هي نقل منطق جلب البيانات وعرضها إلى مكوّن صفحة المشاريع نفسها. وهنا يصبح لكل صفحة بياناتها ودوالها الخاصة.

const Projects = {
  template: `
    <div>
      <div v-for="project in projects" :key="project.id">
        <h2>{{ project.full_name }}</h2>
        <img width="50" :src="project.owner.avatar_url" alt="avatar">
        <a :href="project.html_url" target="_blank">View</a>
      </div>
    </div>
  `,
  data() {
    return {
      projects: [],
      perPage: 20,
      page: 1
    }
  },
  mounted() {
    axios.get(`https://api.github.com/users/fabiopacifici/repos?per_page=${this.perPage}&page=${this.page}`)
      .then(response => {
        this.projects = response.data
      })
      .catch(error => {
        console.log(error)
      })
  }
}

تحسين تجربة المستخدم داخل معرض الأعمال

لجعل الصفحة أكثر إقناعًا وملاءمة للمستخدم، يمكن إضافة مجموعة من التحسينات العملية بدل عرض البيانات الخام فقط.

1) عرض حالة التحميل

من الأفضل إعلام الزائر بأن البيانات قيد الجلب، خاصة إذا كان الاتصال بطيئًا أو كانت الواجهة تعتمد على مصدر خارجي.

2) عرض رسالة خطأ واضحة

إذا فشل الطلب لأي سبب، مثل تجاوز حدود الطلبات أو انقطاع الاتصال، يجب إظهار رسالة مناسبة بدل ترك الصفحة فارغة.

3) الاقتصار على عدد محدد من المشاريع ثم إضافة زر Load More

هذا الأسلوب يحسن قابلية التصفح ويقلل تشتيت المستخدم.

4) استخراج اللغات البرمجية من المشاريع وعرضها كمهارات

بدل كتابة المهارات يدويًا، يمكن استنباطها مباشرة من الحقول language في المستودعات.

مثال عملي أكثر تطورًا على صفحة المشاريع

const Projects = {
  template: `
    <div>
      <main class="container">
        <div class="error" v-if="errors">تعذر جلب البيانات حاليًا.</div>
        <section v-else>
          <div v-if="loading">جاري التحميل...</div>
          <div v-else>
            <div v-for="project in projectsList" :key="project.id">
              <h3>{{ trimedTitle(project.name) }}</h3>
              <p>{{ trimedText(project.description) }}</p>
              <small>{{ new Date(project.updated_at).toDateString() }}</small>
              <a :href="project.html_url" target="_blank" rel="noopener noreferrer">Code</a>
            </div>

            <button v-if="projectsList.length < projects.length" @click="loadMore()">Load More</button>

            <ul>
              <li v-for="skill in skills" :key="skill">{{ skill }}</li>
            </ul>
          </div>
        </section>
      </main>
    </div>
  `,
  data() {
    return {
      projects: [],
      projectsList: null,
      skills: [],
      projectsCount: 5,
      perPage: 20,
      page: 1,
      loading: true,
      errors: false
    }
  },
  methods: {
    trimedTitle(text) {
      let title = text.replaceAll('-', ' ').replace('_', ' ')
      if (title.length > 15) {
        return title.slice(0, 12) + ' ...'
      }
      return title
    },
    trimedText(text) {
      if (text === null) {
        return 'هذا المشروع لا يحتوي على وصف حتى الآن.'
      } else if (text.length > 100) {
        return text.slice(0, 100) + ' ...'
      }
      return text
    },
    getProjects() {
      this.projectsList = this.projects.slice(0, this.projectsCount)
      return this.projectsList
    },
    fetchData() {
      axios.get(`https://api.github.com/users/fbhood/repos?per_page=${this.perPage}&page=${this.page}`)
        .then(response => {
          this.projects = response.data
          this.projects.forEach(project => {
            if (project.language !== null && !this.skills.includes(project.language)) {
              this.skills.push(project.language)
            }
          })
        })
        .catch(error => {
          console.log(error)
          this.errors = true
        })
        .finally(() => {
          this.loading = false
          this.getProjects()
        })
    },
    loadMore() {
      if (this.projectsList.length <= this.projects.length) {
        this.projectsCount += 5
        this.projectsList = this.projects.slice(0, this.projectsCount)
      }
    }
  },
  mounted() {
    setTimeout(this.fetchData, 3000)
  }
}

هيكل المشروع المقترح

للحفاظ على تنظيم جيد، من الأفضل تقسيم الملفات كما يلي:

|-- index.html
|-- assets/
|   |-- css/
|   |   |-- style.css
|   |-- js/
|   |   |-- main.js
|   |-- img/

هذا الهيكل مناسب للمشاريع الصغيرة والمتوسطة، ويسهل رفعه إلى خدمات النشر الثابتة مثل Netlify.

نشر المشروع على Netlify

بعد اكتمال التطوير، تأتي مرحلة النشر. وهنا تظهر قوة Netlify، لأنه يوفّر استضافة سهلة وسريعة للمشاريع الأمامية الثابتة، مع دعم ممتاز للتكامل المستمر.

خطوات النشر الأساسية

  1. إنشاء مجلد المشروع النهائي.
  2. تهيئة مستودع Git.
  3. رفع المشروع إلى GitHub أو BitBucket.
  4. ربط المستودع بحسابك في Netlify.
  5. اختيار الفرع المناسب وبدء عملية النشر.

تهيئة المشروع محليًا باستخدام Git

cd vue-folio
git init
git add .
git commit -m "Initial Commit"

بعد ذلك يمكنك دفع المشروع إلى مستودعك البعيد، ثم ربطه في لوحة Netlify. وعند كل تحديث جديد يتم دفعه إلى الفرع المرتبط، سيُعاد نشر الموقع تلقائيًا.

لماذا هذا الأسلوب مناسب لمحركات البحث وAdSense؟

من المهم أن يكون المحتوى التقني مفيدًا وواضحًا ومبنيًا على تجربة فعلية، لا مجرد إعادة تدوير للمعلومات. المشروع الذي تناولناه هنا يحقق عدة عناصر مهمة:

  • يعرض تطبيقًا عمليًا يمكن للقارئ تنفيذه والاستفادة منه.
  • يجمع بين مفاهيم تعليمية أساسية ومتقدمة في الوقت نفسه.
  • يحل مشكلة حقيقية: تحديث معرض الأعمال تلقائيًا من GitHub.
  • يعتمد على شرح تدريجي يسهل متابعته.
  • يقدم قيمة تعليمية مستمرة، لا مجرد وصف سطحي للأدوات.

أفضل تحسينات يمكنك إضافتها لاحقًا

  • إضافة صفحات تفصيلية لكل مشروع باستخدام المسارات الديناميكية في Vue Router.
  • دعم التصفية حسب اللغة البرمجية مثل JavaScript أو Python.
  • إظهار عدد النجوم stars أو التفرعات forks من GitHub API.
  • تحسين الأداء باستخدام التخزين المؤقت أو التوليد المسبق.
  • الترقية إلى Vue 3 وهيكلة المشروع بمفهوم المكونات الحديثة.

الخلاصة التقنية

بناء معرض أعمال باستخدام Vue.js وAxios وواجهة GitHub REST API ثم نشره على Netlify ليس مجرد تمرين تعليمي، بل مشروع عملي يمكن تحويله إلى واجهة مهنية حقيقية تعرض خبراتك ومشاريعك بشكل متجدد. تقنيًا، هذا الأسلوب ذكي لأنه يفصل بين المحتوى ومصدره، ويجعل إدارة المشاريع أسهل بكثير. وإذا أضفت تحسينات تتعلق بالأداء، وتجربة المستخدم، والتنقل، فستحصل على موقع احترافي قابل للتوسع ويعكس مستواك البرمجي بصورة قوية.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *