decodex | آموزش برنامه نویسی و طراحی سایت
decodex | ساده‌تر کردن مدیریت state در Vue 3 با Pinia

ساده‌تر کردن مدیریت state در Vue 3 با Pinia

اگه تا حالا سر و کله زدن با state توی پروژه‌های Vue اذیتت کرده، خبر خوب اینه که Pinia اومده کار رو ساده کنه. Pinia عملا نسل بعدی Vuex حساب می‌شه که با تایپ اسکریپت مشکلی نداره، با Composition API سازگاره و در کل تمیز تره. توی این مطلب قدم به قدم میریم جلو تا هم درک کنی چرا باید Pinia رو انتخاب کنی، هم سریع راهش بندازی، هم الگوهاش رو ببینی!

اول بگیم منظور از Store در Pinia چیه؟

در Pinia، Store یک مخزن داده است که شامل:

  • state (وضعیت یا داده‌ها)
  • getters (تابع‌های محاسباتی برای دسترسی به مفدار داده‌ها)
  • actions (توابعی برای تغییر داده‌ها یا انجام منطق)

به بیان ساده‌تر، هر Store مثل یک "مینی‌ماژول" از داده‌ها و منطق مرتبط با یک بخش از برنامه ست.

۱) چرا Pinia؟ ویژگی‌ها و تفاوت‌ها با Vuex

Pinia با تمرکز روی سادگی و قابلیت ترکیب‌پذیری ساخته شده. چند تا نکته کلیدی:

  • API ساده: خبری از مفاهیم پیچیده مثل mutations نیست؛ state، getters و actions کفایت میکنه.
  • Composition API-first: تعریف store ها در کنار setup به‌شدت طبیعی و خوش دست شده.
  • TypeScript دو آتیشه: تایپ های تمیز و قابل اعتماد تعریف میکنی، کمتر درگیر any میشی و حرفه ای تر کد میزنی.
  • DevTools عالی: توی devtools میتونی جزئیات عملکردش رو رصد کنی.
  • SSR و HMR: پشتیبانی خوب از رندر سمت سرور و Hot Module Replacement

نکته: Hot Module Replacement (HMR) قابلیتی ست برای به‌روزرسانی بخش‌هایی از کد (ماژول‌ها) در حال اجرا، بدون رفرش شدن کل صفحه، در حالت توسعه (development). در واقع همون که شما در حال کدنویسی یه تغییری میدی، اتومات میبینی صفحه مرورگر آپدیت شده، اینو میگن HMR!

موضوع Pinia در برابر Vuex
تعریف state ساده و ماژولار، هر Store مستقله
Mutations توی Pinia حذف شده؛ تغییر state درون actions یا مستقیم با patch اتفاق میوفته
TypeScript Pinia خیلی بهتر ts رو پوشش میده و تجربه بهتری برات میسازه!
یادگیری انصافا یادگیری جفتش سخت نیس یکم باید وقت بزاری مطالعه کنی و 2 تا مثال هم پیاده کنی

۲) نصب Pinia روی Vue

دستور نصب Pinia

npm i pinia
# یا
yarn add pinia

اتصال به برنامه

// main.js یا main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)
app.use(createPinia())
app.mount('#app')

تعریف یک استور ساده

// src/stores/counter.js یا counter.ts
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() { this.count++ },
  },
})

استفاده در کامپوننت

<script setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'

const counter = useCounterStore()
const { count, double } = storeToRefs(counter)

function add() {
  counter.increment()
}
</script>

<template>
  <div>
    <p>Count: {{ count }} - Double: {{ double }}</p>
    <button @click="add" class="btn btn-success">افزایش</button>
  </div>
</template>
ساده‌تر کردن مدیریت state در Vue 3 با Pinia | decodex.ir

۳) روش‌ها و شیوه‌های رایج استفاده از Pinia

سناریوی واقعی معمولا این شکلیِ، به store نمونه دقت کنید:

// src/stores/todos.ts
import { defineStore } from 'pinia'

export const useTodos = defineStore('todos', {
  state: () => ({
    items: [] as Array<{ id: number; title: string; done: boolean }>,
    loading: false,
    error: null as string | null,
    filter: 'all' as 'all' | 'done' | 'open',
  }),
  getters: {
    total: (s) => s.items.length,
    completed: (s) => s.items.filter(i => i.done),
    visible(state) {
      if (state.filter === 'done') return state.items.filter(i => i.done)
      if (state.filter === 'open') return state.items.filter(i => !i.done)
      return state.items
    },
  },
  actions: {
    async fetchTodos() {
      this.loading = true
      this.error = null
      try {
        const res = await fetch('/api/todos')
        if (!res.ok) throw new Error('Failed to fetch')
        this.items = await res.json()
      } catch (e: any) {
        this.error = e.message
      } finally {
        this.loading = false
      }
    },
    add(title: string) {
      const id = Date.now()
      this.items.push({ id, title, done: false })
    },
    toggle(id: number) {
      const t = this.items.find(i => i.id === id)
      if (t) t.done = !t.done
    },
    patchFilter(f: 'all' | 'done' | 'open') {
      this.$patch({ filter: f })
    },
  },
})

نکات ریز اما مهم:

  • برای stateهای پیچیده از $patch استفاده کن تا تغییرات گروهی تمیزتر بشه.
  • storeToRefs کمک می‌کنه getters و state واکنش‌گرا رو راحت بگیری بدون از دست دادن reactivity.
  • میتونی رو اکشن ها subscribe کنی و لاگ بزنی یا آنالیتیکس بفرستی.
// استفاده در کامپوننت
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useTodos } from '@/stores/todos'

const todos = useTodos()
const { visible, loading, error, total } = storeToRefs(todos)

todos.$onAction(({ name, args, after, onError }) => {
  const start = performance.now()
  after(() => console.log(`[action] ${name}`, args, performance.now() - start, 'ms'))
  onError((err) => console.error(`[action error] ${name}`, err))
})
</script>
ساده‌تر کردن مدیریت state در Vue 3 با Pinia | decodex.ir

۴) نکات حرفه‌ای: ساختاردهی، پرفورمنس، Persist و SSR

ساختاردهی store ها

  • هر دامنه (auth، cart، todos، ui) یک استور: خوانایی و تست‌پذیری بهتر.
  • کدهای مشترک (مثل fetcher یا mapperها) رو بیرون از استورها نگه دار تا coupling کم بشه.
  • برای وابستگی بین استورها، داخل action یکی از استورها از دیگری استفاده کن (تزریق غیرضروری ایجاد نکن).

Persist کردن state

برای ذخیره بخشی از state در localStorage می‌تونی از افزونه‌ها کمک بگیری:

npm i pinia-plugin-persistedstate
// main.ts
import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(persist)
// در استور
export const useAuth = defineStore('auth', {
  state: () => ({ token: '', user: null as null | { id: number; name: string } }),
  persist: {
    paths: ['token'], // فقط توکن
  },
})

پرفورمنس و بهینه‌سازی

  • استورهای خیلی چاق رو تکه تکه کن، getters سنگین رو memoize کن یا نتیجه‌ها رو جزئی کن.
  • از storeToRefs فقط برای چیزهایی که لازم داری استفاده کن تا رندرهای اضافی کم بشه.
  • برای لیست های بزرگ، داده مشتق‌شده رو paginate یا virtualize کن.
  • اکشن‌های async رو debounce/throttle کن وقتی ورودی کاربر سریعه.

SSR و هیدریشن

  • Pinia با SSR سازگاره؛ برای هر ریکوئست یک instance جدا بساز.
  • state سمت سرور رو serialize کن و در کلاینت hydrate؛ مراقب داده‌های حساس باش.

تست کردن استورها

  • با Vitest/Jest اکشن‌ها رو مستقل تست کن؛ I/O رو mock کن.
  • برای snapshot از store.$state استفاده کن؛ تغییرات با $patch قابل رهگیریه.

جمع‌بندی

Pinia مدیریت state رو در Vue 3 واقعاً بی دردسر می‌کنه: API مینیمال، تایپ فرندلی، سازگار با ابزارهای مدرن و انعطاف‌پذیر برای سناریوهای واقعی. اگر از Vuex میای، مهاجرتش سرراسته و اگه تازه می‌خوای استور داشته باشی، یادگیریش سریعه. دوست دارم بدونم توی پروژه‌هات چه چالش‌هایی در مدیریت state داشتی یا چه الگوهایی برات جواب داده. تجربیاتت رو توی کامنت‌ها بنویس تا بقیه هم استفاده کنن!

دیدگاه شما

ثبت دیدگاه

اگه در مورد این مطلب نظری داری یا در همین موضوع سوالی داری، همینجا مطرح کن تا از دیدگاه ارزشمندت استفاده کنیم و انرژی بگیریم، یا سوالت رو جواب بدیم

در ضمن، شماره موبایلت تو سایت نمایش داده نمیشه و پیش ما به صورت محرمانه میمونه

کد تایید پیامک شده به شماره را وارد نمایید