Vue.js: إدارة الحالة مع Pinia
آخر تحديث: 2026-08-26
Pinia هي مكتبة إدارة الstatus الرسمية التي توصي بها Vue 3 — لتحل محل Vuex. توفر Pinia واجهة برمجة تطبيقات (API) أكثر إيجازًا، ودعمًا كاملاً لـ TypeScript، وإعادة التحميل السريع للوحدات النمطية، ودعمًا أصليًّا لأدوات التطوير (DevTools). وقد جعل فريق Vue Pinia هي التوصية الافتراضية (لم يعد يتم صيانة Vuex).
تتيح لك Pinia إدارة الstatus المشتركة بين المكونات (معلومات المستخدم، سلة التسوق، الإعدادات العامة) بطريقة أكثر تنظيماً من provide/inject، كما أنها أكثر إيجازاً بنسبة 50% من Vuex.
1. ما ستتعلمه
- لماذا تُعد Pinia بديلاً أفضل عن Vuex (5 مزايا رئيسية)
- defineStore: نمطان (خيارات / إعدادات)
- الstatus / دالة الاسترجاع / الإجراء: ثلاثة مفاهيم أساسية
- Pinia Modular (متعدد المتاجر)
- الاستمرارية (pinia-plugin-persistedstate)
- تكامل أدوات المطورين (DevTools)
- 5 سيناريوهات رئيسية من واقع الحياة
2. كابوس «5 تناقضات في مكون سلة التسوق»
(1) المشكلة: هناك خمسة مكونات، كل منها يدير بيانات سلة التسوق الخاصة به
تتألف منصة التجارة الإلكترونية الخاصة بـ«أليس» من 5 مكونات تحتاج إلى بيانات سلة التسوق:
// ❌ The "Broken" Version:5 component 5 set of data
// CartIcon.vue
const cartCount = ref(0)
// ProductCard.vue
const localCart = ref([])
// CartPage.vue
const cart = ref({ items: [], total: 0 })
// CheckoutPage.vue
const myCart = ref([])
// Header.vue
const cartItems = ref([])
مدير المنتج تشارلي:
«أليس، عندما أقوم بإضافة منتج في ProductCard، لا يتم تحديث رمز سلة التسوق! أرى الرقم «0» لكن الصفحة تُظهر «عنصر واحد». خمسة مكونات، خمس سلال تسوق — لا تتزامن مع بعضها!»
(2) حل Vue Pinia: متجر واحد مشترك بين 5 مكونات
// stores/cart.js
import { defineStore } from 'pinia'
export const useCartStore = defineStore('cart', {
state: () => ({
item: [],
total: 0
}),
getters: {
itemCount: (state) => state.item.length,
totalPrice: (state) => state.item.reduce((sum, i) => sum + i.price, 0)
},
actions: {
addItem(product) {
this.item.push(product)
this.total += product.price
},
removeItem(id) {
this.item = this.item.filter(i => i.id !== id)
}
}
})
<!-- CartIcon.vue -->
<script setup>
import { useCartStore } from '@/stores/cart'
const cart = useCartStore()
// Automatic Response:cart.itemCount It's changed,icon Update Now
</script>
<template>
<span>🛒 {{ cart.itemCount }}</span>
</template>
<!-- ProductCard.vue -->
<script setup>
import { useCartStore } from '@/stores/cart'
const cart = useCartStore()
</script>
<template>
<button @click="cart.addItem(product)">Add to Cart</button>
</template>
متجر واحد، و5 مكونات متزامنة في الوقت الفعلي.
(3) الإيرادات
بعد استخدام Pinia:
- اتساق البيانات: متجر واحد، و5 مكونات متزامنة في الوقت الفعلي
- حجم الكود: 5 مراجع → 1 مخزن (-80٪)
- DevTools: تصحيح الأخطاء باستخدام ميزة «السفر عبر الزمن» (عرض الstatus في كل خطوة)
- TypeScript: استدلال الأنواع الكامل
- الاحتفاظ بالبيانات: يتم الحفظ تلقائيًا في localStorage
3. 5 مزايا رئيسية لـ Pinia مقارنةً بـ Vuex
| البعد | Vuex 4 (Vue 3) | Pinia (Vue 3) |
|---|---|---|
| بساطة واجهة برمجة التطبيقات (API) | ⭐⭐⭐ معقدة (التعديلات / الإجراءات) | ⭐⭐⭐⭐⭐ بسيطة (الstatus / دالات الاسترجاع / الإجراءات) |
| TypeScript | ⭐⭐⭐ يتطلب إعدادات إضافية | ⭐⭐⭐⭐⭐ دعم مدمج |
| واجهة برمجة تطبيقات التكوين | ⭐⭐ غير سهلة الاستخدام | ⭐⭐⭐⭐⭐ عنصر أساسي |
| أدوات المطورين | ⭐⭐⭐⭐ | تحسينات ⭐⭐⭐⭐⭐ |
| حجم الحزمة | ~10 كيلوبايت | ~1 كيلوبايت |
توصي Vue رسميًا باستخدام Pinia، وقد دخلت Vuex 4 في وضع الصيانة (لن تُضاف أي ميزات جديدة).
4. نمطان من defineStore
(1) نمط الخيارات (مشابه لـ Vuex)
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
// state:Data
state: () => ({
count: 0,
name: 'Counter'
}),
// getters:Derived values(Similar computed)
getters: {
doubleCount: (state) => state.count * 2,
isZero: (state) => state.count === 0
},
// actions:Methods(Similar methods)
actions: {
increment() {
this.count++
},
async fetchData() {
const res = await fetch('/api/count')
this.count = await res.json()
}
}
})
(2) نمط الإعداد (موصى به، واجهة برمجة التطبيقات المركبة)
// stores/auth.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useAuthStore = defineStore('auth', () => {
// 1. state (use ref)
const user = ref(null)
const token = ref(localStorage.getItem('token') || '')
// 2. getters (use computed)
const isLoggedIn = computed(() => !!token.value)
const userName = computed(() => user.value?.name || 'Guest')
// 3. actions(Ordinary Functions)
function login(credentials) {
// API call...
user.value = { name: 'Alice' }
token.value = 'xxx'
}
function logout() {
user.value = null
token.value = ''
}
return { user, token, isLoggedIn, userName, login, logout }
})
(3) مقارنة بين الخيارات والإعدادات
| البعد | نمط الخيارات | نمط الإعداد |
|---|---|---|
| كيفية الكتابة | state: () => ({}) |
const x = ref() |
| مُستخرجات | (state) => ... |
computed() |
| الإجراءات | function() { this.x } |
وظيفة عادية |
| TypeScript | يدوي | تلقائي |
| قابلية التركيب | ضعيفة | قوية (يمكن استخدامها مع عناصر قابلة للتركيب أخرى) |
| مستوى التوصية | لمن لديهم خبرة في استخدام Vuex | يُوصى به للمشاريع الجديدة |
5. واجهات برمجة التطبيقات (API) الأساسية الخمس
(1) الstatus: البيانات
// Options Style
state: () => ({
count: 0,
user: null,
items: []
})
// Setup Style
const count = ref(0)
const user = ref(null)
const items = ref([])
(2) دالات الاسترجاع: القيم المشتقة
// Options Style
getters: {
// Simple getter
doubleCount: (state) => state.count * 2,
// Visit Other getters (use this)
ratio(state) {
return this.doubleCount / 100
},
// Return Function(Parameterization getter)
getItemById: (state) => (id) => {
return state.items.find(i => i.id === id)
}
}
// Setup Style
const doubleCount = computed(() => count.value * 2)
const getItemById = (id) => items.value.find(i => i.id === id)
(3) الإجراءات: الطرق
// Options Style
actions: {
// Synchronize
increment() {
this.count++
},
// Asynchronous
async fetchData() {
const res = await fetch('/api/data')
this.data = await res.json()
},
// Visit Others actions
async loginAndFetch(credentials) {
await this.login(credentials)
await this.fetchUser()
}
}
// Setup Style
function increment() {
count.value++
}
async function fetchData() {
const res = await fetch('/api/data')
data.value = await res.json()
}
(4) الاستخدام في المكونات
<script setup>
import { useCartStore } from '@/stores/cart'
import { storeToRefs } from 'pinia'
const cart = useCartStore()
// 1. Direct Access state(Responsive)
console.log(cart.item)
// 2. Use storeToRefs Destructuring (Keep Reactive)
const { item, total } = storeToRefs(cart)
// 3. Call action
cart.addItem(product)
// 4. Monitoring state Changes
watch(() => cart.item, (newItems) => {
console.log('Cart changed:', newItems)
})
</script>
(5) 5 نقاط أساسية يجب أخذها في الاعتبار
// ⚠️ Note 1:Deconstruction state Responsive Design Missing
const { items } = cart // ❌ items It is a normal value
const { items } = storeToRefs(cart) // ✅ items is ref
// ⚠️ Note 2:Edit state Must use action
cart.items.push(...) // ❌ Not recommended(Edit directly)
cart.addItem(...) // ✅ Use action
// ⚠️ Note 3:action inside this Orientation store
actions: {
increment() {
this.count++ // ✅ this = store
}
}
// ⚠️ Note 4: Getter Cache
getters: {
doubleCount() {
console.log('recomputed') // Print only when dependencies change
return this.count * 2
}
}
// ⚠️ Note 5: useStore in setup, use pinia instance externally
import { getActivePinia } from 'pinia'
const cart = useCartStore(getActivePinia()) // In .js files
6. ثبات بينيا
(1) تثبيت المكون الإضافي
npm install pinia-plugin-persistedstate
(2) تهيئة ملف main.js
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
const app = createApp(App)
app.use(pinia)
app.mount('#app')
(3) 5 طرق لحفظ الإعدادات
// stores/cart.js
export const useCartStore = defineStore('cart', () => {
const items = ref([])
return { items }
}, {
// 1. Default: localStorage key is 'cart'
persist: true,
// 2. Custom key
persist: {
key: 'my-cart',
storage: localStorage
},
// 3. Persist only a portion state
persist: {
paths: ['items'] // Save only items,Do not save total
},
// 4. sessionStorage(Closing the browser clears it)
persist: {
storage: sessionStorage
},
// 5. Custom Serialization(Encryption, etc.)
persist: {
serializer: {
serialize: (value) => btoa(JSON.stringify(value)),
deserialize: (value) => JSON.parse(atob(value))
}
}
})
7. مثال كامل: 5 ميزات رئيسية للواجهة الخلفية لمنصة التجارة الإلكترونية «Pinia Store»
▶ مثال: 1. 5 واجهات برمجة تطبيقات أساسية
import { defineStore, storeToRefs } from 'pinia'
import { ref, computed } from 'vue'
// 1. state
const count = ref(0)
// 2. getter
const double = computed(() => count.value * 2)
// 3. action
function increment() { count.value++ }
// 4. Export
export const useStore = defineStore('store', () => {
return { count, double, increment }
})
▶ مثال: 2. 5 طرق للوصول إلى «Pinia»
<!-- Direct Access -->
<template>{{ cart.items.length }}</template>
<script setup>
const cart = useCartStore()
</script>
<!-- Deconstruction(Stay Responsive)-->
<script setup>
const cart = useCartStore()
const { items, total } = storeToRefs(cart)
</script>
<!-- Monitor Changes -->
<script setup>
watch(() => cart.items, (newItems) => {
console.log('Cart updated:', newItems.length)
})
</script>
▶ مثال: 3. أسلوب «الخيارات» مقابل أسلوب «الإعداد»
// Options Style(Vuex Habits)
export const useStore1 = defineStore('store1', {
state: () => ({ count: 0 }),
getters: { double: (s) => s.count * 2 },
actions: { increment() { this.count++ } }
})
// Setup Style(Recommendations)
export const useStore2 = defineStore('store2', () => {
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() { count.value++ }
return { count, double, increment }
})
▶ مثال: 4. حفظ 5 أنواع من الإعدادات
// 1. Default
persist: true
// 2. Custom key
persist: { key: 'my-cart' }
// 3. Selectivity
persist: { paths: ['items'] }
// 4. sessionStorage
persist: { storage: sessionStorage }
// 5. Encryption
persist: {
serializer: {
serialize: JSON.stringify,
deserialize: JSON.parse
}
}
▶ مثال: 5. مرجع سريع لـ 5 أخطاء شائعة
| الخطأ | الأعراض | الحل |
|---|---|---|
| تحليل ردود «State Lost» | البيانات لم تتغير | استخدم storeToRefs |
| تعديل الstatus مباشرةً | تحذير | استخدم إجراءً بدلاً من ذلك |
إجراء غير متزامن بدون await |
نتائج غير متساوية | await cart.fetchData() |
| Pinia غير مسجلة | useStore is not a function |
main.js app.use(pinia) |
| الحقول لم يتم حفظها بعد عملية الحفظ | مفتاح غير صحيح | تحقق من إعدادات المسارات |
▶ مثال: 6. 5 سيناريوهات عملية
| السيناريو | المتجر | الحقول الرئيسية |
|---|---|---|
| Auth | useAuthStore | user، token، isLoggedIn |
| سلة التسوق | useCartStore | العناصر، الإجمالي، عدد العناصر |
| السمة | useThemeStore | السمة، الإعدادات المحلية، الكثافة |
| الإشعارات | useNotificationStore | الإشعارات، غير المقروءة |
| البيانات | useDataStore | القائمة، التحميل، الخطأ |
❓ أسئلة شائعة
useMouse وuseFetch). وتتميز Pinia بكونها أكثر تنظيماً وتشمل أدوات DevTools.src/stores/. ويدعم Pinia عملية «tree-shaking» تلقائيًّا.storeToRefs؟state؛ وإلا فستفقد التفاعلية. ولا يُشترط استخدامه في دالات الاسترجاع (getters) والإجراءات (actions).import { createPinia } from 'pinia'، يتم إنشاء مثيل منفصل لكل طلب. يتضمن Nuxt 3 تكاملاً مدمجًا مع Pinia.📖 ملخص
- Pinia هي مكتبة إدارة الstatus الموصى بها رسميًا لـ Vue 3، وهي تحل محل Vuex
- 5 مزايا رئيسية: البساطة / TypeScript / التركيب / أدوات التطوير / حجم الحزمة الصغير
- defineStore: نمطان: «Options» (وفقًا لمعايير Vuex) / «Setup» (موصى به)
- 5 واجهات برمجة تطبيقات رئيسية: state / getters / actions / storeToRefs / Persistence
- قابلية التجزئة: ملف واحد لكل متجر
- الاستمرارية: pinia-plugin-persistedstate
- Pinia مقابل Composable: الstatus العالمية مقابل منطق المكونات
📝 تمارين
-
أسئلة أساسية (مستوى الصعوبة: ⭐)
تنفيذ متجر بسيط يعتمد على العداد:
- status العد
- خاصية القراءة doubleCount
- إجراءات الزيادة / النقصان / إعادة الضبط
- موزعة على 3 مكونات
-
مسائل متقدمة (مستوى الصعوبة: ⭐⭐)
تنفيذ عربة التسوق (المتجر):
- array العناصر
- متغيرات القراءة itemCount و totalPrice
- إجراءات addItem / removeItem / clearCart
- الحفظ في localStorage
- تم اختباره في 5 مكونات (ProductCard / CartIcon / CartPage / Checkout / Header)
-
مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)
تنفيذ نظام «متجر» خلفي متكامل للتجارة الإلكترونية:
- 5 مخازن: auth / cart / theme / notification / product
- كل متجر: الstatus الكاملة + دالات الاسترجاع + الإجراءات
- الاستمرارية (التوثيق/سلة التسوق/السمة)
- أسلوب الإعداد + TypeScript
- تحليل الدالة storeToRefs
- المكالمات بين المتاجر (تقوم سلة التسوق بالاتصال بوحدة المصادقة للتحقق من تسجيل الدخول)
- اختبار الوحدات (باستخدام Vitest)