8. مثال [nuxt-05]: استمرارية المخزن باستخدام ملف تعريف ارتباط الجلسة
الهدف: نريد ألا يتم إعادة تعيين مخزن [Vuex] مع كل طلب إلى الخادم. لتحقيق ذلك، سنستخدم ملف تعريف ارتباط للجلسة:
- سيتم تهيئة المخزن بواسطة الخادم وتخزينه في ملف تعريف ارتباط للجلسة؛
- سيتلقى متصفح العميل ملف تعريف الارتباط هذا ويرسله تلقائيًا مع كل طلب جديد إلى الخادم؛
- يمكن للخادم بعد ذلك استرداد ملف تعريف الارتباط هذا والعمل مع المخزن الذي يحتوي عليه، وهو مخزن تم تحديثه بواسطة العميل؛
8.1. نظرة عامة
يتم إنشاء مشروع [nuxt-05] مبدئيًا عن طريق استنساخ مشروع [nuxt-04]:

سنرى أن ملف [store/index.js] هو الوحيد الذي سيتغير.
لاستخدام ملفات تعريف الارتباط مع [nuxt]، سنستخدم الوحدة النمطية [cookie-universal-nuxt]، التي نقوم بتثبيتها باستخدام [yarn] في محطة VSCode:

- في [4]، اكتب الأمر [yarn add cookie-universal-nuxt]؛
وبذلك تتم إضافة وحدة نمطية جديدة إلى ملف [package.json] لمشروع [dvp]:
...
},
"dependencies": {
"@nuxtjs/axios": "^5.3.6",
"bootstrap": "^4.1.3",
"bootstrap-vue": "^2.0.0",
"cookie-universal-nuxt": "^2.0.19",
"nuxt": "^2.0.0"
},
8.2. ملف التكوين [nuxt.config.js]
لكي يستخدم [nuxt] ملفات تعريف الارتباط من [cookie-universal-nuxt]، يجب عليك تعريف هذه الوحدة في ملف التكوين [nuxt.config.js]:
...
],
/*
** Nuxt.js modules
*/
modules: [
// Doc: https://bootstrap-vue.js.org
'bootstrap-vue/nuxt',
// Doc: https://axios.nuxtjs.org/usage
'@nuxtjs/axios',
// https://www.npmjs.com/package/cookie-universal-nuxt
'cookie-universal-nuxt'
],
...
- السطر 12، تمت إضافة الوحدة النمطية [cookie-universal-nuxt] إلى مصفوفة الوحدات النمطية [nuxt] [6]؛
يصبح ملف [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: { 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',
// 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) {}
},
// source code directory
srcDir: 'nuxt-05',
// router
router: {
// application URL root
base: '/nuxt-05/'
},
// 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: 'localhost'
},
// environment
env: {
maxAge: 60 * 5
}
}
- السطر 79: أضفنا المفتاح [env] إلى الملف. هذا المفتاح هو كلمة محجوزة. العناصر المعلنة في هذا الكائن متاحة عبر الكائن [context.env] في عناصر التطبيق؛
- السطر 80: ستكون السمة [maxAge] هي المدة القصوى لملف تعريف الارتباط للجلسة، محسوبة من آخر مرة تم فيها تهيئة ملف تعريف الارتباط. يتم التعبير عن هذه المدة بالثواني. هنا، قمنا بتعيين مدة صلاحية تبلغ 5 دقائق؛
8.3. مبدأ استمرارية التخزين
ملفات تعريف الارتباط المتبادلة بين العميل والخادم متاحة على كلا الجانبين (العميل والخادم) في:
- [context.app.$cookies] أينما كان الكائن [context] متاحًا، أي في كل مكان تقريبًا؛
- [this.$cookies] داخل طريقة عرض؛
يتم استرداد ملف تعريف ارتباط معين باستخدام التعبير [...$cookies.get('cookie_name')]. يتم تعيين قيمة ملف تعريف الارتباط باستخدام التعبير [...$cookies.set('cookie_name', cookie_value)].
المبدأ الكامن وراء ملف تعريف الارتباط الدائم للمتجر هو كما يلي:
- عندما يقوم الخادم بتهيئة المخزن في دالة [nuxtServerInit]، سيتم تخزين حالة المخزن في ملف تعريف ارتباط باسم "session"؛
- سيصبح ملف تعريف الارتباط "session" بعد ذلك جزءًا من استجابة HTTP للخادم. نعلم أن المتصفح يعيد إلى الخادم ملفات تعريف الارتباط التي أرسلها الخادم إليه. ويقوم بذلك مع كل طلب جديد يرسله إلى الخادم. نعلم أيضًا أن الخادم يرسل المخزن ضمن الصفحة التي يرسلها إلى العميل؛
- داخل المتصفح، يسترد تطبيق العميل المخزن الذي أرسله الخادم ثم يؤدي مهامه. سنضمن أنه في كل مرة يتم فيها تعديل المخزن، يتم تخزين حالته الجديدة في ملف تعريف الارتباط "session" الذي يحفظه المتصفح؛
- إذا أجبر المستخدم الخادم على إرسال طلب، فسيرسل متصفح العميل تلقائيًا جميع ملفات تعريف الارتباط التي أرسلها الخادم إليه سابقًا، بما في ذلك ملف تعريف الارتباط المسمى "session"؛
- عندما يعيد الخادم تعيين المخزن مرة أخرى بعد هذا الطلب، سيسترد ملف تعريف الارتباط "session" ويقوم بتهيئة حالة المخزن بقيمته؛
- وبالتالي ستكون هناك استمرارية للمتجر بين العميل والخادم؛
8.4. تهيئة المخزن
يتم تنفيذ المخزن في ملف [store / index.js]:
/* eslint-disable no-console */
export const state = () => ({
// meter
counter: 0
})
export const mutations = {
// increment counter by one [inc] value
increment(state, inc) {
state.counter += inc
},
// state replacement
replace(state, newState) {
for (const attr in newState) {
state[attr] = newState[attr]
}
}
}
export const actions = {
async nuxtServerInit(store, context) {
// who executes this code?
console.log('nuxtServerInit, client=', process.client, 'serveur=', process.server, 'env=', context.env)
// waiting for a promise to be fulfilled
await new Promise(function(resolve, reject) {
// this is normally an asynchronous function
// we simulate it with a one-second wait
setTimeout(() => {
// init session
initStore(store, context)
// success
resolve()
}, 1000)
})
}
}
function initStore(store, context) {
// is there a session cookie in the current request?
const cookies = context.app.$cookies
const session = cookies.get('session')
if (!session) {
// no existing session
console.log("nuxtServerInit, initialisation d'une nouvelle session")
// initialize the blind
store.commit('increment', 77)
} else {
console.log("nuxtServerInit, reprise d'une session existante")
// update the store with the session cookie
store.commit('replace', session.store)
}
// put the store in the session cookie
cookies.set('session', { store: store.state }, { path: context.base, maxAge: context.env.maxAge })
// log
console.log('initStore terminé, store=', store.state)
}
تعليقات
- الأسطر 2-5: سيتألف المتجر من عداد؛
- الأسطر 9–11: يمكن زيادة هذا العداد؛
- الأسطر 13-17: يمكن تهيئة حالة المخزن من حالة جديدة. تم تضمين هذه الوظيفة لتوضيح إمكانية تهيئة المخزن عندما لا يقتصر على مجرد عداد، كما في هذا المثال؛
- الأسطر 21–35: لم تتغير وظيفة [nuxtServerInit]؛
- السطر 30: عند انقضاء مهلة الثانية الواحدة، تتم تهيئة المتجر باستخدام الدالة الموجودة في الأسطر 38–56؛
- السطران 40-41: نبدأ باسترداد ملف تعريف الارتباط المسمى "session":
- أثناء التنفيذ الأول للتطبيق وأثناء الطلب الأول المقدم إلى الخادم، لن يكون ملف تعريف الارتباط هذا موجودًا بعد. سيتم إنشاؤه بعد ذلك (السطر 53) وإرساله إلى متصفح العميل؛
- أثناء نفس تشغيل التطبيق وأثناء الطلبات رقم 2 ورقم 3... الموجهة إلى الخادم، سيكون ملف تعريف الارتباط هذا موجودًا لأن متصفح العميل سيرسله مرة أخرى مع كل طلب جديد موجه إلى الخادم؛
- أثناء التنفيذ الثاني للتطبيق وعند الطلب الأول الموجه إلى الخادم، قد يكون ملف تعريف الارتباط هذا موجودًا أيضًا. في الواقع، في نهاية الخطوة 1، تم تخزين ملف تعريف الارتباط على المتصفح بفترة صلاحية محددة. إذا لم تنته هذه الفترة، فسيتم إرسال ملف تعريف الارتباط المسمى "session" مع الطلب الأول الموجه إلى الخادم
باختصار، لكل طلب يتم إرساله إلى الخادم: إذا كانت ملف تعريف الارتباط "session" مخزنة بالفعل في متصفح العميل، فسيتلقاها الخادم؛ وإلا، فلن يتلقاها.
- الأسطر 42-47: إذا لم يتلقى الخادم ملف تعريف الارتباط الخاص بالجلسة، فسيتم تهيئة المخزن بواسطة السطر 46؛
- ثم، في السطر 53، سيتم إنشاء ملف تعريف ارتباط باسم "session" ووضعه في استجابة HTTP للخادم. قيمة ملف تعريف الارتباط هي الكائن [{ store: store.state }]. وبالتالي، فإن حالة المخزن، وليس المخزن نفسه، هي التي يتم وضعها في ملف تعريف ارتباط الجلسة؛
- المعلمة الثالثة لدالة [set] هي كائن خيارات:
- يحدد [path] عنوان URL الذي يجب إرسال ملف تعريف الارتباط هذا إليه. [context.base] هو عنوان URL الأساسي لتطبيق [nuxt-05]. يتم تعريف هذا في ملف [nuxt.config.js]:
// router
router: {
// application URL root
base: '/nuxt-05/'
},
- [maxAge] هي مدة صلاحية ملف تعريف الارتباط بالثواني على المتصفح. بعد هذه المدة، يتوقف المتصفح عن إرساله إلى الخادم. [context.env.maxAge] تُرجع قيمة محددة في ملف [nuxt.config.js]:
[env] هي كلمة رئيسية محجوزة في ملف التكوين. هنا، نحدد مدة الصلاحية بـ 5 دقائق. تُحسب هذه المدة من آخر مرة تلقى فيها المتصفح ملف تعريف الارتباط الخاص بالجلسة. بعد هذه المدة، لن يتم إرسال ملف تعريف الارتباط إلى الخادم، والذي سيحتاج عندئذٍ إلى بدء جلسة جديدة؛
- الأسطر 48–50: إذا تلقى الخادم ملف تعريف ارتباط الجلسة، يتم تهيئة حالة المتجر باستخدام كائن [store] من ملف تعريف ارتباط الجلسة. تذكر أن هذا الكائن يحتوي على الحالة المحفوظة للمتجر؛
- ثم، في السطر 53، يتم وضع ملف تعريف ارتباط الجلسة في الاستجابة المرسلة إلى متصفح العميل:
- تسترد الدالة [get] ملف تعريف الارتباط للجلسة من الطلب الذي تلقّاه الخادم؛
- تضع الدالة [set] ملف تعريف ارتباط الجلسة في الاستجابة التي يرسلها الخادم إلى متصفح العميل؛
- ثم، في السطر 53، يتم وضع ملف تعريف ارتباط الجلسة في الاستجابة المرسلة إلى متصفح العميل:
8.5. زيادة عداد المخزن
تعمل زيادة العداد في صفحة [index.vue] على النحو التالي:
// event management
methods: {
incrementCounter() {
console.log('incrementCounter')
// counter increment of 1
this.$store.commit('increment', 1)
// change display value
this.value = this.$store.state.counter
// store in session cookie
this.$cookies.set('session', { store: this.$store.state }, { path: this.$nuxt.context.base, maxAge: this.$nuxt.context.env.maxAge })
}
}
على جانب العميل، في كل مرة يتم فيها تعديل المخزن، يجب حفظه في ملف تعريف ارتباط الجلسة. وذلك لأن المستخدم يمكنه طلب عنوان URL يدويًا في أي وقت، ويجب أن نكون قادرين عندئذٍ على إرسال مخزن محدث إلى الخادم. ولهذا السبب، في السطر 10، بعد زيادة عداد المخزن، نقوم بحفظ حالته في ملف تعريف ارتباط الجلسة:
- ملفات تعريف الارتباط متاحة في الخاصية [this.$cookies]؛
- يتم حفظ حالة المخزن [this.$store.state] في ملف تعريف الارتباط المرتبط بالمفتاح [store]؛
- مسار ملف تعريف الارتباط هو [context.base]. في طريقة العرض، يتوفر السياق في [this.$nuxt.context]؛
- عمر ملف تعريف الارتباط هو [context.env.maxAge]، وهو متاح هنا في الخاصية [this.$nuxt.context.env.maxAge]؛
8.6. تشغيل مثال [nuxt-05]
نقوم بتشغيل تطبيق [nuxt-05]:

اللقطات التالية مأخوذة من متصفح Chrome. نطلب عنوان URL [http://localhost:81/nuxt-05/]. لا تنسَ إضافة علامة / في النهاية بعد /nuxt-05، وإلا فلن تحصل على النتائج المتوقعة:

- في [4]، حصلنا على القيمة الأولية للمتجر (77)؛
دعونا نفحص سجلات المتصفح (F12):

- في [5-6]، سجلات الخادم؛
- في [7]، نرى أن الخادم يبدأ جلسة جديدة. وهذا يعني أنه لم يتلق ملف تعريف ارتباط للجلسة؛
- في [8]، تهيئة العداد بالقيمة 77؛
- في [9]، تعرض صفحة [index] الخاصة بالخادم (9) وصفحة [index] الخاصة بالعميل (10) نفس قيمة العداد؛
الآن دعونا نلقي نظرة على ملفات تعريف الارتباط التي استلمها المتصفح:

- في [1]، حدد علامة التبويب [Application]، ثم الخيار [Cookies] [2]. من بين جميع ملفات تعريف الارتباط الموجودة في متصفحك، حدد الملف الخاص بنطاق [http://localhost:81]؛
- في [4]، ملف تعريف الارتباط المسمى "session". إذا لم تراه، أعد تحميل الصفحة [F5]: ربما انتهت مدة صلاحيته البالغة 5 دقائق؛
- في [5]، قيمة ملف تعريف الارتباط. على الرغم من أنه ليس واضحًا تمامًا بسبب ترميز الأحرف { :، يمكنك تمييز القيمة 77 من العداد؛
- في [6]، عنوان URL لملف تعريف الارتباط: في كل مرة يتم فيها طلب عنوان URL هذا، سيرسل المتصفح ملف تعريف الارتباط إلى الخادم؛
- في [7]، وقت انتهاء صلاحية ملف تعريف الارتباط. بمجرد انقضاء هذا الوقت، سيتم حذف ملف تعريف الارتباط من المتصفح؛
تأكد من أن لديك ملف تعريف الارتباط هذا. إذا لم يكن لديك، أعد تحميل الصفحة (F5). بمجرد ظهور الصفحة مع ملف تعريف الارتباط الخاص بها، أعد تحميل الصفحة مرة أخرى (F5). ستبدو السجلات عندئذٍ كما يلي:

هذه المرة، في [3]، نجح الخادم في استرداد ملف تعريف الارتباط الخاص بالجلسة. وقد تم إرساله إليه بواسطة متصفح العميل.
الآن، قم بزيادة العداد، ثم أعد تحميل الصفحة الحالية (F5) من حين لآخر —سواء كانت [index] أو [page1]— وستلاحظ أن العداد لا يعود إلى 77 كما في مثال [nuxt-04]، بل يحتفظ بالقيمة التي كان عليها في متصفح العميل قبل إعادة تحميل الصفحة:


تكون سجلات المتصفح عندئذٍ كما يلي:

ملاحظة: لأغراض الاختبار، قد تحتاج إلى حذف [5] ملف تعريف الارتباط الخاص بالجلسة المخزن في المتصفح لبدء جلسة جديدة، يتم تهيئتها بواسطة الخادم، أثناء الطلب التالي الموجه إليه.
أخيرًا، دعونا نوضح تأثير الدالة [incrementCounter] في الصفحة [index] على ملف تعريف الارتباط الخاص بالجلسة المخزن في متصفح العميل:
// event management
methods: {
incrementCounter() {
console.log('incrementCounter')
// counter increment of 1
this.$store.commit('increment', 1)
// change display value
this.value = this.$store.state.counter
// store in session cookie
this.$cookies.set('session', { store: this.$store.state }, { path: this.$nuxt.context.base, maxAge: this.$nuxt.context.env.maxAge })
}
}
- السطر 10: ينعكس التغيير في العداد في ملف تعريف الارتباط الخاص بالجلسة؛
دعونا نتحقق من ذلك. نبدأ بالوضع التالي:

- في [4]، يعكس عداد ملف تعريف الارتباط للجلسة القيمة المعروضة [1] بشكل صحيح؛
الآن، دعونا نزيد العداد مرة واحدة [5]. يتغير ملف تعريف الارتباط للجلسة في [4] على النحو التالي:

- في [7]، تغير عداد ملف تعريف ارتباط الجلسة بالفعل إلى 84. لرؤية ذلك، تحتاج إلى تحديث العرض [8]. للقيام بذلك، حدد خيارًا آخر من [التخزين] [9]، ثم أعد تحديد الخيار [8]. عندئذٍ ستظهر القيمة الجديدة لملف تعريف ارتباط الجلسة؛