Skip to content

3. أول تطبيق [nuxt.js]

3.1. إنشاء التطبيق

بالنسبة لتطوير [nuxt.js]، سنستمر في استخدام VS Code. لقد أنشأنا مجلد [dvp] فارغًا حيث سنضع أمثلةنا. ثم نفتح هذا المجلد:

Image

نحفظ مساحة العمل تحت اسم [intro-nuxtjs] [3-5]:

Image

نفتح محطة طرفية [6-7]:

Image

حتى الآن، استخدمنا مدير حزم JavaScript [npm]. وللتغيير، سنستخدم مدير [yarn] هنا. ومثل [npm]، يتم تثبيته مع الإصدارات الحديثة من [node.js]. لإنشاء أول تطبيق [nuxt]، نستخدم الأمر [yarn create nuxt-app <folder>] [1]. سيطلب الأمر بعض المعلومات حول المشروع المراد إنشاؤه، وبمجرد توفير هذه المعلومات، سيقوم بإنشائه [2]:

Image

في [2]، تم إنشاء شجرة ملفات كاملة. يسرد ملف [package.json] مكتبات JavaScript التي تم تنزيلها إلى مجلد [node-modules] [4]:


{
  "name": "nuxt-intro",
  "version": "1.0.0",
  "description": "nuxt-intro",
  "author": "serge-tahe",
  "private": true,
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate",
    "lint": "eslint --ext .js,.vue --ignore-path .gitignore ."
  },
  "dependencies": {
    "nuxt": "^2.0.0",
    "bootstrap-vue": "^2.0.0",
    "bootstrap": "^4.1.3",
    "@nuxtjs/axios": "^5.3.6"
  },
  "devDependencies": {
    "@nuxtjs/eslint-config": "^1.0.1",
    "@nuxtjs/eslint-module": "^1.0.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^6.1.0",
    "eslint-plugin-nuxt": ">=0.4.2",
    "eslint-config-prettier": "^4.1.0",
    "eslint-plugin-prettier": "^3.0.1",
    "prettier": "^1.16.4"
  }
}

يعكس هذا الملف الردود المقدمة إلى الأمر [create nuxt-app] لتعريف المشروع الذي تم إنشاؤه (نوفمبر 2019). قد يكون لدى القارئ ملف [package.json] مختلف:

  • قد يكون قد قدم إجابات مختلفة على الأسئلة؛
  • قد يكون الأمر [create nuxt-app] قد تطور منذ كتابة هذا المستند: قد تكون التبعيات والإصدارات قد تغيرت؛

السطر 8 من البرنامج النصي هو الأمر الذي يقوم بتشغيل التطبيق:

Image

  • في [4]، نرى أن التطبيق متاح على عنوان URL [localhost:3000
  • في [5-6]، نرى أن التطبيق ينشئ خادمًا [6] وعميلًا (لذلك الخادم) [5]؛

دعونا نطلب عنوان URL [http://localhost:3000/] في متصفح:

Image

3.2. وصف بنية الدليل لتطبيق [nuxt]

دعونا نلقي نظرة على بنية الدلائل للتطبيق الذي تم إنشاؤه:

Image

دور المجلدات هو كما يلي:

الأصول
موارد التطبيق غير المُجمَّعة (الصور، إلخ)؛
static
الملفات في هذا المجلد ستكون متاحة في جذر التطبيق. نضع الملفات في هذا المجلد التي يجب أن تكون موجودة في جذر التطبيق، مثل ملف [robots.txt] المخصص لمحركات البحث؛
مكونات
مكونات [عرض] التطبيق المستخدمة في [التخطيطات] و[الصفحات]؛
التخطيطات
مكونات [Vue] الخاصة بالتطبيق المستخدمة لتصميم [الصفحات]؛
الصفحات
مكونات [Vue] التي تعرضها المسارات المختلفة للتطبيق. يمكن تسمية هذه المكونات بعروض التطبيق. تلعب الصفحات دورًا خاصًا في [Nuxt]: يتم إنشاء المسارات ديناميكيًا بناءً على بنية الدليل الموجودة في مجلد [الصفحات]؛
البرمجيات الوسيطة
نصوص برمجية يتم تنفيذها كلما تغير مسار. تتيح لك التحكم في هذه المسارات؛
المكونات الإضافية
اسمها مضلل. يمكن أن تحتوي على مكونات إضافية وكذلك نصوص برمجية قياسية. يتم تنفيذ النصوص البرمجية الموجودة في هذا المجلد عند بدء تشغيل التطبيق؛
المخزن
إذا كان يحتوي على نص برمجي [index.js]، فإن هذا النص يحدد مثيلًا لمخزن [Vuex

إذا كان المجلد فارغًا، فيمكن إزالته من بنية الدليل. في المثال أعلاه، يمكن إزالة المجلدات [assets، static، middleware، plugins، store] [2].

3.3. ملف التكوين [nuxt.config]

يتم التحكم في تشغيل التطبيق بواسطة ملف [nuxt.config.js] التالي:


export default {
  mode: 'universal',
  /*
   ** Headers of the page
   */
  head: {
    title: process.env.npm_package_name || '',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
        hid: 'description',
        name: 'description',
        content: process.env.npm_package_description || ''
      }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
  },
  /*
   ** Customize the progress-bar color
   */
  loading: { color: '#fff' },
  /*
   ** Global CSS
   */
  css: [],
  /*
   ** Plugins to load before mounting the App
   */
  plugins: [],
  /*
   ** Nuxt.js dev-modules
   */
  buildModules: [
    // Doc: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module'
  ],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Doc: https://bootstrap-vue.js.org
    'bootstrap-vue/nuxt',
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios'
  ],
  /*
   ** Axios module configuration
   ** See https://axios.nuxtjs.org/options
   */
  axios: {},
  /*
   ** Build configuration
   */
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  }
}
  • السطر 2: نوع التطبيق الذي تم إنشاؤه:
    • [universal]: تطبيق عميل/خادم. عند تحميل التطبيق لأول مرة، وكذلك مع كل تحديث للصفحة في المتصفح، يُطلب من الخادم تقديم الصفحة؛
    • [sap]: نوع [تطبيق صفحة واحدة]: يقوم الخادم في البداية بتسليم التطبيق بالكامل. بعد ذلك، يعمل العميل بشكل مستقل، حتى عند تحديث الصفحة في المتصفح؛
  • الأسطر 6–18: تحدد رأس HTML `<head>` لمختلف صفحات التطبيق:
    • السطر 7: علامة <title> لعنوان الصفحة؛
    • الأسطر 8-16: علامات <meta
    • السطر 17: علامات <link>

في التطبيق الذي تم إنشاؤه، تكون علامة <head> كما يلي (كود المصدر للصفحة المعروضة في المتصفح):


<title>nuxt-intro</title>
<meta data-n-head="ssr" charset="utf-8">
<meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1">
<meta data-n-head="ssr" data-hid="description" name="description" content="nuxt-intro">
<link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="preload" href="/_nuxt/runtime.js" as="script">
<link rel="preload" href="/_nuxt/commons.app.js" as="script">
<link rel="preload" href="/_nuxt/vendors.app.js" as="script">
<link rel="preload" href="/_nuxt/app.js" as="script">

الآن، دعونا نعدل ملف [nuxt.config] على النحو التالي:


head: {
    title: 'Introduction à [nuxt.js]',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
        hid: 'description',
        name: 'description',
        content: 'ssr routing loading asyncdata middleware plugins store'
      }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
},

عندما نعيد تشغيل التطبيق، تتغير علامة <head> إلى ما يلي (كود المصدر للصفحة المعروضة في المتصفح):


  <head >
    <title>Introduction à [nuxt.js]</title>
    <meta data-n-head="ssr" charset="utf-8">
    <meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1">
    <meta data-n-head="ssr" data-hid="description" name="description" content="ssr routing loading asyncdata middleware plugins store">
    <link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
    <link rel="preload" href="/_nuxt/runtime.js" as="script">
    <link rel="preload" href="/_nuxt/commons.app.js" as="script">
    <link rel="preload" href="/_nuxt/vendors.app.js" as="script">
<link rel="preload" href="/_nuxt/app.js" as="script">

لنعد إلى ملف [nuxt.config]:


export default {
  mode: 'universal',
  /*
   ** Headers of the page
   */
  head: {
    ...
  },
  /*
   ** Customize the progress-bar color
   */
  loading: { color: '#fff' },
  /*
   ** Global CSS
   */
  css: [],
  /*
   ** Plugins to load before mounting the App
   */
  plugins: [],
  /*
   ** Nuxt.js dev-modules
   */
  buildModules: [
    // Doc: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module'
  ],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Doc: https://bootstrap-vue.js.org
    'bootstrap-vue/nuxt',
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios'
  ],
  /*
   ** Axios module configuration
   ** See https://axios.nuxtjs.org/options
   */
  axios: {},
  /*
   ** Build configuration
   */
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  }
}
  • السطر 12: بين كل مسار في عميل [nuxt]، يظهر شريط تحميل إذا استغرق تغيير المسار بعض الوقت. تسمح لك الخاصية [loading] بتكوين شريط التحميل هذا؛ هنا، لون الشريط؛
  • السطر 16: ملفات [css] العامة. سيتم تضمينها تلقائيًا في جميع صفحات التطبيق؛
  • الأسطر 24-27: وحدات JavaScript المطلوبة لبناء التطبيق؛
  • الأسطر 31–36: وحدات JavaScript التي يستخدمها التطبيق؛
  • السطر 41: تكوين مكتبة [axios] عندما يختارها المستخدم للتفاعلات HTTP مع خوادم الجهات الخارجية؛
  • الأسطر 45–50: تكوين بناء المشروع؛

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


// source code directory
  srcDir: '.',
  router: {
    // URL root of application pages
    base: '/nuxt-intro/'
  },
  // server
  server: {
    // service port - default 3000
    port: 81,
    // network addresses listened to - default localhost=127.0.0.1
    host: '0.0.0.0'
  }
  • السطر 2: مكان وجود شفرة المصدر للمشروع. وهي موجودة هنا في الدليل الحالي، أي في نفس مستوى ملف [nuxt.config.js]. هذه هي القيمة الافتراضية؛
  • الأسطر 8–13: تكوين الخادم (ضع في اعتبارك أن تطبيق [nuxt] من النوع [universal] مثبت على كل من الخادم ومتصفح العميل)؛
  • السطر 10: سيتم تقديم صفحات التطبيق على المنفذ 81 للخادم؛
  • السطر 12: بشكل افتراضي [localhost] (عنوان الشبكة 127.0.0.1). يمكن أن يكون للجهاز عناوين شبكة متعددة إذا كان ينتمي إلى شبكات متعددة. يشير العنوان 0.0.0.0 إلى أن خادم الويب يستمع إلى جميع عناوين شبكة الجهاز؛
  • الأسطر 3-6: تكوين موجه التطبيق [nuxt
  • السطر 5: ستكون صفحات التطبيق متاحة على عنوان URL [http://localhost:81/nuxt-intro/

دعونا نضيف هذه الأسطر إلى ملف [nuxt.config.js] ثم نقوم بتشغيل المشروع (نص برمجي npm dev). والنتيجة هي كما يلي:

Image

  • [1] هو عنوان الجهاز على شبكة عامة؛
  • في [2]، منفذ الخدمة؛
  • في [3]، عنوان URL الجذر للتطبيق؛

3.4. مجلد [layouts]

Image

المجلد [layouts] مخصص لمكونات التخطيط. بشكل افتراضي، يتم استخدام المكون المسمى [default.vue]. في هذا المشروع، يكون كما يلي:


<template>
  <div>
    <nuxt />
  </div>
</template>
 
<style>
html {
  font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI',
    Roboto, 'Helvetica Neue', Arial, sans-serif;
  font-size: 16px;
  word-spacing: 1px;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
  box-sizing: border-box;
}
 
*,
*:before,
*:after {
  box-sizing: border-box;
  margin: 0;
}
 
.button--green {
  display: inline-block;
  border-radius: 4px;
  border: 1px solid #3b8070;
  color: #3b8070;
  text-decoration: none;
  padding: 10px 30px;
}
 
.button--green:hover {
  color: #fff;
  background-color: #3b8070;
}
 
.button--grey {
  display: inline-block;
  border-radius: 4px;
  border: 1px solid #35495e;
  color: #35495e;
  text-decoration: none;
  padding: 10px 30px;
  margin-left: 15px;
}
 
.button--grey:hover {
  color: #fff;
  background-color: #35495e;
}
</style>

تعليقات

  • الأسطر 1-5: [قالب] المكون؛
  • السطر 3: تحدد علامة <nuxt /> صفحة التوجيه الحالية؛
  • الأسطر 7–55: النمط المضمن في مكون التخطيط. نظرًا لأن هذا المكون يحتوي على صفحة التوجيه الحالية، فسيتم تطبيق هذا النمط على جميع الصفحات الموجهة في التطبيق؛

يمكننا أن نرى أن الغرض الأساسي من صفحة [default.vue] هنا هو تطبيق نمط على الصفحات الموجهة.

3.5. مجلد [pages]

Image

يحتوي المجلد [pages] على طرق العرض الموجهة، وهي ما يراه المستخدم. صفحة [index.vue] هي الصفحة الرئيسية للتطبيق. مع [nuxt.js]، لا يوجد ملف توجيه. يتم تحديد المسارات بناءً على بنية مجلد [pages]. هنا، يؤدي وجود ملف [index.vue] تلقائيًا إلى إنشاء مسار باسم [index] بمسار [/index]، والذي يتم اختصاره إلى [/] نظرًا لأنه الصفحة الرئيسية. وبالتالي، يتم إنشاء المسار التالي:

        { name : ‘index’, path : ‘/’}

ملف [index.vue] هو كما يلي:


<template>
  <div class="container">
    <div>
      <logo />
      <h1 class="title">
        nuxt-intro
      </h1>
      <h2 class="subtitle">
        nuxt-intro
      </h2>
      <div class="links">
        <a href="https://nuxtjs.org/" target="_blank" class="button--green">
          Documentation
        </a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
        >
          GitHub
        </a>
      </div>
    </div>
  </div>
</template>
 
<script>
import Logo from '~/components/Logo.vue'
 
export default {
  components: {
    Logo
  }
}
</script>
 
<style>
.container {
  margin: 0 auto;
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}
 
.title {
  font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
    'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  display: block;
  font-weight: 300;
  font-size: 100px;
  color: #35495e;
  letter-spacing: 1px;
}
 
.subtitle {
  font-weight: 300;
  font-size: 42px;
  color: #526488;
  word-spacing: 5px;
  padding-bottom: 15px;
}
 
.links {
  padding-top: 15px;
}
</style>

يعرض [القالب] في الأسطر 1–25 العرض التالي:

Image

يتم إنشاء الصورة [1] بواسطة السطر 4 من [القالب]. يمكننا أن نرى أن الصفحة تستخدم مكونًا يسمى [logo]. يتم تعريف هذا في الأسطر 27–35 من نص الصفحة البرمجي. في السطر 28، تشير الرمز [~] إلى جذر المشروع.

Image

مكون [Logo.vue] هو كما يلي:


<template>
  <div class="VueToNuxtLogo">
    <div class="Triangle Triangle--two" />
    <div class="Triangle Triangle--one" />
    <div class="Triangle Triangle--three" />
    <div class="Triangle Triangle--four" />
  </div>
</template>
 
<style>
.VueToNuxtLogo {
  display: inline-block;
  animation: turn 2s linear forwards 1s;
  transform: rotateX(180deg);
  position: relative;
  overflow: hidden;
  height: 180px;
  width: 245px;
}
 
.Triangle {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
}
 
.Triangle--one {
  border-left: 105px solid transparent;
  border-right: 105px solid transparent;
  border-bottom: 180px solid #41b883;
}
 
.Triangle--two {
  top: 30px;
  left: 35px;
  animation: goright 0.5s linear forwards 3.5s;
  border-left: 87.5px solid transparent;
  border-right: 87.5px solid transparent;
  border-bottom: 150px solid #3b8070;
}
 
.Triangle--three {
  top: 60px;
  left: 35px;
  animation: goright 0.5s linear forwards 3.5s;
  border-left: 70px solid transparent;
  border-right: 70px solid transparent;
  border-bottom: 120px solid #35495e;
}
 
.Triangle--four {
  top: 120px;
  left: 70px;
  animation: godown 0.5s linear forwards 3s;
  border-left: 35px solid transparent;
  border-right: 35px solid transparent;
  border-bottom: 60px solid #fff;
}
 
@keyframes turn {
  100% {
    transform: rotateX(0deg);
  }
}
 
@keyframes godown {
  100% {
    top: 180px;
  }
}
 
@keyframes goright {
  100% {
    left: 70px;
  }
}
</style>

يتكون هذا المكون بشكل أساسي من أنماط ورسوم متحركة لإنشاء صورة متحركة.

3.7. Vue DevTools

[Vue DevTools] هو امتداد للمتصفح يتيح لك فحص كائنات [nuxt.js] و[vue.js] في المتصفح. وقد استخدمناه بالفعل في الفصل الخاص بـ [vue.js]. دعونا ندرس ما تجده هذه الأداة عند عرض الصفحة الرئيسية لتطبيقنا:

Image

  • في [1]، يشير المكون [PagesIndex] إلى الصفحة [pages/index.vue
  • في [2] نرى أن هذا المكون يحتوي على خاصية [$route]، وهي المسار الذي أدى إلى صفحة [index

كممارسة بسيطة، دعونا نعرض هذا المسار في وحدة التحكم.

3.8. تعديل الصفحة الرئيسية

سنقوم بتعديل ملف [index.vue]. في إعداد مشروعنا، قمنا بتثبيت اثنين من التبعيات:

  • [eslint]: الذي يتحقق من صحة بناء جملة ملفات JavaScript ومكونات Vue. إذا تم تثبيت ملحق [ESLint] لـ VSCode، فسيتم التحقق من صحة بناء الجملة أثناء الكتابة، وسيتم الإبلاغ عن الأخطاء على الفور؛
  • [prettier]: الذي يقوم بتنسيق كود JavaScript بطريقة قياسية؛

هذه التبعيات مدرجة في ملف [package.json]:


"devDependencies": {
    "@nuxtjs/eslint-config": "^1.0.1",
    "@nuxtjs/eslint-module": "^1.0.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^6.1.0",
    "eslint-config-prettier": "^4.1.0",
    "eslint-plugin-nuxt": ">=0.4.2",
    "eslint-plugin-prettier": "^3.0.1",
    "prettier": "^1.16.4"
}

لاحظت (في نوفمبر 2019) أنه عند التثبيت باستخدام الأمر [yarn create nuxt-app]، لا تعمل أدوات [eslint و prettier] أثناء الكتابة. ولا تظهر الأخطاء إلا أثناء التحويل البرمجي. وبعد إجراء بعض البحث، وجدت إعدادات تعمل بشكل صحيح:

Image

أنشئ مجلد [.vscode] في جذر المشروع وضع ملف [settings.json] التالي بداخله:


{
  "eslint.validate": [
    {
      "language": "vue",
      "autoFix": true
    },
    {
      "language": "javascript",
      "autoFix": true
    }
  ],
  "eslint.autoFixOnSave": true,
  "editor.formatOnSave": false
}
  • الأسطر 2–11: تحدد أنه عند قيام [eslint] بالتحقق من صحة ملفات .vue و .js، يجب عليه إصلاح أي أخطاء يمكنه إصلاحها؛
  • السطر 12: عند حفظ ملف، يجب على [eslint] إصلاح أي أخطاء يمكنه إصلاحها؛
  • السطر 13: يعطل التنسيق الافتراضي في VSCode عند الحفظ. سيتولى [prettier] هذه المهمة بدلاً من ذلك؛

مع هذا التكوين:

  • يتم الإبلاغ عن أخطاء الصياغة أو التنسيق فور كتابة النص؛
  • يتم تصحيح أخطاء التنسيق تلقائيًا عند حفظ الملف؛

يتم تكوين مكتبة [prettier] بواسطة ملف [.prettierrc]:

Image

بشكل افتراضي، يكون هذا الملف كما يلي:


{
  "semi": false,
  "arrowParens": "always",
  "singleQuote": true
}
  • السطر 1: لا توجد فاصلة منقوطة في نهاية الجمل؛
  • السطر 2: إذا كانت دالة السهم تحتوي على معلمة واحدة، يتم وضعها بين قوسين؛
  • السطر 3: تُحاط السلاسل بعلامات اقتباس مفردة (وليس مزدوجة)؛

نضيف القاعدتين التاليتين:


{
  "semi": false,
  "arrowParens": "always",
  "singleQuote": true,
  "printWidth": 120,
  "endOfLine": "auto"
}
  • السطر 5: يمكن أن يصل طول سطر الكود إلى 120 حرفًا؛
  • السطر 6: يمكن أن يكون حرف نهاية السطر إما CRLF (ويندوز) أو LF (يونيكس)؛

أخيرًا، يتم تعديل ملف [package.json] على النحو التالي:


"scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate",
    "lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
    "lintfix": "eslint --fix --ext .js,.vue --ignore-path .gitignore ."
},
  • السطر 7: نضيف الأمر [lintfix]، وهو مطابق للأمر [lint] في السطر 6 باستثناء أنه يتضمن المعلمة [--fix]. يقوم الأمر [lint] بفحص بناء الجملة وتنسيق جميع الملفات في المشروع والإبلاغ عن أي أخطاء. سيقوم [lintfix] بنفس الشيء، باستثناء أن أي مشكلات في التنسيق يمكن تصحيحها سيتم إصلاحها تلقائيًا. [lintfix] هو الأمر الذي يجب استخدامه إذا فشل البناء بسبب مشكلات في تنسيق الملفات؛

بعد الانتهاء من ذلك، نقوم بتعديل ملف [index.vue] على النحو التالي:

Image


<script>
/* eslint-disable no-console */
import Logo from '~/components/Logo.vue'
 
export default {
  components: {
    Logo
  },
  // cycle de vie
  created() {
    console.log('created, route=', this.$route)
  }
}
</script>
  • الأسطر 10–12: نضيف الدالة [created]، التي يتم تنفيذها تلقائيًا عند إنشاء المكون؛
  • السطر 11: نعرض المسار الحالي؛
  • السطر 2: تعليق مخصص لـ [eslint]. بدون هذا التعليق، يبلغ [eslint] عن خطأ السطر 11: لا يسمح ببيانات [console] في وظائف دورة الحياة. [eslint] قابل للتكوين. سنحتفظ بتكوينه الافتراضي ونستخدم تعليقات مثل تلك الموجودة في السطر 2 لتعطيل قاعدة [eslint] محددة. سنستخدم نوعين من التعليقات:
    • /* تعطيل قاعدة [eslint] */: يعطل قاعدة للملف بأكمله؛
    • // تعطيل قاعدة [eslint]: يعطل قاعدة للسطر التالي؛

أثناء الكتابة، يتم تمييز الأخطاء وتتوفر ميزة [Quick Fix]:

Image

تشغيل المشروع:

Image

  • في [1]، علامة التبويب [عرض] في أدوات المطور بالمتصفح (F12
  • في [2] و[3]، عرض المسار؛

لماذا عرضان بدلاً من عرض واحد فقط؟

يتكون تطبيق [Nuxt] من مكونين، خادم وعميل:

  1. يوفر الخادم صفحات التطبيق عند بدء تشغيل التطبيق، ثم في كل مرة يتم فيها تحديث الصفحة في المتصفح (F5) أو يقوم المستخدم بإدخال عنوان URL للتطبيق يدويًا؛
  2. تحتوي كل صفحة يقدمها المتصفح على الصفحة المطلوبة بالإضافة إلى كود JavaScript للتطبيق بأكمله، والذي يتم تنفيذه بعد ذلك في المتصفح. هذا هو العميل. طالما لم يتم تحديث الصفحة في المتصفح، يعمل التطبيق مثل تطبيق Vue الكلاسيكي في وضع [SPA] (تطبيق صفحة واحدة). بمجرد أن يقوم المستخدم بتحديث الصفحة يدويًا، يتم طلب الصفحة من الخادم، ونعود إلى الخطوة 1 السابقة.

ما تحتاج إلى فهمه هو أن هذه هي نفس الصفحات الموجودة في مجلد [pages] التي يتم تقديمها إما من قبل الخادم أو العميل. لهذا السبب، يطلق مطورو [nuxt] على هذا النوع من الصفحات اسم الصفحة المتماثلة. يمكن تفسير نفس صفحات [.vue] من قبل كل من العميل والخادم. لنأخذ مثال صفحة [index]:


<template>
  <div class="container">
    <div>
      <logo />
      <h1 class="title">
        nuxt-intro
      </h1>
      <h2 class="subtitle">
        nuxt-intro
      </h2>
      <div class="links">
        <a href="https://nuxtjs.org/" target="_blank" class="button--green">
          Documentation
        </a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
        >
          GitHub
        </a>
      </div>
    </div>
  </div>
</template>
 
<script>
/* eslint-disable no-console */
import Logo from '~/components/Logo.vue'
 
export default {
  components: {
    Logo
  },
  // cycle de vie
  created() {
    console.log('created, route=', this.$route)
  }
}
</script>

نظرًا لأن هذه هي الصفحة الرئيسية، فإن الخادم يعرضها عند بدء تشغيل التطبيق. وتتمتع الصفحة الموجودة على الخادم أيضًا بدورة حياة، وهي مماثلة لتلك الخاصة بصفحة [Vue] التقليدية باستثناء الدالتين [beforeMount] و[mounted]، اللتين لا توجدان على جانب الخادم. يتم تنفيذ الدالة [created]، وهو ما يفسر السجل الأول. وهذا يعني، بالمناسبة، أن الخادم قادر على تنفيذ نصوص JavaScript. هنا وبشكل عام، هذا الخادم هو خادم [node.js]. بمجرد إنشاء الصفحة على الخادم، تصل إلى المتصفح حيث تمر بدورة الحياة مرة أخرى. يتم تنفيذ الدالة [created] للمرة الثانية، مما ينتج عنه السجل الثاني.

يمكن أن تكون بنية تطبيق [nuxt] كما يلي:

Image

  • [1]: المتصفح الذي يستضيف تطبيق [nuxt] بمجرد تحميله في المتصفح. وهذا ما نسميه عميل [nuxt
  • [3]: الخادم الذي يستضيف تطبيق [nuxt] في البداية. يتم تحميله في المتصفح [1] عند بدء تشغيل التطبيق وفي كل مرة يقوم فيها المستخدم بتحديث صفحة المتصفح الحالية أو إدخال عنوان URL للتطبيق يدويًا. هنا يكمن الاختلاف في التشغيل مقارنةً بتطبيق Vue الكلاسيكي. مع تطبيق Vue الكلاسيكي، بمجرد تحميله في المتصفح، لم يتم استدعاء الخادم مرة أخرى أبدًا. هناك اختلاف مهم آخر لم نره بعد، وهو أن الخادم لتطبيق Vue هو خادم ثابت، غير قادر على تفسير صفحات [.vue]، في حين أن الخادم لتطبيق Nuxt [universal] هو خادم JavaScript. قبل إرسال الصفحة إلى المتصفح، يمكن للخادم تنفيذ البرامج النصية، وعلى سبيل المثال، جلب البيانات من الخادم [2]؛
  • [2]: هو الخادم الذي يوفر البيانات إما لعميل [nuxt] [1] أو لخادم [nuxt] [3]؛

في الرسم البياني أعلاه، يمكننا تمييز ثلاثة أنظمة فرعية للعميل/الخادم:

  • [1، 3]: يستضيف تطبيق [nuxt]. يوفر [3] التطبيق عند بدء تشغيل التطبيق مع الصفحة الرئيسية وفي كل مرة يطلب فيها المستخدم صفحة يدويًا. يستضيف [1] تطبيق [nuxt] المستلم من [3]، والذي يعمل بعد ذلك في وضع [SAP] طالما لم يتم طلب الصفحات يدويًا من [3]؛
  • [1، 2]: في وضع [SAP]، يسترد عميل [nuxt] البيانات الخارجية من خادم واحد أو أكثر؛
  • [3، 2]: عند إنشاء الصفحة التي يطلبها المستخدم، يمكن للخادم [3] أيضًا استرداد البيانات الخارجية من خادم واحد أو أكثر؛

وبالتالي، فإن الخادم [3] هو الذي يميز تطبيق [Nuxt] عن تطبيق [Vue]. يتم استدعاء هذا الخادم في كل مرة يطلب فيها المستخدم صفحة يدويًا. وهو يعالج نفس صفحات [.vue] التي يعالجها عميل [Vue] [1]. وهو خادم JavaScript قادر على تنفيذ البرامج النصية الموجودة على الصفحة. وهذا يمكن، على سبيل المثال، أن يغير طريقة إنشاء الصفحة الرئيسية باستخدام البيانات الخارجية: في حين أن تطبيق [vue] يسترد هذه البيانات بالضرورة من العميل [1]، هنا يمكن استردادها بواسطة الخادم [3] قبل إرسال الصفحة إلى العميل. وبذلك تصبح الصفحة الرئيسية ذات مغزى ويمكن أن تساعد في تحسين تحسين محركات البحث (SEO) للتطبيق.

ملاحظة: في وضع التطوير، غالبًا ما تكون الكيانات الثلاثة [1، 2، 3] موجودة على نفس الجهاز. وسيكون هذا هو الحال هنا في جميع أمثلةنا.

3.9. نقل كود مصدر التطبيق إلى مجلد منفصل

بعد ذلك، سننشئ تطبيقات [nuxt] متنوعة في نفس مجلد [dvp]. وذلك لأن مجلد التبعيات [node_modules] الذي يتم إنشاؤه لكل مشروع [nuxt] يمكن أن يصل حجمه إلى عدة مئات من الميغابايت. سننشئ مجلدات مختلفة [nuxt-00، nuxt-01، ...] داخل مجلد [dvp] لتخزين شفرة المصدر للأمثلة التي سيتم اختبارها. ثم سنستخدم ملف التكوين [nuxt-config.js] لتحديد موقع شفرة المصدر لمشروع [dvp]، الذي سيظل المشروع [nuxt] الوحيد في هذا البرنامج التعليمي.

ننقل شفرة المصدر للتطبيق التي تم إنشاؤها مبدئيًا بواسطة الأمر [yarn create nuxt-app] إلى مجلد [nuxt-00]:

Image

  • في [2]، قمنا بنقل المجلدات [components, layouts, pages] إلى مجلد [nuxt-00
  • في [3]، نحتاج إلى تعديل ملف [nuxt.config.js

نقوم بتعديل ملف [nuxt.config.js] على النحو التالي:


export default {
  mode: 'universal',
  /*
   ** Headers of the page
   */
  ...
  /*
   ** Build configuration
   */
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  },
  // source code directory
  srcDir: 'nuxt-00',
  // router
  router: {
    // application URL root
    base: '/nuxt-00/'
  },
  // server
  server: {
    // service port, default 3000
    port: 81,
    // network addresses listened to, default localhost: 127.0.0.1
    // 0.0.0.0 = all the machine's network addresses
    host: '0.0.0.0'
  }
}

تم تعديل الملف في موضعين:

  • السطر 17: نحدد أن شفرة المصدر لمشروع [dvp] موجودة في المجلد [nuxt-00
  • السطر 21: نحدد أن عنوان URL الجذر للتطبيق هو الآن [/nuxt-00/]. لم يكن هذا التغيير إلزامياً. كان بإمكاننا حذف هذه الخاصية، وفي هذه الحالة سيكون جذر عناوين URL هو [/]. هنا، سيساعدنا هذا على تذكر أن شفرة المصدر التي يتم تنفيذها موجودة في المجلد [nuxt-00

بمجرد الانتهاء من ذلك، يتم تنفيذ مشروع [dvp] كما كان من قبل:

Image

3.10. نشر تطبيق [nuxt-00]

سنقوم بتشغيل تطبيق [nuxt-00] في بيئة أخرى غير البيئة المدمجة في VSCode.

أولاً، نقوم بتجميع التطبيق:

Image

  • في [3]، نتيجة ترجمة العميل. سيتم تنفيذ هذا بواسطة المتصفح؛
  • في [4]، نتيجة ترجمة الخادم. سيتم تنفيذ هذا بواسطة خادم [node.js

يتم وضع ناتج التجميع في المجلد [.nuxt]:

Image

نقوم بنسخ المجلدين [.nuxt و node_modules] والملفين [package.json و nuxt.config.js] إلى مجلد منفصل:

Image

تم تبسيط ملف [package.json] على النحو التالي:


{
  "scripts": {
    "start": "nuxt start"
  }
}
  • نحتفظ فقط بالبرنامج النصي [start]، الذي يقوم بتشغيل النسخة المُجمَّعة من المشروع؛

تم تبسيط ملف [nuxt.config.js] على النحو التالي:


export default {
  // router
  router: {
    // application URL root
    base: '/nuxt-00/'
  },
  // server
  server: {
    // service port, default 3000
    port: 81,
    // network addresses listened to, default localhost: 127.0.0.1
    // 0.0.0.0 = all the machine's network addresses
    host: '0.0.0.0'
  }
}
  • السطر 5: يحدد عنوان URL الأساسي للتطبيق المُجمَّع؛
  • الأسطر 8–14: تحدد منفذ الخدمة وعناوين الشبكة التي سيتم الاستماع إليها؛

بمجرد الانتهاء من ذلك، افتح محطة Laragon وانتقل إلى المجلد الذي يحتوي على النسخة المُجمَّعة من المشروع. يمكنك فتح أي نوع من المحطات، ولكن يجب أن يكون الملف القابل للتنفيذ [npm] موجودًا في مسار PATH الخاص بالمحطة. وهذا هو الحال بالنسبة لمحطة Laragon.

بمجرد الانتهاء من ذلك، اكتب الأمر [npm run start]:

Image

في [3]، نرى أنه تم تشغيل خادم وهو يستمع على عنوان URL [http://192.168.1.128:81/nuxt-00/]. الآن دعونا نطلب عنوان URL هذا باستخدام متصفح [4]. نرى نفس النتيجة كما في السابق. في المحطة الطرفية، تمت كتابة السجلات [5]. هذا هو السجل الموجود في طريقة [created] لصفحة [index.vue]، والتي تم تنفيذها بواسطة خادم [node.js].

Image

على جانب المتصفح [6]، نرى أيضًا السجل من طريقة [created] لصفحة [index.vue]، ولكن هذه المرة تم تنفيذه بواسطة العميل.

3.11. إعداد خادم آمن

أعلاه، عنوان URL للتطبيق هو [http://192.168.1.128/nuxt-00/]. نود أن يكون [https://192.168.1.128/nuxt-00/]. لذلك نحتاج إلى إعداد خادم آمن. وإليك كيفية القيام بذلك.

ملاحظة: هذه الطريقة مأخوذة من المقالة [https://stackoverflow.com/questions/56966137/how-to-run-nuxt-npm-run-dev-with-https-in-localhost].

أولاً، نقوم بإنشاء مفتاح خاص ومفتاح عام باستخدام [openssl]. عادةً ما يتم تثبيت [openssl] مع خادم Laragon. ونتيجة لذلك، يتوفر هذا الأمر في أي محطة طرفية لـ Laragon. لذا، دعونا نفتح محطة طرفية لـ Laragon وننتقل إلى مجلد التطبيق الذي تم نشره:

Image

Image

  • في [2]، اكتب الأمر [openssl genrsa 2048 > server.key
  • في [3]، يتم إنشاء ملف [server.key
  • في [4]، اكتب الأمر [openssl req -new -x509 -nodes -sha256 -days 365 -key server.key -out server.crt
  • في [5]، يتم إنشاء ملف باسم [server.crt

يشكل هذان الملفان شهادة موقعة ذاتيًا. لا تقبل معظم المتصفحات هذه الشهادات إلا بعد موافقة المستخدم الذي طلب الصفحة.

يجب الآن استخدام ملفات [server.key، server.crt] بواسطة تطبيق الويب. للقيام بذلك، يجب تعديل ملف [nuxt.config.js] على النحو التالي:


import path from 'path'
import fs from 'fs'
 
export default {
  // router
  router: {
    // application URL root
    base: '/nuxt-00/'
  },
  // server
  server: {
    // service port, default 3000
    port: 81,
    // network addresses listened to, default localhost: 127.0.0.1
    // 0.0.0.0 = all the machine's network addresses
    host: '0.0.0.0',
    // self-signed certificate
    https: {
      key: fs.readFileSync(path.resolve(__dirname, 'server.key')),
      cert: fs.readFileSync(path.resolve(__dirname, 'server.crt'))
    }
  }
}

الأسطر 18–21 تنفذ بروتوكول [https].

الآن دعونا نشغل التطبيق مرة أخرى:

Image

3.12. نهاية المثال الأول

انتهى المثال الأول الآن. وقد علمنا العديد من مفاهيم [nuxt]. سنقوم الآن بتطوير أمثلة أخرى سنضعها في مجلدات [nuxt-01، nuxt-02، ...]. وبما أن هذه الأمثلة ستستخدم ملف [nuxt.config.js] مختلف، فسنحفظ ملف [nuxt.config.js] المستخدم لتشغيل كل منها في المجلدات الخاصة بها:

Image