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. ما ستتعلمه



2. كابوس «5 تناقضات في مكون سلة التسوق»

(1) المشكلة: هناك خمسة مكونات، كل منها يدير بيانات سلة التسوق الخاصة به

تتألف منصة التجارة الإلكترونية الخاصة بـ«أليس» من 5 مكونات تحتاج إلى بيانات سلة التسوق:

JS
// ❌ 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 مكونات

JS
// 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)
    }
  }
})
VUE
<!-- 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>
VUE
<!-- 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:



3. 5 مزايا رئيسية لـ Pinia مقارنةً بـ Vuex

البعد Vuex 4 (Vue 3) Pinia (Vue 3)
بساطة واجهة برمجة التطبيقات (API) ⭐⭐⭐ معقدة (التعديلات / الإجراءات) ⭐⭐⭐⭐⭐ بسيطة (الstatus / دالات الاسترجاع / الإجراءات)
TypeScript ⭐⭐⭐ يتطلب إعدادات إضافية ⭐⭐⭐⭐⭐ دعم مدمج
واجهة برمجة تطبيقات التكوين ⭐⭐ غير سهلة الاستخدام ⭐⭐⭐⭐⭐ عنصر أساسي
أدوات المطورين ⭐⭐⭐⭐ تحسينات ⭐⭐⭐⭐⭐
حجم الحزمة ~10 كيلوبايت ~1 كيلوبايت

توصي Vue رسميًا باستخدام Pinia، وقد دخلت Vuex 4 في وضع الصيانة (لن تُضاف أي ميزات جديدة).



4. نمطان من defineStore

(1) نمط الخيارات (مشابه لـ Vuex)

JS
// 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) نمط الإعداد (موصى به، واجهة برمجة التطبيقات المركبة)

JS
// 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: البيانات

JS
// Options Style
state: () => ({
  count: 0,
  user: null,
  items: []
})

// Setup Style
const count = ref(0)
const user = ref(null)
const items = ref([])

(2) دالات الاسترجاع: القيم المشتقة

JS
// 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) الإجراءات: الطرق

JS
// 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) الاستخدام في المكونات

VUE
<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 نقاط أساسية يجب أخذها في الاعتبار

JS
// ⚠️ 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) تثبيت المكون الإضافي

BASH
npm install pinia-plugin-persistedstate

(2) تهيئة ملف main.js

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 طرق لحفظ الإعدادات

JS
// 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 واجهات برمجة تطبيقات أساسية

JS
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»

VUE
<!-- 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. أسلوب «الخيارات» مقابل أسلوب «الإعداد»

JS
// 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 أنواع من الإعدادات

JS
// 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 القائمة، التحميل، الخطأ


❓ أسئلة شائعة

س أيهما يجب أن أختار، Pinia أم Vuex؟
ج Pinia. يوصي فريق Vue رسميًا باستخدام Pinia، ولم يعد يتم تحديث Vuex 4. يجب أن تستخدم المشاريع الجديدة Pinia، ويمكن ترحيل مشاريع Vuex الحالية تدريجيًّا.
س هل تحل Pinia محل provide/inject؟
ج لا، لا تحل محلها. تُستخدم Pinia لإدارة الstatus العامة، بينما تُستخدم provide/inject للتواصل بين المستويات. تُستخدم Pinia لـ«الstatus على مستوى التطبيق» (المستخدم/سلة التسوق)، بينما تُستخدم provide/inject لـ«السمات/الترجمة/التكوين».
س متى ينبغي استخدام Pinia مقابل Composable؟
ج تُستخدم Pinia للstatus العامة المشتركة بين المكونات (مثل بيانات المستخدم وعربة التسوق). أما Composable فتُستخدم للمنطق القابل لإعادة الاستخدام على مستوى المكونات (مثل useMouse وuseFetch). وتتميز Pinia بكونها أكثر تنظيماً وتشمل أدوات DevTools.
س كيف يتم تقسيم Pinia إلى وحدات؟
ج كل متجر موجود في ملف خاص به، يقع في الدليل src/stores/. ويدعم Pinia عملية «tree-shaking» تلقائيًّا.
س متى ينبغي استخدام storeToRefs؟
ج يجب استخدامه عند تفكيك state؛ وإلا فستفقد التفاعلية. ولا يُشترط استخدامه في دالات الاسترجاع (getters) والإجراءات (actions).
س ما هي خيارات التخزين التي تدعمها Pinia للحفظ الدائم؟
ج بشكل افتراضي، localStorage؛ كما أنها تدعم sessionStorage، والتخزين المخصص (IndexedDB، وملفات تعريف الارتباط، وما إلى ذلك)، والتسلسل المخصص (المشفّر).
س ما هو أداء Pinia؟
ج تؤدي تغييرات الstatus إلى تحديثات تفاعلية، ويتم تخزين دالات الاسترجاع تلقائيًا في ذاكرة التخزين المؤقت، كما توفر أدوات التطوير (DevTools) تحسينات. وهو أسرع بنسبة 30–50% من Vuex (حيث لا يستخدم مفهوم «المتغيرات»).
س هل يمكن استخدام Pinia مع SSR؟
ج نعم. import { createPinia } from 'pinia'، يتم إنشاء مثيل منفصل لكل طلب. يتضمن Nuxt 3 تكاملاً مدمجًا مع Pinia.

📖 ملخص


📝 تمارين

  1. أسئلة أساسية (مستوى الصعوبة: ⭐)

    تنفيذ متجر بسيط يعتمد على العداد:

    • status العد
    • خاصية القراءة doubleCount
    • إجراءات الزيادة / النقصان / إعادة الضبط
    • موزعة على 3 مكونات
  2. مسائل متقدمة (مستوى الصعوبة: ⭐⭐)

    تنفيذ عربة التسوق (المتجر):

    • array العناصر
    • متغيرات القراءة itemCount و totalPrice
    • إجراءات addItem / removeItem / clearCart
    • الحفظ في localStorage
    • تم اختباره في 5 مكونات (ProductCard / CartIcon / CartPage / Checkout / Header)
  3. مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)

    تنفيذ نظام «متجر» خلفي متكامل للتجارة الإلكترونية:

    1. 5 مخازن: auth / cart / theme / notification / product
    2. كل متجر: الstatus الكاملة + دالات الاسترجاع + الإجراءات
    3. الاستمرارية (التوثيق/سلة التسوق/السمة)
    4. أسلوب الإعداد + TypeScript
    5. تحليل الدالة storeToRefs
    6. المكالمات بين المتاجر (تقوم سلة التسوق بالاتصال بوحدة المصادقة للتحقق من تسجيل الدخول)
    7. اختبار الوحدات (باستخدام Vitest)
Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%