15. مثال [nuxt-12]: استعلامات HTTP باستخدام axios
15.1. Présentation
في هذا المثال الجديد، سنكتشف كيف يمكن في وظائف [asyncData] إجراء طلبات HTTP باستخدام مكتبة [axios]. علاوة على ذلك، سنستخدم المفاهيم التي اكتسبناها بالفعل:
- استخدام المكونات الإضافية في المثال [nuxt-06]:
- استمرارية التخزين في ملف تعريف ارتباط الجلسة في المثال [nuxt-06]؛
- التحكم في التنقل باستخدام برامج الوسيطة من المثال [nuxt-09]؛
- إدارة الأخطاء من المثال [nuxt-11]؛
ستكون بنية المثال كما يلي:

- سيتم تخزين التطبيق [nuxt] على الخادم [node.js] [3]، وسيتم تنزيله بواسطة المتصفح [1] الذي سيقوم بتنفيذه بعد ذلك؛
- سيقوم كل من العميل [nuxt] [1] وكذلك الخادم [nuxt] [3] بإرسال طلبات HTTP إلى خادم البيانات [2]. سيكون هذا الخادم هو خادم حساب الضريبة الذي تم تطويره في الجزء PHP 7. سنستخدم أحدث إصدار منه، الإصدار 14، مع الاستعلامات المسموح بها CORS؛
يمكن تبسيط بنية المثال على النحو التالي:

- في [1]، يقوم الخادم [node.js] بتسليم الصفحات [nuxt] إلى المتصفح [2]. إن الطبقة [web] [8] من الخادم هي التي تقدم هذه الصفحات. لتسليم الصفحة، قد يكون الخادم قد طلب بيانات خارجية من خادم البيانات [3]. الطبقة [DAO] [9] هي التي تقوم بالطلبات HTTP اللازمة؛
- عند كل استدعاء للصفحة إلى الخادم [node.js][1]، يتلقى المتصفح [2] التطبيق بالكامل [nuxt] الذي سيتم تنفيذه بعد ذلك في الوضع SPA. يعرض الكتلة [UI] (واجهة المستخدم) [4] صفحات [vue.js] للمستخدم. قد تؤدي الإجراءات التي يقوم بها المستخدم أو دورة الحياة الطبيعية للصفحات إلى استدعاء بيانات خارجية من خادم البيانات [3]. ثم تقوم الطبقة [DAO] [5] بإجراء الطلبات HTTP اللازمة؛
15.2. هيكل المشروع

15.3. ملف التكوين [nuxt.config.js]
سيتم التحكم في المشروع بواسطة الملف التالي: [nuxt.config.js]:
export default {
mode: 'universal',
/*
** Headers of the page
*/
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' }]
},
/*
** Customize the progress-bar color
*/
loading: false,
/*
** Global CSS
*/
css: [],
/*
** Plugins to load before mounting the App
*/
plugins: [
{ src: '@/plugins/client/plgSession', mode: 'client' },
{ src: '@/plugins/server/plgSession', mode: 'server' },
{ src: '@/plugins/client/plgDao', mode: 'client' },
{ src: '@/plugins/server/plgDao', mode: 'server' },
{ src: '@/plugins/client/plgEventBus', mode: 'client' }
],
/*
** Nuxt.js dev-modules
*/
buildModules: [
// الوثيقة: https://github.com/nuxt-community/eslint-module
'@nuxtjs/eslint-module'
],
/*
** Nuxt.js modules
*/
modules: [
// الوثيقة: https://bootstrap-vue.js.org
'bootstrap-vue/nuxt'،
// الوثيقة: https://axios.nuxtjs.org/usage
'@nuxtjs/axios،
// https://www.npmjs.com/package/cookie-universal-nuxt
'cookie-universal-nuxt'
],
/*
** Axios module configuration
** See https://axios.nuxtjs.org/options
*/
axios: {},
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend(config, ctx) { }
},
// مجلد شفرة المصدر
srcDir: 'nuxt-12',
// الموجه
router: {
// جذر URL للتطبيق
base: '/nuxt-12/',
// برمجيات التوجيه الوسيطة
middleware: ['routing']
},
// الخادم
server: {
// منفذ الخدمة، 3000 افتراضيًا
port: 81,
// عناوين الشبكة المستمعة، افتراضيًا localhost: 127.0.0.1
// 0.0.0.0 = جميع عناوين الشبكة الخاصة بالجهاز
host: 'localhost'
},
// البيئة
env: {
// تكوين axios
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/version-14'،
// تكوين ملف تعريف ارتباط الجلسة [nuxt]
maxAge: 60 * 5
}
}
- السطر 22: نحن ندير بأنفسنا تنبيه انتظار انتهاء إجراء غير متزامن؛
- السطر 31: سنستخدم مكونات إضافية متنوعة ستكون مخصصة إما للعميل أو للخادم، ولكن ليس لكليهما في آن واحد؛
- السطر 52: تم دمج الوحدة النمطية [axios] في [nuxt]. سيؤدي ذلك إلى أن الكائن [axios] الذي سيقوم بإجراء طلبات HTTP منالتطبيق [nuxt] إلى خادم حساب الضريبة PHP سيكون متاحًا في [context.$axios]؛
- السطر 54: ستسمح لنا الوحدة النمطية [cookie-universal-nuxt] بحفظ جلسة [nuxt] في ملف تعريف ارتباط؛
- السطر 60: تسمح لنا الخاصية [axios] بتكوين الوحدة النمطية [@nuxtjs/axios] في السطر 52. لن نستخدم هذه الإمكانية، بل سنفضل الخاصية [env] في السطر 88؛
- السطر 90: المدة القصوى لانتظار استجابة خادم حساب الضريبة؛
- السطر 91: ضروري للعميل [nuxt] - يسمح باستخدام ملفات تعريف الارتباط في التبادلات مع خادم حساب الضريبة؛
- السطر 92: URL الأساسي لخادم حساب الضريبة؛
- السطر 94: مدة صلاحية جلسة nuxt (5 دقائق)؛
- السطر 77: سيتم التحكم في تنقل العميل والخادم [nuxt] بواسطة برنامج وسيط للتوجيه؛
15.4. طبقة [UI] للتطبيق

سنمنح التطبيق [nuxt] حق الوصول إلى API لخادم حساب الضريبة عبر العرض التالي:

- في [2]، القائمة التي تتيح الوصول إلى API لخادم حساب الضرائب:
- [Authentification]: تتوافق مع الصفحة [authentification]. تقوم هذه الصفحة بإرسال طلب مصادقة إلى خادم حساب الضريبة باستخدام معرفات [admin, admin] التي تعد حالياً الوحيدة المصرح بها. النتيجة المعروضة مشابهة لـ [3]؛
- [Requête AdminData]: تتوافق مع الصفحة [get-admindata]. تطلب هذه الصفحة من خادم حساب الضريبة البيانات، المشار إليها هنا بـ [adminData]، التي تسمح بحساب الضريبة. النتيجة المعروضة مشابهة لـ [3]؛
- [Fin session impôt]: تتوافق مع الصفحة [fin-session]. تطلب هذه الصفحة إنهاء الجلسة PHP من خادم حساب الضريبة. ثم يقوم الخادم بإلغاء الجلسة الحالية PHP وتهيئة جلسة جديدة فارغة؛
15.5. طبقات [dao] لتطبيق [nuxt]
كما هو موضح أعلاه، ستكون بنية التطبيق [nuxt] كما يلي:

- في [1]، يقوم الخادم [node.js] بتسليم الصفحات [nuxt] إلى المتصفح [2]. إن الطبقة [web] [8] من الخادم هي التي تقدم هذه الصفحات. لتسليم الصفحة، قد يكون الخادم قد طلب بيانات خارجية من خادم البيانات [3]. الطبقة [DAO] [9] هي التي تقوم بالطلبات HTTP اللازمة؛
- عند كل استدعاء للصفحة إلى الخادم [node.js][1]، يتلقى المتصفح [2] التطبيق بالكامل [nuxt] الذي سيتم تنفيذه بعد ذلك في الوضع SPA. يعرض الكتلة [UI] (واجهة المستخدم) [4] صفحات [vue.js] للمستخدم. قد تؤدي الإجراءات التي يقوم بها أو دورة حياة الصفحات إلى استدعاءات لبيانات خارجية إلى خادم البيانات [3]. وهي الطبقة [DAO] [5] التي تقوم عندئذٍ بإجراء الطلبات HTTP اللازمة؛
سنستخدم الإصدار 14 من خادم حساب الضريبة الذي تم تطويره في الوثيقة |مقدمة إلى لغة PHP7 من خلال الأمثلة|. سنستخدم جزءًا فقط من واجهة برمجة التطبيقات (API) الخاصة به:
الاستعلام | الاستجابة |
| |
| |
| |
| |
15.5.1. الطبقة [dao] من الخادم [nuxt]

سيستخدم الخادم [node.js] [1] الطبقة [dao] الموصوفة في الوثيقة |مقدمة إلى إطار العمل VUE.JS من خلال المثال|. نذكر هنا رمزه:
'use strict';
// الاستيرادات
import qs from 'qs'
class Dao {
// المنشئ
constructor(axios) {
this.axios = axios;
// ملف تعريف ارتباط الجلسة
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
// بدء الجلسة
async initSession() {
// خيارات الطلب HHTP [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// معلمات URL
params: {
action: 'init-session',
type: 'json'
}
};
// تنفيذ الاستعلام HTTP
return await this.getRemoteData(options);
}
async authentifierUtilisateur(user, password) {
// خيارات الطلب HHTP [post /main.php?action=authentifier-utilisateur]
const options = {
method: "POST",
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
// نص POST
data: qs.stringify({
user: user,
password: password
}),
// معلمات URL
params: {
action: 'authentifier-utilisateur'
}
};
// تنفيذ الطلب HTTP
return await this.getRemoteData(options);
}
async getAdminData() {
// خيارات الاستعلام HHTP [get /main.php?action=get-admindata]
const options = {
method: "GET",
// معلمات الاستعلام URL
params: {
action: 'get-admindata'
}
};
// تنفيذ الاستعلام HTTP
const data = await this.getRemoteData(options);
// النتيجة
return data;
}
async getRemoteData(options) {
// لملف تعريف الارتباط الخاص بالجلسة
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = this.sessionCookie;
// تنفيذ الطلب HTTP
let response;
try {
// طلب غير متزامن
response = await this.axios.request('main.php', options);
} catch (error) {
// المعلمة [error] هي حالة استثنائية - يمكن أن تتخذ أشكالًا مختلفة
if (error.response) {
// رد الخادم موجود في [error.response]
response = error.response;
} else {
// يتم إعادة إرسال الخطأ
throw error;
}
}
// response هي مجمل استجابة الخادم HTTP (رؤوس HTTP + الاستجابة نفسها)
// يتم استرداد ملف تعريف ارتباط الجلسة إن وجد
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie هو مصفوفة
// يتم البحث عن ملف تعريف ارتباط الجلسة في هذا المصفوفة
let trouvé = false;
let i = 0;
while (!trouvé && i < setCookie.length) {
// نبحث عن ملف تعريف ارتباط الجلسة
const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// يتم حفظ ملف تعريف ارتباط الجلسة
// eslint-disable-next-line require-atomic-updates
this.sessionCookie = results[1];
// تم العثور عليه
trouvé = true;
} else {
// العنصر التالي
i++;
}
}
}
// رد الخادم موجود في [response.data]
return response.data;
}
}
// تصدير الفئة
export default Dao;
- جميع طرق الطبقة [dao] تعرض الكائن المرسل من خادم البيانات [{action : ‘xx’, état : nn, réponse : {...}] مع:
- [action]: اسم الإجراء الذي ينفذه خادم البيانات؛
- [état]: مؤشر رقمي:
- [initSession]: الحالة=700 لرد بدون أخطاء؛
- [authentifierUtilisateur]: الحالة=200 للاستجابة بدون أخطاء؛
- [getAdminData]: الحالة=1000 لرد بدون أخطاء؛
- [fin-session]: الحالة=400 لرد خالٍ من الأخطاء؛
- [réponse]: استجابة مرتبطة بالمؤشر الرقمي [état]. قد تختلف وفقًا لهذا المؤشر الرقمي؛
دعونا نلقي نظرة على منشئ الفئة [Dao]:
// المنشئ
constructor(axios) {
this.axios = axios;
// ملف تعريف ارتباط الجلسة
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
- السطر 2: الكائن [axios] المقدم كحجة لمُنشئ يُقدم بواسطة الكود المستدعي. وهو الذي سيقوم بإجراء الاستعلامات HTTP؛
- السطر 5: اسم ملف تعريف الارتباط للجلسة الذي أرسله خادم البيانات مكتوب في PHP؛
- السطر 6: ملف تعريف ارتباط الجلسة الذي يتم تبادله بين الطبقة [dao] وخادم البيانات. يتم تهيئة هذا الملف بواسطة الدالة [getRemoteData] في الأسطر 67-113؛
بالنسبة لملف تعريف الارتباط الخاص بالجلسة، علينا النظر إلى طبقتين منفصلتين [dao]:
- طبقة المتصفح؛
- طبقة الخادم؛
سيتعين علينا إدارة ثلاث ملفات تعريف ارتباط للجلسة:
- ملف تعريف الارتباط الذي يتم تبادله بين العميل [nuxt] والخادم PHP 7؛
- الملف الذي يتم تبادله بين الخادم [nuxt] والخادم PHP 7؛
- الملف الذي يتم تبادله بين العميل [nuxt] والخادم [nuxt]؛
سنحرص على أن تكون ملف تعريف الارتباط الخاص بالجلسة مع الخادم PHP هو نفسه بالنسبة للعميل والخادم [nuxt]. سنسمي ملف تعريف الارتباط هذا ملف تعريف ارتباط الجلسة PHP. ملف تعريف الارتباط هذا هو ملف تعريف الارتباط الخاص بالحالتين 1 و 2. سنسمي ملف تعريف ارتباط الجلسة [nuxt]، ملف تعريف الارتباط الخاص بالحالة 3. سيكون لدينا إذن جلستان:
- جلسة PHP مع ملف تعريف ارتباط الجلسة PHP؛
- جلسة [nuxt] مع ملف تعريف ارتباط الجلسة [nuxt]؛
لماذا نستخدم ملف تعريف الارتباط نفسه لجلسات PHP الخاصة بالعميل والمتصفح [nuxt]؟ نريد أن تتمكن التطبيق من التواصل مع الخادم PHP 7 سواء مع العميل أو الخادم [nuxt]:
- إذا أدت إجراء A من الخادم [nuxt] إلى وضع الخادم PHP في حالة E، فإن هذه الحالة تنعكس في الجلسة PHP التي يديرها الخادم PHP؛
- باستخدام ملف تعريف الارتباط للجلسة PHP نفسه الذي يستخدمه الخادم، فإن الإجراء B للعميل [nuxt] الذي يتبع الإجراء A للخادم [nuxt] سيجد الخادم PHP فيالحالة E التي تركها الخادم [nuxt] وبالتالي يمكنه الاعتماد على العمل الذي أنجزه بالفعل الخادم [nuxt]؛
- إذا جاءت بعد الإجراء B للعميل [nuxt] إجراء C للخادم [nuxt]، وللسبب نفسه المذكور سابقًا، سيتمكن هذا الإجراء من الاعتماد على العمل الذي أنجزه الإجراء B للعميل [nuxt]؛
لكي يتمكن متصفح العميل [nuxt] من التواصل مع خادم حساب الضريبة PHP، سنستخدم الإصدار 14 من هذا الخادم الذي يسمح بالمكالمات بين المجالات، أي تلك التي تتم من متصفح إلى الخادم PHP. أما المكالمات من الخادم [nuxt] إلى الخادم PHP فهي ليست مكالمات بين المجالات. هذا المفهوم موجود فقط للمكالمات التي تتم من متصفح.
لنعد إلى كود منشئ الفئة [Dao] السابقة:
// منشئ
constructor(axios) {
this.axios = axios;
// ملف تعريف ارتباط الجلسة
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
- السطران 5 و6 يتوافقان مع ملف تعريف الارتباط الخاص بجلسة PHP مع خادم حساب الضريبة؛
إدارة ملف تعريف الارتباط للجلسة PHP أعلاه لا تناسب الخادم [nuxt]: يتم إنشاء مثيل لطبقته [dao] عند كل طلب جديد يتم إرساله إلى الخادم [nuxt]. نتذكر في الواقع أن طلب صفحة من الخادم [nuxt] يعادل إعادة تعيين التطبيق [nuxt]. وبالتالي، عندما يتم تهيئة ملف تعريف الارتباط الخاص بجلسة العمل PHP للطبقة [dao] في نهاية الطلب الأول المقدم إلى خادم البيانات بواسطة الخادم [nuxt]، يتم تهيئة ملف تعريف الارتباط للجلسة PHP للطبقة [dao]، تُفقد هذه القيمة عند الطلب التالي HTTP من نفس الخادم [nuxt]، لأنه في غضون ذلك تم إعادة إنشاء طبقته [dao]، وإعادة تشغيل المنشئ، وإعادة تعيين ملف تعريف الارتباط للجلسة PHP بسلسلة فارغة (السطر 6)؛
أحد الحلول هو استخدام منشئ آخر للطبقة [dao] للخادم:
// منشئ
constructor(axios, phpSessionCookie) {
// مكتبة axios
this.axios = axios
// قيمة ملف تعريف ارتباط الجلسة
this.phpSessionCookie = phpSessionCookie
// اسم ملف تعريف ارتباط جلسة العمل الخاص بالخادم PHP
this.phpSessionCookieName = 'PHPSESSID'
}
- السطر 2: هذه المرة سيتم توفير ملف تعريف الارتباط للجلسة PHP لمُنشئ الطبقة [dao] لخادم البيانات؛
كيف سيتمكن الخادم [nuxt] من توفير ملف تعريف ارتباط الجلسة PHP إلى منشئ طبقته [dao]؟ سنقوم بتخزين ملف تعريف الارتباط للجلسة PHP في ملف تعريف الارتباط للجلسة [nuxt] الذي يتم تبادله بين المتصفح وخادم [nuxt]. وتتم العملية على النحو التالي:
- يتم تشغيل التطبيق [nuxt]؛
- عندما يقوم الخادم [nuxt] بإجراء طلبه الأول HTTP مع الخادم PHP، يقوم بتخزين ملف تعريف الارتباط الخاص بالجلسة PHP الذي تلقّاه في ملف تعريف الارتباط الخاص بالجلسة [nuxt] الذي يتبادله مع العميل [nuxt]؛
- المتصفح الذي يستضيف العميل [nuxt] يتلقى ملف تعريف الارتباط الخاص بالجلسة [nuxt]، وبالتالي يعيد إرساله بشكل منهجي مع كل طلب جديد إلى الخادم [nuxt]؛
- عندما يتعين على الخادم [nuxt] إرسال طلب جديد إلى الخادم PHP، سيجد ملف تعريف الارتباط للجلسة PHP في ملف تعريف الارتباط للجلسة [nuxt] الذي أرسله المتصفح إليه. ثم سيرسله إلى الخادم PHP؛
هناك ملفان لملفات تعريف الارتباط للجلسة ولا يجب الخلط بينهما:
- ملف تعريف الارتباط للجلسة [nuxt] الذي يتم تبادله بين الخادم [nuxt] ومتصفح العميل [nuxt]؛
- ملف تعريف الارتباط للجلسة PHP الذي يتم تبادله بين الخادم [nuxt] والخادم PHP أو بين العميل [nuxt] والخادم PHP؛
لنعد الآن إلى كود طريقة الفئة [Dao]. لا تتضمن هذه الطريقة دالة لإغلاق الجلسة PHP مع خادم حساب الضريبة. نضيف هذه الدالة:
// نهاية جلسة حساب الضريبة
async finSession() {
// خيارات الطلب HHTP [get /main.php?action=fin-session]
const options = {
method: 'GET',
// معلمات URL
params: {
action: 'fin-session'
}
}
// تنفيذ الطلب HTTP
const data = await this.getRemoteData(options)
// النتيجة
return data
}
عند الاختبار، نكتشف أن الدالة [getRemoteData] التي تم استدعاؤها في السطر 12 لا تناسب الطريقة [finSession]:
async getRemoteData(options) {
// لملف تعريف الارتباط الخاص بالجلسة
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = this.sessionCookie;
// تنفيذ الطلب HTTP
let response;
try {
// طلب غير متزامن
response = await this.axios.request('main.php', options);
} catch (error) {
// المعلمة [error] هي حالة استثنائية - يمكن أن تتخذ أشكالًا مختلفة
if (error.response) {
// رد الخادم موجود في [error.response]
response = error.response;
} else {
// يتم إعادة إرسال الخطأ
throw error;
}
}
// response هي مجمل استجابة الخادم HTTP (رؤوس HTTP + الاستجابة نفسها)
// يتم استرداد ملف تعريف ارتباط الجلسة إن وجد
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie هو مصفوفة
// يتم البحث عن ملف تعريف ارتباط الجلسة في هذا المصفوفة
let trouvé = false;
let i = 0;
while (!trouvé && i < setCookie.length) {
// نبحث عن ملف تعريف ارتباط الجلسة
const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// يتم حفظ ملف تعريف ارتباط الجلسة
// eslint-disable-next-line require-atomic-updates
this.sessionCookie = results[1];
// تم العثور عليه
trouvé = true;
} else {
// العنصر التالي
i++;
}
}
}
// رد الخادم موجود في [response.data]
return response.data;
}
- الأسطر 30-43: نبحث عن ملف تعريف الارتباط [PHPSESSID=xxx]. إذا تم العثور عليه، يتم تخزينه في الفئة (السطر 36)؛
هذا الرمز غير مناسب للطريقة الجديدة [finSession] لأن في الإجراء [fin-session]، يرسل الخادم PHP ملفين من ملفات تعريف الارتباط باسم [PHPSESSID]. فيما يلي مثال تم الحصول عليه باستخدام عميل [Postman]:

- في [1]، طلب العميل [Postman]؛
- إلى [3]، استجابة الخادم PHP؛
- في [4]، الرؤوس HTTP من رد الخادم PHP؛

- في [5]، يشير الخادم PHP أولاً إلى أنه قد حذف الجلسة الحالية PHP؛
- في [6]، يرسل الخادم PHP ملف تعريف الارتباط الخاص بالجلسة الجديدة PHP؛
مع الكود الحالي، تسترد الدالة [getRemoteData] ملف تعريف الارتباط [5] في حين أن ملف تعريف الارتباط [6] هو الذي يجب تخزينه.
لذلك، يجب تعديل كود الدالة [getRemoteData]:
async getRemoteData(options) {
// هل يوجد ملف تعريف ارتباط للجلسة PHP؟
if (this.phpSessionCookie) {
// هل توجد رؤوس؟
if (!options.headers) {
// يتم إنشاء كائن فارغ
options.headers = {}
}
// رأس ملف تعريف ارتباط الجلسة PHP
options.headers.Cookie = this.phpSessionCookie
}
// تنفيذ الطلب HTTP
let response
try {
// طلب غير متزامن
response = await this.axios.request('main.php', options)
} catch (error) {
// المعلمة [error] هي حالة استثنائية - يمكن أن تتخذ أشكالًا مختلفة
if (error.response) {
// رد الخادم موجود في [error.response]
response = error.response
} else {
// يتم إعادة إرسال الخطأ
throw error
}
}
// الاستجابة هي مجمل استجابة الخادم HTTP (رؤوس HTTP + الاستجابة نفسها)
// نبحث عن ملف تعريف ارتباط الجلسة PHP في ملفات تعريف الارتباط المستلمة
// جميع ملفات تعريف الارتباط المستلمة
const cookies = response.headers['set-cookie']
if (cookies) {
// ملفات تعريف الارتباط عبارة عن مصفوفة
// نبحث عن ملف تعريف ارتباط الجلسة PHP في هذا المصفوفة
let trouvé = false
let i = 0
while (!trouvé && i < cookies.length) {
// نبحث عن ملف تعريف الارتباط للجلسة PHP
const results = RegExp('^(' + this.phpSessionCookieName + '.+?)$').exec(cookies[i])
if (results) {
// يتم حفظ ملف تعريف الارتباط للجلسة PHP
const phpSessionCookie = results[1]
// هل توجد الكلمة [deleted] بداخله؟
const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
if (!results2) {
// لدينا ملف تعريف الارتباط الصحيح للجلسة PHP
this.phpSessionCookie = phpSessionCookie
// تم العثور على
trouvé = true
} else {
// العنصر التالي
i++
}
} else {
// العنصر التالي
i++
}
}
}
// رد الخادم موجود في [response.data]
return response.data
}
- السطر 41: تم العثور على ملف تعريف ارتباط باسم [PHPSESSID]. يتم تخزينه محليًا؛
- السطر 43: نتحقق مما إذا كان ملف تعريف الارتباط المحفوظ يحتوي على السلسلة [PHPSESSID=deleted]؛
- السطر 46: إذا كانت الإجابة لا، فهذا يعني أننا عثرنا على ملف تعريف الارتباط الصحيح [PHPSESSID]. نقوم بتخزينه في الفئة؛
بعد الدالة [getRemoteData]، يتم تخزين ملف تعريف الارتباط للجلسة PHP في الفئة، في [this.phpSessionCookie]. قلنا إن الفئة يتم إنشاء مثيل لها مع كل طلب جديد HTTP من الخادم [nuxt]. لذا يجب استخراج ملف تعريف الارتباط للجلسة PHP من الفئة. وللقيام بذلك، نضيف طريقة جديدة إليها:
// الوصول إلى ملف تعريف ارتباط الجلسة PHP
getPhpSessionCookie() {
return this.phpSessionCookie
}
- يطلب الخادم [nuxt] إجراءً من طبقته [dao] من خلال تزويد ملف تعريف الارتباط للجلسة PHP إلى مُنشئه، إن وجد؛
- بمجرد تنفيذ الإجراء، يسترد الخادم [nuxt] ملف تعريف الارتباط الخاص بجلسة العمل PHP الذي تم تخزينه بواسطة الطبقة [dao] باستخدام الطريقة [getPhpSessionCookie] السابقة. قد تكون ملف تعريف الارتباط هذا هو نفسه الملف السابق أو ملف آخر. تحدث الحالة الأخيرة في حالتين:
- أثناء تنفيذ الطريقة [initSession] (لم يكن هناك ملف تعريف ارتباط للجلسة PHP من قبل)؛
- عند تنفيذ الطريقة [finSession] (يقوم الخادم PHP بتغيير ملف تعريف الارتباط للجلسة PHP)؛
تجدر الإشارة إلى ميزة خاصة بملف تعريف الارتباط للجلسة PHP. لا يتلقى الخادم [nuxt] دائمًا ملف تعريف الارتباط هذا من الخادم PHP. في الواقع، لا يرسله هذا الأخير إلا مرة واحدة. وبعد ذلك لا يرسلها مرة أخرى. وعندما ننظر إلى كود [getRemoteData] وكود [getPhpSessionCookie]، نلاحظ أنه عندما لا يرسل الخادم PHP ملف تعريف ارتباط الجلسة، تقوم الدالة [getPhpSessionCookie] بإرجاع ملف تعريف ارتباط الجلسة PHP المقدم إلى المنشئ. وهكذا، يرسل الخادم دائمًا إلى الخادم PHP ملف تعريف الارتباط الأخير للجلسة PHP الذي أرسله إليه هذا الأخير.
15.5.2. الطبقة [dao] للعميل [nuxt]

بالنسبة للعميل [nuxt] الذي يعمل في متصفح، نستخدم كود فئة [Dao] من المستند |مقدمة إلى إطار العمل VUE.JS من خلال المثال|:
"use strict";
// الاستيرادات
import qs from "qs";
class Dao {
// المنشئ
constructor(axios) {
this.axios = axios;
}
// بدء الجلسة
async initSession() {
// خيارات الطلب HHTP [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// معلمات URL
params: {
action: "init-session",
type: "json"
}
};
// تنفيذ الاستعلام HTTP
return await this.getRemoteData(options);
}
async authentifierUtilisateur(user, password) {
// خيارات الاستعلام HHTP [post /main.php?action=authentifier-utilisateur]
const options = {
method: "POST",
headers: {
"Content-type": "application/x-www-form-urlencoded"
},
// نص POST
data: qs.stringify({
user: user,
password: password
}),
// معلمات URL
params: {
action: "authentifier-utilisateur"
}
};
// تنفيذ الاستعلام HTTP
return await this.getRemoteData(options);
}
async getAdminData() {
// خيارات الاستعلام HHTP [get /main.php?action=get-admindata]
const options = {
method: "GET",
// معلمات URL
params: {
action: "get-admindata"
}
};
// تنفيذ الطلب HTTP
const data = await this.getRemoteData(options);
// النتيجة
return data;
}
async getRemoteData(options) {
// تنفيذ الاستعلام HTTP
let response;
try {
// طلب غير متزامن
response = await this.axios.request("main.php", options);
} catch (error) {
// المعلمة [error] هي حالة استثنائية - يمكن أن تتخذ أشكالًا مختلفة
if (error.response) {
// رد الخادم موجود في [error.response]
response = error.response;
} else {
// يتم إعادة إرسال الخطأ
throw error;
}
}
// response هي مجمل استجابة الخادم HTTP (رؤوس HTTP + الاستجابة نفسها)
// رد الخادم موجود في [response.data]
return response.data;
}
}
// تصدير الفئة
export default Dao;
يختلف هذا الكود عن الطبقة [dao] للخادم [nuxt] في أنه لا يدير ملف تعريف الارتباط الخاص بجلسة العمل PHP مع خادم حساب الضريبة: بل يقوم المتصفح بذلك.
سنقوم، كما فعلنا مع الطبقة [dao] للخادم [nuxt]، بإضافة طريقة [finSession]:
// نهاية جلسة حساب الضريبة
async finSession() {
// خيارات الاستعلام HHTP [get /main.php?action=fin-session]
const options = {
method: 'GET',
// معلمات URL
params: {
action: 'fin-session'
}
}
// تنفيذ الطلب HTTP
const data = await this.getRemoteData(options)
// النتيجة
return data
}
عندما يقوم العميل [nuxt] بتنفيذ هذه الطريقة، يتلقى، مثل الخادم [nuxt]، ملفين من ملفات تعريف الارتباط للجلسة PHP. في الواقع، المتصفح هو الذي يستقبلهما ويقوم بإدارة الموقف بشكل صحيح: فهو يحتفظ فقط بملف تعريف الارتباط الخاص بجلسة العمل الجديدة PHP التي بدأها خادم حساب الضريبة. لذلك، عند الإجراء التالي للعميل [nuxt] تجاه الخادم PHP، سيكون ملف تعريف الارتباط للجلسة PHP صحيحًا لأن المتصفح هو الذي يرسله. ولكن هناك مشكلة: لا يعلم الخادم [nuxt] أن ملف تعريف الارتباط للجلسة PHP قد تغير. وفي تبادلاته مع الخادم PHP، سيرسل ملف تعريف ارتباط الجلسة PHP الذي لم يعد موجودًا، مما سيؤدي إلى حدوث مشاكل. يجب أن يقوم العميل [nuxt] بإخطار الخادم [nuxt] بأن ملف تعريف الارتباط للجلسة PHP قد تغير وأن يرسل له هذا الملف. ونعرف كيف يمكنه القيام بذلك: عبر ملف تعريف الارتباط للجلسة [nuxt]، وهو ملف تعريف الارتباط الذي يتم تبادله بين العميل والخادم [nuxt]. لدى العميل [nuxt] طريقتان على الأقل لاسترداد ملف تعريف الارتباط الجديد للجلسة PHP:
- عن طريق طلبه من المتصفح؛
- باستخدام طريقة [getRemoteData] الخاصة بالخادم الذي يعرف كيفية استرداد ملف تعريف الارتباط الجديد للجلسة PHP؛
سنستخدم الحل الثاني لأنه جاهز بالفعل. تصبح طريقة [getRemoteData] للعميل [nuxt] كما يلي:
async getRemoteData(options) {
// تنفيذ الاستعلام HTTP
let response
try {
// استعلام غير متزامن
response = await this.axios.request('main.php', options)
} catch (error) {
// المعلمة [error] هي حالة استثنائية - يمكن أن تتخذ أشكالًا مختلفة
if (error.response) {
// رد الخادم موجود في [error.response]
response = error.response
} else {
// يتم إعادة إرسال الخطأ
throw error
}
}
// response هي مجمل استجابة الخادم HTTP (رؤوس HTTP + الاستجابة نفسها)
// نبحث عن ملف تعريف ارتباط الجلسة PHP في ملفات تعريف الارتباط المستلمة
// جميع ملفات تعريف الارتباط المستلمة
const cookies = response.headers['set-cookie']
if (cookies) {
// ملفات تعريف الارتباط عبارة عن مصفوفة
// نبحث عن ملف تعريف الارتباط للجلسة PHP في هذا المصفوفة
let trouvé = false
let i = 0
while (!trouvé && i < cookies.length) {
// نبحث عن ملف تعريف الارتباط للجلسة PHP
const results = RegExp('^(' + this.phpSessionCookieName + '.+?)$').exec(cookies[i])
if (results) {
// يتم حفظ ملف تعريف الارتباط للجلسة PHP
const phpSessionCookie = results[1]
// هل توجد الكلمة [deleted] بداخله؟
const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
if (!results2) {
// لدينا ملف تعريف الارتباط الصحيح للجلسة PHP
this.phpSessionCookie = phpSessionCookie
// تم العثور عليه
trouvé = true
} else {
// العنصر التالي
i++
}
} else {
// العنصر التالي
i++
}
}
}
// رد الخادم موجود في [response.data]
return response.data
}
احتفظنا في [getRemoteData] فقط بالرمز الذي يستفيد من استجابة الخادم PHP للبحث عن ملف تعريف الارتباط للجلسة PHP. لم يتم الاحتفاظ بالرمز الذي تضمن ملف تعريف ارتباط الجلسة PHP في الطلب الموجه إلى الخادم PHP لأن المتصفح الذي يستضيف العميل [nuxt] هو الذي يتولى هذه المهمة.
بمجرد حصول العميل [nuxt] على ملف تعريف الارتباط للجلسة PHP، يجب وضعه في الجلسة [nuxt] حتى يتمكن الخادم [nuxt] من الاستفادة منه. ليست الطبقة [dao] هي التي تتولى ذلك، ولكنها تتيح الوصول عبر طريقة إلى ملف تعريف الارتباط للجلسة PHP الذي قامت بتخزينه:
// الوصول إلى ملف تعريف ارتباط الجلسة PHP
getPhpSessionCookie() {
return this.phpSessionCookie
}
لا تعرض الدالة [getPhpSessionCookie] دائمًا ملف تعريف ارتباط جلسة عمل صالحًا:
- يجب أن نتذكر هنا أن الطبقة [dao] للعميل [nuxt] هي طبقة ثابتة. يتم إنشاء مثيل لها مرة واحدة ثم تبقى في الذاكرة؛
- طالما أن الخادم PHP لا يرسل ملف تعريف ارتباط جلسة PHP إلى العميل [nuxt]، تقوم الدالة [getPhpSessionCookie] الخاصة بالعميل [nuxt] بإرجاع القيمة [undefined]؛
- عندما يرسل الخادم PHP ملف تعريف ارتباط الجلسة PHP إلى العميل [nuxt]، يتم تخزينه في [this.phpSessionCookie] وسيبقى كذلك إلى أن يتم استبداله بملف تعريف ارتباط جلسة جديد PHP مرسَل من الخادم PHP. ثم تقوم الوظيفة [getPhpSessionCookie] الخاصة بالعميل [nuxt] بإرجاع ملف تعريف الارتباط الخاص بالجلسة الأخير PHP الذي تم استلامه؛
لا تختلف طبقة [dao] للعميل [nuxt] عن تلك الخاصة بالخادم [nuxt] إلا في نقطة واحدة: فهي لا ترسل ملف تعريف الارتباط الخاص بالجلسة PHP بنفسها لأن المتصفح هو الذي يقوم بذلك. ومع ذلك، فقد فضلنا الاحتفاظ بطبقتين منفصلتين [dao] لأن الأسباب التي أدت إلى كتابة كل منهما مختلفة.
15.6. جلسة [nuxt]
![]()
سيتم تغليف الجلسة [nuxt] (بين العميل وخادم nuxt) في الكائن [session] التالي:
/* eslint-disable no-console */
// تعريف الجلسة
const session = {
// محتوى الجلسة
value: {
// المخزن غير مهيأ
initStoreDone: false,
// قيمة مخزن Vuex
store: ''
},
// حفظ الجلسة في ملف تعريف ارتباط
save(context) {
// حفظ المخزن في الجلسة
this.value.store = context.store.state
console.log('nuxt-session save=', this.value)
// حفظ قيمة الجلسة
context.app.$cookies.set('nuxt-session', this.value, { path: context.base, maxAge: context.env.maxAge })
},
// إعادة تعيين الجلسة
reset(context) {
console.log('nuxt-session reset')
// إعادة تعيين المخزن
context.store.commit('reset')
// حفظ المخزن الجديد في الجلسة وحفظ الجلسة
this.save(context)
}
}
// تصدير الجلسة
export default session
- الأسطر 5-10: تحتوي الجلسة على خاصية واحدة فقط هي [value] مع خاصيتين فرعيتين:
- [initStoreDone] التي تشير إلى ما إذا كان المخزن قد تم تهيئته أم لا؛
- [store]: القيمة [store.state] لمخزن Vuex الخاص بالتطبيق؛
- الأسطر 12-18: تُستخدم الطريقة [save] لحفظ الجلسة [nuxt] في ملف تعريف ارتباط. نستخدم هنا المكتبة [cookie-universal-nuxt] لإدارة ملف تعريف الارتباط. يُلاحظ اسم ملف تعريف الارتباط الخاص بجلسة العمل [nuxt]: [nuxt-session] (السطر 17)؛
- السطور 20-26: تعيد الطريقة [reset] تعيين الجلسة [nuxt]؛
- السطر 23: يتم إعادة تعيين مخزن Vuex ثم حفظه في الجلسة، السطر 25؛
15.7. مكونات إضافية لإدارة الجلسة [nuxt]

15.7.1. المكوّن الإضافي لإدارة الجلسة [nuxt] الخاص بـ serveur و [nuxt]
عند بدء تشغيل التطبيق، يكون الخادم [nuxt] هو أول من يعمل. وبالتالي، فهو الذي سيقوم بتهيئة الجلسة [nuxt]. النص البرمجي [server/plgSession] هو التالي:
/* eslint-disable no-console */
// استيراد الجلسة
import session from '@/entities/session'
export default (context, inject) => {
// إدارة جلسة عمل الخادم
console.log('[plugin server plgSession]')
// هل توجد جلسة عمل حالية؟
const value = context.app.$cookies.get('nuxt-session')
if (!value) {
// جلسة جديدة
console.log("[plugin server plgSession], démarrage d'une nouvelle session")
} else {
// جلسة عمل موجودة
console.log("[plugin server plgSession], reprise d'une session existante")
session.value = value
}
// يتم إدخال دالة في [context, Vue] تجعل الجلسة الحالية
inject('session', () => session)
}
- السطر 4: يتم استيراد رمز الجلسة [nuxt]؛
- السطر 11: يتم استرداد قيمة ملف تعريف الارتباط الخاص بجلسة العمل [nuxt]؛
- الأسطر 12-15: إذا لم يكن ملف تعريف الارتباط الخاص بالجلسة [nuxt] موجودًا، فإن الجلسة [nuxt] التي تم استيرادها في السطر 4 تكون كافية. ولا يوجد ما يمكن فعله أكثر من ذلك؛
- الأسطر 15-19: إذا كان ملف تعريف الارتباط الخاص بالجلسة [nuxt] موجودًا، فإننا نقوم في السطر 18 بتخزين قيمته في الجلسة المستوردة في السطر 4؛
- السطر 22: تم تهيئة الجلسة أو استعادتها. يتم إتاحتها عبر الدالة [$session]؛
15.7.2. المكوّن الإضافي لإدارة الجلسة [nuxt] للعميل [nuxt]
النص البرمجي [client/plgSession] هو التالي:
/* eslint-disable no-console */
// استيراد الجلسة
import session from '@/entities/session'
export default (context, inject) => {
// إدارة جلسة عمل العميل
console.log('[plugin client plgSession], reprise de la session [nuxt] du serveur')
// استرداد الجلسة الحالية من خادم nuxt
session.value = context.app.$cookies.get('nuxt-session')
// نقوم بإدخال دالة في [context, Vue] تجعل الجلسة الحالية
inject('session', () => session)
}
- السطر 4: يتم استيراد الجلسة [nuxt]؛
- السطر 10: يتم استرداد الجلسة الحالية [nuxt] من ملف تعريف الارتباط [nuxt-session]؛
- السطر 13: يتم عرض الجلسة [nuxt] التي تم استيرادها في السطر 4 من خلال الدالة المُدرجة [$session]؛
15.8. المكونات الإضافية للطبقات [dao]

15.8.1. ملحق الطبقة [dao] للعميل [nuxt]
النص البرمجي [client/plgDao] هو التالي:
/* eslint-disable no-console */
// يتم إنشاء نقطة وصول إلى الطبقة [Dao]
import Dao from '@/api/client/Dao'
export default (context, inject) => {
// تكوين axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
context.$axios.defaults.withCredentials = context.env.withCredentials
// إنشاء مثيل للطبقة [dao]
const dao = new Dao(context.$axios)
// إدخال دالة [$dao] في السياق
inject('dao', () => dao)
// سجل
console.log('[fonction client $dao créée]')
}
- السطر 3: يتم استيراد الطبقة [dao] للعميل [nuxt]؛
- الأسطر 6-8: يتم تكوينالكائن [context.$axios] الذي سيقوم بإجراء الاستعلامات HTTP من الطبقة [dao] للعميل [nuxt] باستخدام معلومات الملف [nuxt.config]:
// البيئة
env: {
// تكوين axios
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
// تكوين ملف تعريف ارتباط الجلسة [nuxt]
maxAge: 60 * 5
}
- السطر 10: يتم إنشاء مثيل للطبقة [dao] للعميل [nuxt]؛
- السطر 12: يتم إدخال الدالة [$dao] في سياق العميل وصفحاته. تتيح هذه الدالة الوصول إلى الطبقة [dao] في السطر 10؛
لذلك، يجب ملاحظة أنه للوصول إلى الطبقة [dao] للعميل [nuxt] عند تنفيذه، سنكتب:
- [context.app.$dao()] حيث يكون السياق معروفًا؛
- [this.$dao()] في صفحة [Vue.js]؛
15.8.2. المكوّن الإضافي للطبقة [dao] من serveur [nuxt]
النص البرمجي [server/plgDao] هو التالي:
/* eslint-disable no-console */
// يتم إنشاء نقطة وصول إلى الطبقة [Dao]
import Dao from '@/api/server/Dao'
export default (context, inject) => {
// تكوين axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
// استرداد ملف تعريف ارتباط الجلسة
const store = context.app.$session().value.store
const phpSessionCookie = store ? store.phpSessionCookie : ''
console.log('session=', context.app.$session().value, 'phpSessionCookie=', phpSessionCookie)
// إنشاء مثيل للطبقة [dao]
const dao = new Dao(context.$axios, phpSessionCookie)
// إدخال دالة [$dao] في السياق
inject('dao', () => dao)
// تسجيل
console.log('[fonction server $dao créée]')
}
- السطر 3: يتم استيراد الطبقة [dao] من الخادم [nuxt]؛
- السطران 6-7: يتم تكوينالكائن [context.$axios] الذي سيقوم بإجراء طلبات HTTP من الطبقة [dao] للخادم [nuxt] باستخدام معلومات الملف [nuxt.config]:
// البيئة
env: {
// تكوين axios
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
// تكوين ملف تعريف ارتباط الجلسة [nuxt]
maxAge: 60 * 5
}
- السطر 9: يتم استرداد مخزن التطبيق [nuxt]؛
- السطر 10: إذا كان المتجر موجودًا، يتم استرداد ملف تعريف الارتباط للجلسة PHP لأننا نحتاج إليه لإنشاء مثيل للطبقة [dao] من الخادم [nuxt]؛
- السطر 13: يتم إنشاء مثيل للطبقة [dao] للخادم [nuxt]؛
- السطر 15: يتم إدخال الدالة [$dao] في سياق وصفحات الخادم [nuxt]. تتيح هذه الدالة الوصول إلى الطبقة [dao] في السطر 13؛
لذلك، يجب أن نتذكر أنه للوصول إلى الطبقة [dao] للخادم [nuxt] عند تشغيله، سنكتب:
- [context.app.$dao()] حيث يكون السياق معروفًا؛
- [this.$dao()] في صفحة [Vue.js]؛
15.9. متجر Vuex
![]()
سيخزن مخزن [Vuex] جميع البيانات التي يجب مشاركتها بين المكونات المختلفة للتطبيق [pages, client, serveur] دون أن تكون هذه البيانات تفاعلية.
/* eslint-disable no-console */
// حالة المتجر
export const state = () => ({
// تم بدء جلسة jSON
jsonSessionStarted: false,
// تم توثيق المستخدم
userAuthenticated: false,
// ملف تعريف ارتباط الجلسة PHP
phpSessionCookie: '',
// adminData
adminData: ''
})
// تغييرات في المخزن
export const mutations = {
// استبدال الحالة
replace(state, newState) {
for (const attr in newState) {
state[attr] = newState[attr]
}
},
// إعادة تعيين المخزن
reset() {
this.commit('replace', { jsonSessionStarted: false, userAuthenticated: false, phpSessionCookie: '', adminData: '' })
}
}
// إجراءات المخزن
export const actions = {
nuxtServerInit(store, context) {
// من ينفذ هذا الرمز؟
console.log('nuxtServerInit, client=', process.client, 'serveur=', process.server, 'env=', context.env)
// بدء الجلسة
initStore(store, context)
}
}
function initStore(store, context) {
// store هي الجلسة المطلوب تهيئتها
// استرداد الجلسة
const session = context.app.$session()
// هل تم تهيئة الجلسة بالفعل؟
if (!session.value.initStoreDone) {
// يتم بدء مخزن جديد
console.log("nuxtServerInit, initialisation d'une nouvelle session")
// يتم وضع المخزن في الجلسة
session.value.store = store.state
// تم تهيئة المخزن الآن
session.value.initStoreDone = true
} else {
console.log("nuxtServerInit, reprise d'un store existant")
// يتم تحديث المخزن بمخزن الجلسة
store.commit('replace', session.value.store)
}
// يتم حفظ الجلسة
session.save(context)
// سجل
console.log('initStore terminé, store=', store.state)
}
البيانات المخزنة في المخزن هي التالية:
- السطر 6: سيتم تعيين [jsonSessionStarted] على "صحيح" بمجرد نجاح تهيئة جلسة jSON مع الخادم PHP، سواء تمت من قبل العميل أو الخادم [nuxt]. عند انتهاء عملية التهيئة هذه، سيتم استرداد ملف تعريف الارتباط الخاص بالجلسة مع الخادم PHP ووضعه في الخاصية [phpSessionCookie]، السطر 10؛
- السطر 8: سيتم تعيين [userAuthenticated] على "صحيح" بمجرد نجاح المصادقة مع الخادم PHP، سواء تمت من قبل العميل أو الخادم [nuxt]؛
- السطر 12: ستكون [adminData] هي القيمة [adminData] التي تم الحصول عليها من الخادم PHP بمجرد نجاح المصادقة؛
- الأسطر 18-22: تسمح عملية التغيير [replace] بتهيئة الخصائص السابقة باستخدام خصائص كائن تم تمريره كمعلمة؛
- الأسطر 24-26: تعيد عملية التغيير [reset] القيم الأولية لخصائص المخزن؛
- الأسطر 31-37: تقوم الدالة [nuxtServerInit] بتفويض مهمتها إلى الدالة [initStore]؛
- الأسطر 39-60: الدالة [initStore] لها دوران:
- إذا لم يتم تهيئة المخزن، يتم تهيئته ووضعه في الجلسة؛
- إذا تم تهيئة المخزن بالفعل، يتم استرداد قيمته في الجلسة [nuxt]؛
- السطر 42: يتم استرداد الجلسة nuxt؛
- السطر 44: يتم التحقق مما إذا كان المخزن قد تم تهيئته:
- إذا لم يكن الأمر كذلك، يتم وضع المخزن الأولي في الجلسة (السطر 48)؛
- ثم في السطر 50، نشير إلى أن المخزن قد تم تهيئته؛
- الأسطر 51-55: إذا تم تهيئة المخزن، يتم استخدامه، في السطر 54، لتهيئة المخزن بالقيمة الموجودة في الجلسة؛
- السطر 57: في جميع الأحوال، يتم حفظ الجلسة في ملف تعريف الارتباط [nuxt-session]، مع المخزن الذي تحتوي عليه؛
15.10. المكوّن الإضافي [plgEventBus]

يهدف هذا المكون الإضافي إلى إتاحة ناقل الأحداث للعميل [nuxt] عبر وظيفة [$eventBus] يتم إدخالها في سياق العميل [nuxt]. لا فائدة من إدخالها في سياق الخادم [nuxt] لأن هذا الأخير لا يستطيع إدارة الأحداث. ومع ذلك، فقد رأينا بالفعل أن إدخالها على جانب الخادم ثم استخدامها لا يسبب أي خطأ.
/* eslint-disable no-console */
// يتم إنشاء ناقل أحداث بين العروض
import Vue from 'vue'
export default (context, inject) => {
// ناقل الأحداث
const eventBus = new Vue()
// إدخال دالة [$eventBus] في السياق
inject('eventBus', () => eventBus)
// السجل
console.log('[fonction $eventBus créée]')
}
لقد سبق أن تناولنا هذا المكون الإضافي في الفقرة "الرابط". ستكون الدالة [$eventBus] متاحة للعميل عبر الترميزات:
- [context.app.$eventBus()] حيثما كان السياق متاحًا؛
- [this.$eventBus()] في صفحات [Vue.js] الخاصة بالعميل؛
15.11. مكونات تطبيق [nuxt]

المكون [layout] هو المكون الموجود في الأمثلة السابقة:
<!-- تنسيق العروض -->
<template>
<!-- السطر -->
<div>
<b-row>
<!-- منطقة ذات ثلاثة أعمدة -->
<b-col v-if="left" cols="3">
<slot name="left" />
</b-col>
<!-- منطقة ذات تسعة أعمدة -->
<b-col v-if="right" cols="9">
<slot name="right" />
</b-col>
</b-row>
</div>
</template>
<script>
export default {
// الإعدادات
props: {
left: {
type: Boolean
},
right: {
type: Boolean
}
}
}
</script>
المكون [navigation] هو التالي:
<template>
<!-- قائمة Bootstrap ذات الخيارات الثلاثة -->
<b-nav vertical>
<b-nav-item to="/authentification" exact exact-active-class="active">
Authentification
</b-nav-item>
<b-nav-item to="/get-admindata" exact exact-active-class="active">
Requête AdminData
</b-nav-item>
<b-nav-item to="/fin-session" exact exact-active-class="active">
Fin session impôt
</b-nav-item>
</b-nav>
</template>
15.12. تخطيطات التطبيق [nuxt]

15.12.1. [default]
التخطيط [default] هو الذي تم استخدامه في المثال [nuxt-11] في الفقرة الرابط:
<template>
<div class="container">
<b-card>
<!-- رسالة -->
<b-alert show variant="success" align="center">
<h4>[nuxt-12] : requêtes HTTP avec axios</h4>
</b-alert>
<!-- عرض التوجيه الحالي -->
<nuxt />
<!-- رسالة انتظار -->
<b-alert v-if="showLoading" show variant="light">
<strong>Requête au serveur de données en cours...</strong>
<div class="spinner-border ml-auto" role="status" aria-hidden="true"></div>
</b-alert>
<!-- خطأ في عملية غير متزامنة -->
<b-alert v-if="showErrorLoading" show variant="danger">
<strong>La requête au serveur de données a échoué : {{ errorLoadingMessage }}</strong>
</b-alert>
</b-card>
</div>
</template>
<script>
/* eslint-disable no-console */
export default {
name: 'App',
data() {
return {
showLoading: false,
showErrorLoading: false
}
},
// دورة الحياة
beforeCreate() {
console.log('[default beforeCreate]')
},
created() {
console.log('[default created]')
if (process.client) {
// نستمع إلى الحدث [loading]
this.$eventBus().$on('loading', this.mShowLoading)
// وكذلك الحدث [errorLoadingMessage]
this.$eventBus().$on('errorLoading', this.mShowErrorLoading)
}
},
beforeMount() {
console.log('[default beforeMount]')
},
mounted() {
console.log('[default mounted]')
},
methods: {
// إدارة رسالة الانتظار
mShowLoading(value) {
console.log('[default mShowLoading], showLoading=', value)
this.showLoading = value
},
// خطأ في عملية غير متزامنة
mShowErrorLoading(value, errorLoadingMessage) {
console.log('[default mShowErrorLoading], showErrorLoading=', value, 'errorLoadingMessage=', errorLoadingMessage)
this.showErrorLoading = value
this.errorLoadingMessage = errorLoadingMessage
}
}
}
</script>
- الأسطر 10-14: تعرض رسالة انتظار انتهاء عملية غير متزامنة للعميل [nuxt]؛
- الأسطر 15-18: تعرض رسالة الخطأ المحتملة لعملية غير متزامنة؛
- السطر 37: يتم تنفيذ الدالة [created] للصفحة [default] قبل الدالة [mounted] للصفحات؛
- السطر 39: إذا كان المنفذ هو العميل [nuxt]، فإن الصفحة [default] تبدأ في الاستماع إلى الأحداث:
- [loading] التي تشير إلى بداية أو نهاية فترة انتظار. ثم يتم تنفيذ الدالة [mShowLoading]؛
- [errorLoading] التي تشير إلى ضرورة عرض رسالة خطأ. عندئذ يتم تنفيذ الدالة [mShowErrorLoading]؛
- الصفحات [nuxt]:
- تعرض رسالة الانتظار عن طريق إرسال الحدث [‘loading’, true] على ناقل الأحداث؛
- تخفي رسالة الانتظار عن طريق إرسال الحدث [‘loading’, false] على ناقل الأحداث؛
- تعرض رسالة خطأ عن طريق إرسال الحدث [‘errorLoading’, true] على ناقل الأحداث؛
- إخفاء رسالة الخطأ عن طريق إرسال الحدث [‘errorLoading’, false] على ناقل الأحداث؛
15.12.2. [error]
يعرض التخطيط [error] رسالة خطأ في النظام (لا يديرها المطور):
<!-- تعريف HTML للعرض -->
<template>
<!-- تنسيق الصفحة -->
<Layout :left="true" :right="true">
<!-- تنبيه في العمود الأيمن -->
<template slot="right">
<!-- رسالة على خلفية وردية -->
<b-alert show variant="danger" align="center">
<h4>L'erreur suivante s'est produite : {{ JSON.stringify(error) }}</h4>
</b-alert>
</template>
<!-- قائمة التنقل في العمود الأيسر -->
<Navigation slot="left" />
</Layout>
</template>
<script>
/* eslint-disable no-undef */
/* eslint-disable no-console */
/* eslint-disable nuxt/no-env-in-hooks */
import Layout from '@/components/layout'
import Navigation from '@/components/navigation'
export default {
name: 'Error',
// المكونات المستخدمة
components: {
Layout,
Navigation
},
// خاصية [props]
props: { error: { type: Object, default: () => 'waiting ...' } },
// دورة الحياة
beforeCreate() {
// العميل والخادم
console.log('[error beforeCreate]')
},
created() {
// العميل والخادم
console.log('[error created, error=]', this.error)
},
beforeMount() {
// العميل فقط
console.log('[error beforeMount]')
},
mounted() {
// العميل فقط
console.log('[error mounted]')
}
}
</script>
15.13. الصفحة [index] التي يتم تنفيذها بواسطة الخادم [nuxt]

تتميز الصفحة [index.vue] بأنها لا يمكن الوصول إليها إلا عبر الخادم [nuxt]. لا يظهر للمستخدم أي رابط للوصول إليها عبر العميل [nuxt]. رمزها هو التالي:
<!-- الصفحة الرئيسية -->
<template>
<Layout :left="true" :right="true">
<!-- التنقل -->
<Navigation slot="left" />
<!-- رسالة-->
<b-alert slot="right" show variant="warning">Initialisation de la session avec le serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'InitSession',
// المكونات المستخدمة
components: {
Layout,
Navigation
},
// بيانات غير متزامنة
async asyncData(context) {
// سجل
console.log('[index asyncData started]')
try {
// بدء جلسة jSON
const dao = context.app.$dao()
const response = await dao.initSession()
// السجل
console.log('[index asyncData response=]', response)
// استرداد ملف تعريف ارتباط الجلسة PHP للاستعلامات القادمة
const phpSessionCookie = dao.getPhpSessionCookie()
// يتم حفظ ملف تعريف ارتباط الجلسة PHP في الجلسة [nuxt]
context.store.commit('replace', { phpSessionCookie })
// هل حدث خطأ؟
if (response.état !== 700) {
// الخطأ موجود في response.réponse
throw new Error(response.réponse)
}
// يُلاحظ أن الجلسة jSON قد بدأت
context.store.commit('replace', { jsonSessionStarted: true })
// يتم إرجاع النتيجة
return { result: '[succès]' }
} catch (e) {
// السجل
console.log('[index asyncData error=]', e)
// يتم تسجيل أن الجلسة jSON لم تبدأ
context.store.commit('replace', { jsonSessionStarted: false })
// يتم الإبلاغ عن الخطأ
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// يتم حفظ المخزن
const session = context.app.$session()
session.save(context)
// سجل
console.log('[index asyncData finished]')
}
},
// دورة الحياة
beforeCreate() {
console.log('[index beforeCreate]')
},
created() {
console.log('[index created]')
},
beforeMount() {
console.log('[index beforeMount]')
},
mounted() {
console.log('[index mounted]')
// العميل فقط
if (this.showErrorLoading) {
console.log('[index mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
- السطر 7: تعرض الصفحة نتيجة [result] لطلب غير متزامن (السطران 46 و51)؛
- السطر 31: العملية غير المتزامنة هي فتح جلسة عمل jSON مع خادم حساب الضريبة؛
- السطر 25: من المعروف أنه عندما يتم طلب الصفحة مباشرة من الخادم [nuxt]، لا يتم تنفيذ الدالة [asyncData] إلا بواسطة الخادم وليس بواسطة العميل [nuxt] الذي يتم تنفيذه عندما يتلقى المتصفح الرد من الخادم [nuxt]؛
- السطر 30: يتم استرداد الطبقة [dao] في سياق الخادم [nuxt]؛
- السطر 35: إذا لم يكن الخادم قد أرسل بعد طلبًا إلى خادم حساب الضريبة، فإنه يتلقى ملف تعريف الارتباط الأول للجلسة PHP، وإلا فإنه يتلقى ملف تعريف الارتباط الأخير للجلسة PHP الذي تلقّاه (راجع كود الطبقة [dao] للخادم [nuxt] في الفقرة الرابط)؛
- السطر 37: يتم تخزين ملف تعريف الارتباط هذا PHP في المخزن؛
- الأسطر 39-42: يتم التحقق مما إذا كانت العملية قد نجحت. إذا لم يكن الأمر كذلك، يتم إطلاق استثناء سيتم اعتراضه بواسطة [catch] في السطر 47؛
- السطر 44: يتم تسجيل في المخزن أن الجلسة jSON مع الخادم PHP قد بدأت؛
- السطر 46: يتم إرجاع النتيجة [result] التي يتم عرضها في السطر 7؛
- السطور 47-54: يتم معالجة أي استثناء محتمل. قد يكون هذا الاستثناء من نوعين:
- فشلت العملية HTTP في السطر 31 بسبب خطأ في الاتصال بين الخادم [nuxt] والخادم PHP؛
- نجحت العملية HTTP في السطر 31 ولكن النتيجة المستلمة أشارت إلى وجود خطأ (السطور 39-42)؛
- السطر 51: نلاحظ أن الجلسة jSON مع الخادم PHP لم تبدأ؛
- السطر 53: يتم إرجاع النتيجة [result] التي يتم عرضها في السطر 7. بالإضافة إلى ذلك، يتم تعيين الخصائص [showErrorLoading] و [errorLoadingMessage] التي سيستخدمها العميل [nuxt] لعرض رسالة خطأ عند استلامه للصفحة المرسلة من الخادم [nuxt] (الأسطر 72-79)؛
- الأسطر 54-60: كود يتم تنفيذه في جميع الحالات (سواء نجح الأمر أم فشل)؛
- السطر 56: يتم استرداد الجلسة [nuxt] في سياق الخادم [nuxt]؛
- السطر 57: يتم حفظها؛
- الأسطر 63-68: بمجرد انتهاء الدالة [asyncData]، يقوم الخادم [nuxt] بتنفيذ الدالتين [beforeCreate] و [create]؛
ملاحظة: قد يفشل تنفيذ الصفحة [index] بواسطة الخادم [nuxt]، على سبيل المثال، إذا لم يتم تشغيل خادم حساب الضريبة عند تشغيل التطبيق [nuxt]:

في هذه الحالة، الحل الوحيد هو تشغيل خادم حساب الضريبة ثم التطبيق [nuxt] نفسه، حيث لا توفر قائمة التنقل خيارًا لبدء جلسة jSON مع خادم حساب الضريبة؛
15.14. الصفحة [index] التي يتم تنفيذها بواسطة العميل [nuxt]
لا يتم تنفيذ الصفحة [index] بواسطة العميل [nuxt] إلا بعد أن يرسلها إليه الخادم [nuxt]. وقد أرسل له الخادم المعلومات [result] وربما [showErrorLoading] و [errorLoadingMessage].
من المعروف أن الدالة [asyncData] لن يتم تنفيذها. وبذلك تبقى دورات الحياة، ولا سيما الدالة [mounted]:
mounted() {
console.log('[index mounted]')
// العميل فقط
if (this.showErrorLoading) {
console.log('[index mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
- يقوم العميل [nuxt] تلقائيًا بدمج العناصر [result] وربما [showErrorLoading, errorLoadingMessage] التي أرسلها إليه الخادم [nuxt] في خصائص الصفحة:
- يتم عرض الخاصية [result] في السطر 7؛
- يتم استخدام خصائص [showErrorLoading, errorLoadingMessage] بواسطة الطريقة [mounted]: في السطر 4، يتم اختبار الخاصية [showErrorLoading]. إذا كانت صحيحة، يتم استخدام ناقل أحداث العميل [nuxt] في السطر 6 للإشارة إلى وجود رسالة خطأ يجب عرضها؛
- يتم اعتراض الحدث [errorLoading] الذي تم إطلاقه في السطر 6 بواسطة الصفحة [layouts/default] الموضحة في الفقرة الرابط؛
15.15. الصفحة [authentification] التي يتم تنفيذها بواسطة الخادم [nuxt]
الصفحة [authentification] مكلفة بتحديد هوية المستخدم لدى خادم حساب الضريبة. رمزها هو التالي:
<!-- صفحة المصادقة -->
<template>
<Layout :left="true" :right="true">
<!-- التنقل -->
<Navigation slot="left" />
<!-- رسالة-->
<b-alert slot="right" show variant="warning">Authentification auprès du serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Authentification',
// المكونات المستخدمة
components: {
Layout,
Navigation
},
// بيانات غير متزامنة
async asyncData(context) {
// سجل
console.log('[authentification asyncData started]')
if (process.client) {
// بدء انتظار العميل [nuxt]
context.app.$eventBus().$emit('loading', true)
// لا توجد أخطاء
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// يتم المصادقة لدى الخادم
const dao = context.app.$dao()
const response = await dao.authentifierUtilisateur('admin', 'admin')
// سجل
console.log('[authentification asyncData response=]', response)
// النتيجة
const userAuthenticated = response.état === 200
// يتم تسجيل ما إذا كان المستخدم قد تمت مصادقته أم لا
context.store.commit('replace', { userAuthenticated })
// يتم حفظ المخزن في الجلسة [nuxt]
const session = context.app.$session()
session.save(context)
// خطأ في المصادقة؟
if (!userAuthenticated) {
// الخطأ موجود في response.réponse
throw new Error(response.réponse)
}
// يتم إرجاع النتيجة
return { result: '[succès]' }
} catch (e) {
// يتم الإبلاغ عن الخطأ
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// سجل
console.log('[authentification asyncData finished]')
if (process.client) {
// نهاية انتظار العميل [nuxt]
context.app.$eventBus().$emit('loading', false)
}
}
},
// دورة الحياة
beforeCreate() {
console.log('[authentification beforeCreate]')
},
created() {
console.log('[authentification created]')
},
beforeMount() {
console.log('[authentification beforeMount]')
},
mounted() {
console.log('[authentification mounted]')
// العميل فقط
if (this.showErrorLoading) {
console.log('[authentification mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
- السطر 7: تعرض الصفحة نتيجة [result] للطلب غير المتزامن [asyncData] من الأسطر 25-65؛
- الأسطر 28-33: لا يقوم الخادم بتنفيذ هذه الأسطر المخصصة للعميل [nuxt]؛
- السطر 36: يتم استرداد الطبقة [dao] من الخادم [nuxt]؛
- السطر 37: يتم المصادقة لدى خادم حساب الضريبة باستخدام معرفات الاختبار [admin, admin]، وهي المعرفات الوحيدة التي يقبلها خادم حساب الضريبة؛
- السطر 41: تنجح عملية المصادقة فقط إذا كانت الاستجابة بالحالة 200؛
- السطر 43: يتم وضع الخاصية [userAuthenticated] في المخزن؛
- الأسطر 44-46: يتم حفظ المخزن في الجلسة [nuxt]؛
- الأسطر 48-51: إذا فشلت المصادقة، يتم إلقاء استثناء مع رسالة الخطأ التي أرسلها خادم حساب الضريبة؛
- وإلا، في السطر 53، يتم إرجاع نتيجة نجاح سيتم عرضها في السطر 7؛
- الأسطر 54-57: في حالة حدوث خطأ، يتم تعيين ثلاث خصائص للصفحة [result, showErrorLoading, errorLoadingMessage]. سيتم عرض الخاصية [result] في السطر 7. سيتم إرسال الخصائص الثلاث إلى العميل [nuxt]؛
- الأسطر 60-63: لا يتم تنفيذها بواسطة الخادم [nuxt]؛
- بمجرد أن يعرض [asyncData] نتيجته، يتم عرضها في السطر 7. ثم يتم تنفيذ الطرق [beforeCreate] (السطور 67-69) و [created] (السطور 70-72)؛
- انتهى الأمر؛
ملاحظة: قد يفشل تنفيذ الصفحة [authentification] بواسطة الخادم [nuxt]، على سبيل المثال، إذا لم يتم تهيئة الجلسة jSON مع خادم حساب الضريبة. يمكن القيام بذلك بالطريقة التالية:
- احذف ملف تعريف الارتباط للجلسة PHP من متصفحك (للبدء من الصفر):

- قم بتشغيل التطبيق [nuxt] في حين أن خادم الحساب لم يتم تشغيله: ستحصل على خطأ؛
- قم بتشغيل خادم حساب الضريبة؛
- اطلب URL [/authentification] مباشرة في شريط عناوين المتصفح:

في هذه الحالة، الحل الوحيد هو إعادة تحميل الصفحة [index].
15.16. الصفحة [authentification] التي يتم تنفيذها بواسطة العميل [nuxt]
لنعد إلى كود الصفحة:
<!-- صفحة المصادقة -->
<template>
<Layout :left="true" :right="true">
<!-- التنقل -->
<Navigation slot="left" />
<!-- رسالة-->
<b-alert slot="right" show variant="warning">Authentification auprès du serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Authentification',
// المكونات المستخدمة
components: {
Layout,
Navigation
},
// بيانات غير متزامنة
async asyncData(context) {
// سجل
console.log('[authentification asyncData started]')
if (process.client) {
// بدء انتظار العميل [nuxt]
context.app.$eventBus().$emit('loading', true)
// لا توجد أخطاء
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// يتم المصادقة لدى الخادم
const dao = context.app.$dao()
const response = await dao.authentifierUtilisateur('admin', 'admin')
// سجل
console.log('[authentification asyncData response=]', response)
// النتيجة
const userAuthenticated = response.état === 200
// يتم تسجيل ما إذا كان المستخدم قد تمت مصادقته أم لا
context.store.commit('replace', { userAuthenticated })
// يتم حفظ المخزن في الجلسة [nuxt]
const session = context.app.$session()
session.save(context)
// خطأ في المصادقة؟
if (!userAuthenticated) {
// الخطأ موجود في response.réponse
throw new Error(response.réponse)
}
// يتم عرض النتيجة
return { result: '[succès]' }
} catch (e) {
// يتم الإبلاغ عن الخطأ
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// سجل
console.log('[authentification asyncData finished]')
if (process.client) {
// نهاية انتظار العميل [nuxt]
context.app.$eventBus().$emit('loading', false)
}
}
},
// دورة الحياة
beforeCreate() {
console.log('[authentification beforeCreate]')
},
created() {
console.log('[authentification created]')
},
beforeMount() {
console.log('[authentification beforeMount]')
},
mounted() {
console.log('[authentification mounted]')
// العميل فقط
if (this.showErrorLoading) {
console.log('[authentification mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
هناك حالتان لتنفيذ الصفحة [authentification] بواسطة العميل [nuxt]:
- يتم تنفيذ العميل [nuxt] بعد أن يرسل الخادم [nuxt] إلى متصفح العميل [nuxt] الصفحة [authentification]؛
- العميل [nuxt] لأن المستخدم نقر على الرابط [Authentification] في قائمة التنقل:

لندرس أولاً الحالة الأولى. في هذه الحالة، لا يقوم العميل [nuxt] بتنفيذ الدالة [asyncData]. وهو يدمج في خصائص الصفحة العناصر [result] وربما [showErrorLoading, errorLoadingMessage] التي أرسلها إليه الخادم [nuxt]:
- يتم عرض الخاصية [result] في السطر 7؛
- يتم استخدام الخصائص [showErrorLoading, errorLoadingMessage] بواسطة الطريقة [mounted]: في السطر 79، يتم اختبار الخاصية [showErrorLoading]. إذا كانت صحيحة، يتم استخدام ناقل أحداث العميل [nuxt] في السطر 81 للإشارة إلى وجود رسالة خطأ يجب عرضها؛
تم شرح آلية عرض رسالة الخطأ للصفحة [index] في الفقرة "الرابط".
الحالة 2 هي حالة العميل [nuxt] الذي يتم تنفيذه عندما ينقر المستخدم على الرابط [Authentification]. في هذه الحالة، يتم تشغيل العميل [nuxt] بشكل مستقل وليس بعد الخادم [nuxt]. ثم يتم تنفيذ الدالة [asyncData]. نقدم فقط التفاصيل التي تختلف عن التفسيرات المقدمة للصفحة التي يتم تنفيذها بواسطة الخادم [nuxt]:
- الأسطر 28-33: يطلب العميل [nuxt] عرض رسالة الانتظار وإخفاء أي رسالة خطأ قد تكون ظهرت سابقًا؛
- السطر 36: يتم الآن الحصول على الطبقة [dao] للعميل [nuxt] هنا؛
- الأسطر 60-63: يطلب العميل [nuxt] إنهاء عرض رسالة الانتظار؛
- بمجرد انتهاء [asyncData]، سيبدأ دورة حياة الصفحة. سيتم تنفيذ الدالة [mounted] في الأسطر 76-83. إذا حدث خطأ، فسيتم عرض رسالة الخطأ؛
ملاحظة: لإحداث خطأ، اتبع الإجراء الموضح للخادم [nuxt] في نهاية الفقرة الرابط، ولكن بدلاً من طلب الصفحة [authentification] عن طريق كتابة URL في شريط العناوين، استخدم الرابط [Authentification] من قائمة التنقل. عندها سيتم تشغيل العميل [nuxt].
15.17. الصفحة [get-admindata]
رمز الصفحة [get-admindata] هو التالي:
<!-- عرض get-admindata -->
<template>
<Layout :left="true" :right="true">
<!-- التنقل -->
<Navigation slot="left" />
<!-- رسالة -->
<b-alert slot="right" show variant="secondary"> Demande de [adminData] au serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'GetAdmindata',
// المكونات المستخدمة
components: {
Layout,
Navigation
},
// بيانات غير متزامنة
async asyncData(context) {
// سجل
console.log('[get-admindata asyncData started]')
if (process.client) {
// بداية الانتظار
context.app.$eventBus().$emit('loading', true)
// لا توجد أخطاء
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// يتم طلب البيانات [admindata]
const response = await context.app.$dao().getAdminData()
// سجل
console.log('[get-admindata asyncData response=]', response)
// النتيجة
const adminData = response.état === 1000 ? response.réponse : ''
// يتم وضع البيانات في المخزن
context.store.commit('replace', { adminData })
// يتم حفظ المخزن في الجلسة [nuxt]
const session = context.app.$session()
session.save(context)
// هل حدث خطأ؟
if (!adminData) {
// الخطأ موجود في response.réponse
throw new Error(response.réponse)
}
// يتم إرجاع القيمة المستلمة
return { result: adminData }
} catch (e) {
// يتم الإبلاغ عن الخطأ
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// سجل
console.log('[get-admindata asyncData finished]')
if (process.client) {
// نهاية الانتظار
context.app.$eventBus().$emit('loading', false)
}
}
},
// دورة الحياة
beforeCreate() {
console.log('[get-admindata beforeCreate]')
},
created() {
console.log('[get-admindata created]')
},
beforeMount() {
console.log('[get-admindata beforeMount]')
},
mounted() {
console.log('[get-admindata mounted]')
// العميل
if (this.showErrorLoading) {
console.log('[get-admindata mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
هذه الصفحة تشبه إلى حد كبير الصفحة [authentification]. التفسيرات متشابهة سواء بالنسبة لتنفيذها بواسطة الخادم [nuxt] أو بواسطة العميل [nuxt]. ولكن تجدر الإشارة إلى أن السطر 7 لا يعرض النجاح/الفشل كما في السابق، بل يعرض قيمة البيانات المستلمة من خادم حساب الضريبة (السطر 52):

يتم الحصول على النتيجة أعلاه سواء باستخدام الخادم أو العميل [nuxt]. لإحداث خطأ، اطلب الصفحة [get-admindata]، عبر الخادم أو العميل [nuxt]، دون مصادقة:

15.18. الصفحة [fin-session]
رمز الصفحة هو التالي:
<!-- الصفحة الرئيسية -->
<template>
<Layout :left="true" :right="true">
<!-- التنقل -->
<Navigation slot="left" />
<!-- رسالة-->
<b-alert slot="right" show variant="warning">Fin de la session avec le serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'FinSession',
// المكونات المستخدمة
components: {
Layout,
Navigation
},
// بيانات غير متزامنة
async asyncData(context) {
// سجل
console.log('[fin-session asyncData started]')
// حالة العميل [nuxt]
if (process.client) {
// بدء الانتظار
context.app.$eventBus().$emit('loading', true)
// لا توجد أخطاء
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// يتم طلب جلسة جديدة PHP من خادم حساب الضريبة
const dao = context.app.$dao()
const response = await dao.finSession()
// سجل
console.log('[fin-session asyncData response=]', response)
// هل حدث خطأ؟
if (response.état !== 400) {
// الخطأ موجود في response.réponse
throw new Error(response.réponse)
}
// أرسل الخادم ملف تعريف ارتباط جلسة جديد PHP
// يتم استرداده لكل من الخادم وعميل nuxt
// إذا تم تنفيذ هذا الرمز بواسطة العميل [nuxt]، فيجب وضع ملف تعريف ارتباط الجلسة PHP في جلسة nuxt
// حتى يتمكن المكون الإضافي [plgDao] الخاص بالخادم [nuxt] من استرداده وتهيئة الطبقة [dao] باستخدام
// إذا تم تنفيذ هذا الرمز بواسطة الخادم [nuxt]، فيجب وضع ملف تعريف ارتباط الجلسة PHP في جلسة nuxt
// حتى يسترده توجيه العميل [nuxt] ويمرره إلى المتصفح
const phpSessionCookie = dao.getPhpSessionCookie()
// يتم تسجيل بدء الجلسة jSON في المخزن وتخزين ملف تعريف ارتباط الجلسة PHP
context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
// يتم حفظ المخزن في الجلسة [nuxt]
const session = context.app.$session()
session.save(context)
// يتم إرجاع النتيجة
return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
} catch (e) {
// السجل
console.log('[fin-session asyncData error=]', e)
// يتم الإبلاغ عن الخطأ
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// سجل
console.log('[fin-session asyncData finished]')
if (process.client) {
// نهاية الانتظار
context.app.$eventBus().$emit('loading', false)
}
}
},
// دورة الحياة
beforeCreate() {
console.log('[fin-session beforeCreate]')
},
created() {
console.log('[fin-session created]')
},
beforeMount() {
console.log('[fin-session beforeMount]')
},
mounted() {
console.log('[fin-session mounted]')
// العميل فقط
if (this.showErrorLoading) {
console.log('[fin-session mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
الرمز مشابه جدًا لرمز الصفحات السابقة والتفسيرات هي نفسها. يجب فقط التركيز على نقطة واحدة: العملية غير المتزامنة في السطر 38، تجعل خادم حساب الضريبة يرسل ملف تعريف ارتباط جلسة جديد PHP. تختلف التفسيرات الخاصة بإدارة ملف تعريف الارتباط هذا باختلاف ما إذا كان الخادم أو العميل [nuxt] هو الذي ينفذ هذا الرمز.
لنبدأ بخادم [nuxt]:
- السطر 37: يتم إنشاء مثيل لطبقة [dao] الخاصة بخادم [nuxt]. لنتذكر كود منشئها:
// المُنشئ
constructor(axios, phpSessionCookie) {
// مكتبة axios
this.axios = axios
// قيمة ملف تعريف ارتباط الجلسة
this.phpSessionCookie = phpSessionCookie
// اسم ملف تعريف ارتباط الجلسة الخاص بالخادم PHP
this.phpSessionCookieName = 'PHPSESSID'
}
نرى في السطر 1 أن المنشئ يحتاج إلى ملف تعريف الارتباط للجلسة الحالية PHP، وهو آخر ملف تم استلامه، سواء من الخادم أو من العميل [nuxt]؛
- السطر 52: يسترد الخادم [nuxt] ملف تعريف الارتباط الخاص بالجلسة الجديدة PHP أو ملف تعريف الارتباط القديم إذا فشلت عملية إنهاء الجلسة؛
- السطر 54: يتم وضع ملف تعريف الارتباط للجلسة PHP في المخزن ثم يتم حفظه في الجلسة [nuxt] في السطرين 56-57؛
- بعد الخادم، يقوم العميل [nuxt] بتنفيذ الصفحة [fin-session] باستخدام البيانات المرسلة من الخادم. ونعلم أنه لن يقوم بتنفيذ الدالة [asyncData]؛
- في النهاية، بعد أن ينتهي الخادم والعميل [nuxt] من عملهما، نعلم أن ملف تعريف الارتباط PHP الضروري للتبادل مع خادم حساب الضريبة موجود في الجلسة [nuxt]؛
يكفي وجود ملف تعريف الارتباط PHP في الجلسة [nuxt] بالنسبة للخادم، لأن طبقة [dao] الخاصة به ستأخذه من هناك. في المكون الإضافي [server/plgDao] الذي يقوم بتهيئة طبقة [dao] للخادم، كُتب ما يلي:
/* eslint-disable no-console */
// يتم إنشاء نقطة وصول إلى الطبقة [Dao]
import Dao from '@/api/server/Dao'
export default (context, inject) => {
// تكوين axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
// استرداد ملف تعريف ارتباط الجلسة
const store = context.app.$session().value.store
const phpSessionCookie = store ? store.phpSessionCookie : ''
console.log('session=', context.app.$session().value, 'phpSessionCookie=', phpSessionCookie)
// إنشاء مثيل للطبقة [dao]
const dao = new Dao(context.$axios, phpSessionCookie)
// إدخال دالة [$dao] في السياق
inject('dao', () => dao)
// تسجيل
console.log('[fonction server $dao créée]')
}
- السطر 13، يتم إنشاء مثيل لطبقة [dao] للخادم [nuxt] باستخدام ملف تعريف الارتباط للجلسة PHP المأخوذ من الجلسة [nuxt]، السطران 9-10؛
بالنسبة للعميل [nuxt]، فإن الأمر يختلف. فليس هو الذي يرسل ملف تعريف الارتباط، بل المتصفح الذي يقوم بتنفيذه. لكن هذا المتصفح لا يعرف ملف تعريف الارتباط الخاص بالجلسة الجديدة PHP الذي استلمه الخادم [nuxt]. إذا استخدمنا روابط قائمة التنقل [3]:

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

البرنامج النصي [client/routing] هو برنامج التوجيه الوسيط المعلن في الملف [nuxt.config]:
// الموجه
router: {
// جذر URL للتطبيق
base: '/nuxt-12/',
// برمجيات التوجيه الوسيطة
middleware: ['routing']
},
النص البرمجي [middleware/routing] هو التالي:
/* eslint-disable no-console */
// نقوم باستيراد البرمجيات الوسيطة من العميل
import clientRouting from './client/routing'
export default function(context) {
// من الذي ينفذ هذا الكود؟
console.log('[middleware], process.server', process.server, ', process.client=', process.client)
if (process.client) {
// توجيه العميل
clientRouting(context)
}
}
- الأسطر 9-12: يتم توجيه العميل فقط باستخدام دالة مستوردة في السطر 4؛
النص البرمجي [middleware/client/routing] هو التالي:
/* eslint-disable no-console */
export default function(context) {
// من يقوم بتنفيذ هذا الكود؟
console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
// إدارة ملف تعريف الارتباط الخاص بالجلسة PHP في المتصفح
// يجب أن تكون ملفات تعريف الارتباط للجلسة PHP في المتصفح مطابقة لتلك الموجودة في جلسة nuxt
// تتلقى العملية [fin-session] ملف تعريف ارتباط جديد PHP (الخادم كعميل nuxt)
// إذا كان الخادم هو الذي يستلمه، يجب على العميل نقله إلى المتصفح
// من أجل تبادلاته الخاصة مع الخادم PHP
// نحن هنا في توجيه عميل
// نسترد ملف تعريف الارتباط الخاص بالجلسة PHP
const phpSessionCookie = context.store.state.phpSessionCookie
if (phpSessionCookie) {
// إذا كان موجودًا، يتم تعيين ملف تعريف ارتباط الجلسة PHP للمتصفح
document.cookie = phpSessionCookie
}
}
لنعد إلى الوضع مباشرة بعد تنفيذ الصفحة [fin-session] بواسطة الخادم [nuxt]:

إذا نقرنا على أحد الروابط في القائمة [3]، فسيتولى العميل [nuxt] زمام الأمور. ونظرًا لأنه سيكون هناك تغيير في الصفحة، فسيتم تنفيذ برنامج التوجيه الخاص بالعميل:
- السطر 13: يتم العثور على ملف تعريف الارتباط للجلسة PHP في مخزن التطبيق [nuxt]؛
- السطر 14: إذا لم يكن فارغًا، يتم إرساله إلى المتصفح (السطر 16). من هذه اللحظة، يمتلك متصفح العميل [nuxt] ملف تعريف الارتباط الصحيح للجلسة PHP؛
يتم تنفيذ البرنامج النصي [client/routing] عند كل تغيير لصفحة العميل [nuxt]. يكون كود البرنامج النصي صالحًا بغض النظر عن الصفحة المستهدفة: ببساطة، في معظم الأحيان، يمنح المتصفح ملف تعريف ارتباط جلسة PHP الذي يمتلكه بالفعل، باستثناء حالتين:
- مباشرة بعد بدء تشغيل التطبيق، يقوم الخادم [nuxt] بتنفيذ الصفحة [index] ويتلقى ملف تعريف ارتباط جلسة أول PHP لا يمتلكه متصفح العميل [nuxt]؛
- عندما يقوم الخادم [nuxt] بتنفيذ الصفحة [fin-session] كما تم شرحه للتو؛
الآن دعونا ندرس الحالة التي يتم فيها تنفيذ الصفحة [fin-session] بواسطة العميل [nuxt] فقط، لأننا نقرنا على الرابط الخاص بها في قائمة التنقل. أصبح الآن العميل [nuxt] هو الذي يقوم بتنفيذ الوظيفة [asyncData]:
try {
// نطلب جلسة جديدة PHP من خادم حساب الضريبة
const dao = context.app.$dao()
const response = await dao.finSession()
// سجل
console.log('[fin-session asyncData response=]', response)
// هل حدث خطأ؟
if (response.état !== 400) {
// الخطأ موجود في response.réponse
throw new Error(response.réponse)
}
// أرسل الخادم ملف تعريف ارتباط جلسة جديد PHP
// يتم استرداده لكل من الخادم وعميل nuxt
// إذا تم تنفيذ هذا الرمز بواسطة العميل [nuxt]، فيجب وضع ملف تعريف ارتباط الجلسة PHP في جلسة nuxt
// حتى يتمكن المكون الإضافي [plgDao] الخاص بالخادم [nuxt] من استرداده وتهيئة الطبقة [dao] باستخدام
// إذا تم تنفيذ هذا الرمز بواسطة الخادم [nuxt]، فيجب وضع ملف تعريف ارتباط الجلسة PHP في جلسة nuxt
// حتى يسترده توجيه العميل [nuxt] ويمرره إلى المتصفح
const phpSessionCookie = dao.getPhpSessionCookie()
// يتم تسجيل بدء الجلسة jSON في المخزن وتخزين ملف تعريف ارتباط الجلسة PHP
context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
// يتم حفظ المخزن في الجلسة [nuxt]
const session = context.app.$session()
session.save(context)
// يتم إرجاع النتيجة
return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
} catch (e) {
// السجل
console.log('[fin-session asyncData error=]', e)
// يتم الإبلاغ عن الخطأ
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// سجل
console.log('[fin-session asyncData finished]')
if (process.client) {
// نهاية الانتظار
context.app.$eventBus().$emit('loading', false)
}
}
- السطر 3: الطبقة [dao] للعميل [nuxt] هي التي يتم الحصول عليها هنا؛
- السطر 18: يتم تخزين ملف تعريف الارتباط للجلسة PHP الذي تم استرداده بواسطة الطبقة [dao] للعميل [nuxt]، ووضعه في المخزن (السطر 20) ثم يتم حفظه في جلسة [nuxt] (السطران 22-23)؛
- من هنا فصاعدًا، كل شيء على ما يرام لأننا نعلم أن الطبقة [dao] للخادم [nuxt] ستبحث عن ملف تعريف الارتباط للجلسة PHP في الجلسة [nuxt]؛
15.19. Exécution
لتنفيذ هذا المثال، يجب الحرص قبل التنفيذ على حذف ملف تعريف الارتباط للجلسة [nuxt] وملف تعريف الارتباط PHP من المتصفح الذي يقوم بتشغيل العميل [nuxt] من أجل البدء من وضع نظيف. فيما يلي مثال باستخدام متصفح Chrome:

15.20. Conclusion
كان هذا المثال معقدًا بشكل خاص. فقد جمع بين المعرفة المكتسبة في الأمثلة السابقة: استمرارية المخزن في جلسة [nuxt]، ومكونات إضافية لحقن الوظائف، وبرمجيات وسيطة للتوجيه، وإدارة أخطاء العمليات غير المتزامنة. وزادت التعقيدات بسبب رغبتنا في أن يتمكن المستخدم من استخدام روابط قائمة التنقل وكذلك كتابة URL يدويًا دون أن يتعطل التطبيق. ولتحقيق ذلك، اضطررنا إلى دراسة سلوك كل صفحة حسب ما إذا كانت تُنفَّذ من قِبل العميل أو الخادم [nuxt].
هذه الوحدة الخاصة بسلوك العميل والخادم [nuxt] ليست ضرورية. يمكننا أن نضع أنفسنا في الحالة الشائعة التالية:
- يتم تقديم الصفحة الأولى بواسطة الخادم [nuxt]؛
- يتم تسليم جميع الصفحات التالية بواسطة العميل [nuxt] الذي يعمل حينها في الوضع [SPA]؛
ومع ذلك، حتى في هذه الحالة، يجب التحقق من نتيجة تنفيذ جميع الصفحات بواسطة الخادم [nuxt] لأن هذا هو ما ستحصل عليه محركات البحث التي تطلبها.