Pinia 项目结构 #

基本结构 #

推荐目录结构 #

text
src/
├── stores/
│   ├── index.ts           # Store 导出和 Pinia 配置
│   ├── user.ts            # 用户相关 Store
│   ├── cart.ts            # 购物车 Store
│   ├── product.ts         # 产品 Store
│   └── types/
│       └── index.ts       # Store 类型定义
├── composables/
│   ├── useAsync.ts        # 异步处理
│   └── usePagination.ts   # 分页处理
└── main.ts

模块化结构 #

对于大型项目,按功能模块组织:

text
src/
├── stores/
│   ├── index.ts
│   ├── modules/
│   │   ├── auth/
│   │   │   ├── index.ts
│   │   │   ├── user.ts
│   │   │   └── session.ts
│   │   ├── shop/
│   │   │   ├── index.ts
│   │   │   ├── cart.ts
│   │   │   ├── product.ts
│   │   │   └── order.ts
│   │   └── admin/
│   │       ├── index.ts
│   │       └── dashboard.ts
│   └── shared/
│       ├── theme.ts
│       └── locale.ts

Store 文件组织 #

单文件 Store #

适用于简单的 Store:

ts
// stores/user.ts
import { defineStore } from 'pinia'

interface User {
  id: number
  name: string
  email: string
}

interface UserState {
  user: User | null
  token: string | null
  loading: boolean
}

export const useUserStore = defineStore('user', {
  state: (): UserState => ({
    user: null,
    token: null,
    loading: false
  }),
  
  getters: {
    isAuthenticated: (state) => !!state.token,
    displayName: (state) => state.user?.name || 'Guest'
  },
  
  actions: {
    async login(email: string, password: string) {
      // ...
    },
    logout() {
      this.user = null
      this.token = null
    }
  }
})

// 导出类型
export type { User, UserState }

多文件 Store #

对于复杂的 Store,拆分为多个文件:

text
stores/
├── user/
│   ├── index.ts          # Store 定义
│   ├── state.ts          # State 定义
│   ├── getters.ts        # Getters 定义
│   ├── actions.ts        # Actions 定义
│   └── types.ts          # 类型定义
ts
// stores/user/types.ts
export interface User {
  id: number
  name: string
  email: string
}

export interface UserState {
  user: User | null
  token: string | null
  loading: boolean
  error: string | null
}
ts
// stores/user/state.ts
import type { UserState } from './types'

export const initialState: UserState = {
  user: null,
  token: null,
  loading: false,
  error: null
}
ts
// stores/user/getters.ts
import type { UserState } from './types'

export const getters = {
  isAuthenticated: (state: UserState) => !!state.token,
  displayName: (state: UserState) => state.user?.name || 'Guest',
  isAdmin: (state: UserState) => state.user?.role === 'admin'
}
ts
// stores/user/actions.ts
import type { User, UserState } from './types'

export const actions = {
  async login(this: any, email: string, password: string) {
    this.loading = true
    this.error = null
    try {
      const response = await fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password })
      })
      const data = await response.json()
      this.user = data.user
      this.token = data.token
    } catch (e) {
      this.error = e instanceof Error ? e.message : 'Login failed'
    } finally {
      this.loading = false
    }
  },
  
  logout(this: any) {
    this.user = null
    this.token = null
  }
}
ts
// stores/user/index.ts
import { defineStore } from 'pinia'
import { initialState } from './state'
import { getters } from './getters'
import { actions } from './actions'

export const useUserStore = defineStore('user', {
  state: () => ({ ...initialState }),
  getters,
  actions
})

export * from './types'

统一导出 #

stores/index.ts #

ts
// stores/index.ts
import { createPinia } from 'pinia'

export const pinia = createPinia()

// 导出所有 Store
export * from './user'
export * from './cart'
export * from './product'
export * from './order'

// 导出类型
export * from './types'

按模块导出 #

ts
// stores/modules/auth/index.ts
export * from './user'
export * from './session'

// stores/modules/shop/index.ts
export * from './cart'
export * from './product'
export * from './order'

// stores/index.ts
export * from './modules/auth'
export * from './modules/shop'
export * from './modules/admin'
export * from './shared'

类型定义组织 #

集中管理类型 #

ts
// stores/types/index.ts
export interface User {
  id: number
  name: string
  email: string
  role: 'admin' | 'user'
}

export interface Product {
  id: number
  name: string
  price: number
  stock: number
}

export interface CartItem {
  productId: number
  quantity: number
  price: number
}

export interface Order {
  id: number
  userId: number
  items: CartItem[]
  total: number
  status: 'pending' | 'paid' | 'shipped' | 'completed'
}

Store 内定义类型 #

ts
// stores/user.ts
import { defineStore } from 'pinia'

// 类型定义在 Store 文件内
interface UserState {
  user: User | null
  token: string | null
}

interface UserGetters {
  isAuthenticated: boolean
  displayName: string
}

interface UserActions {
  login: (email: string, password: string) => Promise<boolean>
  logout: () => void
}

export const useUserStore = defineStore('user', {
  // ...
})

命名规范 #

Store 命名 #

ts
// 推荐:使用 use 前缀
export const useUserStore = defineStore('user', { /* ... */ })
export const useCartStore = defineStore('cart', { /* ... */ })
export const useProductStore = defineStore('product', { /* ... */ })

// 文件名使用小写
// stores/user.ts
// stores/cart.ts
// stores/product.ts

State 属性命名 #

ts
export const useUserStore = defineStore('user', {
  state: () => ({
    // 使用清晰的命名
    user: null,
    isLoading: false,      // 布尔值使用 is/has 前缀
    hasError: false,
    errorMessage: null,    // 错误相关使用 error 前缀
    
    // 列表使用复数
    users: [],
    orders: [],
    
    // 当前选中项使用 current 前缀
    currentUser: null,
    selectedProduct: null
  })
})

Actions 命名 #

ts
export const useUserStore = defineStore('user', {
  actions: {
    // 获取数据使用 fetch/get
    async fetchUser() { /* ... */ },
    async getUsers() { /* ... */ },
    
    // 创建使用 create/add
    async createUser() { /* ... */ },
    addItem() { /* ... */ },
    
    // 更新使用 update/set
    async updateUser() { /* ... */ },
    setTheme() { /* ... */ },
    
    // 删除使用 delete/remove
    async deleteUser() { /* ... */ },
    removeItem() { /* ... */ },
    
    // 重置使用 reset/clear
    resetState() { /* ... */ },
    clearCart() { /* ... */ }
  }
})

完整示例 #

电商应用结构 #

text
src/
├── stores/
│   ├── index.ts
│   ├── types/
│   │   ├── user.ts
│   │   ├── product.ts
│   │   ├── cart.ts
│   │   └── order.ts
│   ├── auth/
│   │   ├── index.ts
│   │   ├── user.ts
│   │   └── session.ts
│   ├── shop/
│   │   ├── index.ts
│   │   ├── cart.ts
│   │   ├── product.ts
│   │   └── order.ts
│   └── shared/
│       ├── theme.ts
│       ├── locale.ts
│       └── notification.ts
├── composables/
│   ├── useAsync.ts
│   ├── usePagination.ts
│   └── useForm.ts
├── api/
│   ├── index.ts
│   ├── auth.ts
│   ├── product.ts
│   └── order.ts
└── main.ts
ts
// stores/index.ts
import { createPinia } from 'pinia'

export const pinia = createPinia()

// Auth
export * from './auth'
// Shop
export * from './shop'
// Shared
export * from './shared'

// Types
export * from './types'

最佳实践 #

1. 保持 Store 独立 #

每个 Store 只负责一个领域:

ts
// 好的做法
useUserStore()    // 用户相关
useCartStore()    // 购物车相关
useOrderStore()   // 订单相关

// 不好的做法
useDataStore()    // 所有数据混在一起

2. 避免过度拆分 #

ts
// 好的做法:相关状态放在一起
export const useUserStore = defineStore('user', {
  state: () => ({
    profile: null,
    preferences: null,
    settings: null
  })
})

// 过度拆分
export const useUserProfileStore = defineStore('userProfile', { /* ... */ })
export const useUserPreferencesStore = defineStore('userPreferences', { /* ... */ })
export const useUserSettingsStore = defineStore('userSettings', { /* ... */ })

3. 使用 TypeScript #

ts
// 定义完整的类型
interface UserState {
  user: User | null
  loading: boolean
  error: string | null
}

export const useUserStore = defineStore('user', {
  state: (): UserState => ({
    user: null,
    loading: false,
    error: null
  })
})

4. 文档注释 #

ts
/**
 * 用户状态管理
 * @module stores/user
 */

/**
 * 用户 Store
 * 管理用户登录状态、用户信息等
 */
export const useUserStore = defineStore('user', {
  state: () => ({
    /** 当前登录用户 */
    user: null as User | null,
    /** 认证令牌 */
    token: null as string | null
  })
})

下一步 #

现在你已经掌握了项目结构的组织方式,接下来让我们学习 TypeScript 集成。

最后更新:2026-03-28