定义 Store #

defineStore 函数 #

Store 是使用 defineStore 函数定义的。它接受两个参数:

  1. 唯一标识符:Store 的唯一 ID,用于 DevTools 和代码分割
  2. Store 定义:可以是 Options 对象或 Setup 函数
ts
import { defineStore } from 'pinia'

// Options Store
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: { /* ... */ },
  actions: { /* ... */ }
})

// Setup Store
export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  return { count }
})

Options Store #

Options Store 类似于 Vue 的 Options API,包含三个选项:

基本结构 #

ts
export const useUserStore = defineStore('user', {
  // state: 返回初始状态的函数
  state: () => ({
    name: '',
    email: '',
    age: 0
  }),
  
  // getters: 计算属性
  getters: {
    isAdult: (state) => state.age >= 18,
    displayName: (state) => state.name || 'Guest'
  },
  
  // actions: 方法(同步/异步)
  actions: {
    updateProfile(data) {
      this.name = data.name
      this.email = data.email
      this.age = data.age
    },
    
    async fetchProfile() {
      const response = await api.getProfile()
      this.updateProfile(response.data)
    }
  }
})

完整示例 #

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

interface Todo {
  id: number
  text: string
  completed: boolean
}

export const useTodoStore = defineStore('todo', {
  state: () => ({
    todos: [] as Todo[],
    filter: 'all' as 'all' | 'active' | 'completed',
    loading: false
  }),
  
  getters: {
    filteredTodos: (state) => {
      switch (state.filter) {
        case 'active':
          return state.todos.filter(todo => !todo.completed)
        case 'completed':
          return state.todos.filter(todo => todo.completed)
        default:
          return state.todos
      }
    },
    
    remainingCount: (state) => {
      return state.todos.filter(todo => !todo.completed).length
    },
    
    completedCount: (state) => {
      return state.todos.filter(todo => todo.completed).length
    }
  },
  
  actions: {
    addTodo(text: string) {
      this.todos.push({
        id: Date.now(),
        text,
        completed: false
      })
    },
    
    removeTodo(id: number) {
      const index = this.todos.findIndex(todo => todo.id === id)
      if (index !== -1) {
        this.todos.splice(index, 1)
      }
    },
    
    toggleTodo(id: number) {
      const todo = this.todos.find(todo => todo.id === id)
      if (todo) {
        todo.completed = !todo.completed
      }
    },
    
    setFilter(filter: 'all' | 'active' | 'completed') {
      this.filter = filter
    },
    
    clearCompleted() {
      this.todos = this.todos.filter(todo => !todo.completed)
    },
    
    async fetchTodos() {
      this.loading = true
      try {
        const response = await fetch('/api/todos')
        this.todos = await response.json()
      } finally {
        this.loading = false
      }
    }
  }
})

Setup Store #

Setup Store 类似于 Vue 的 Composition API,使用函数定义:

基本结构 #

ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCounterStore = defineStore('counter', () => {
  // state: 使用 ref 或 reactive
  const count = ref(0)
  
  // getters: 使用 computed
  const doubleCount = computed(() => count.value * 2)
  
  // actions: 普通函数
  function increment() {
    count.value++
  }
  
  function decrement() {
    count.value--
  }
  
  // 必须返回所有需要暴露的属性
  return {
    count,
    doubleCount,
    increment,
    decrement
  }
})

完整示例 #

ts
// stores/todo.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

interface Todo {
  id: number
  text: string
  completed: boolean
}

export const useTodoStore = defineStore('todo', () => {
  // State
  const todos = ref<Todo[]>([])
  const filter = ref<'all' | 'active' | 'completed'>('all')
  const loading = ref(false)
  
  // Getters
  const filteredTodos = computed(() => {
    switch (filter.value) {
      case 'active':
        return todos.value.filter(todo => !todo.completed)
      case 'completed':
        return todos.value.filter(todo => todo.completed)
      default:
        return todos.value
    }
  })
  
  const remainingCount = computed(() => {
    return todos.value.filter(todo => !todo.completed).length
  })
  
  const completedCount = computed(() => {
    return todos.value.filter(todo => todo.completed).length
  })
  
  // Actions
  function addTodo(text: string) {
    todos.value.push({
      id: Date.now(),
      text,
      completed: false
    })
  }
  
  function removeTodo(id: number) {
    const index = todos.value.findIndex(todo => todo.id === id)
    if (index !== -1) {
      todos.value.splice(index, 1)
    }
  }
  
  function toggleTodo(id: number) {
    const todo = todos.value.find(todo => todo.id === id)
    if (todo) {
      todo.completed = !todo.completed
    }
  }
  
  function setFilter(newFilter: 'all' | 'active' | 'completed') {
    filter.value = newFilter
  }
  
  function clearCompleted() {
    todos.value = todos.value.filter(todo => !todo.completed)
  }
  
  async function fetchTodos() {
    loading.value = true
    try {
      const response = await fetch('/api/todos')
      todos.value = await response.json()
    } finally {
      loading.value = false
    }
  }
  
  return {
    // State
    todos,
    filter,
    loading,
    // Getters
    filteredTodos,
    remainingCount,
    completedCount,
    // Actions
    addTodo,
    removeTodo,
    toggleTodo,
    setFilter,
    clearCompleted,
    fetchTodos
  }
})

两种风格对比 #

特性 Options Store Setup Store
结构 类似 Options API 类似 Composition API
灵活性 固定结构 更灵活
组合性 有限 可以使用组合式函数
私有属性 不支持 支持(不返回即可)
学习曲线 较低 需要了解 Composition API
TypeScript 需要类型注解 自动推断

Options Store 适用场景 #

  • 刚接触 Vue 的开发者
  • 简单的状态管理需求
  • 喜欢固定结构的团队

Setup Store 适用场景 #

  • 熟悉 Composition API
  • 需要更灵活的代码组织
  • 需要使用组合式函数
  • 需要私有属性或方法

使用组合式函数 #

Setup Store 的一个强大特性是可以使用组合式函数:

ts
// composables/useAsync.ts
import { ref } from 'vue'

export function useAsync<T>(asyncFn: () => Promise<T>) {
  const data = ref<T | null>(null)
  const loading = ref(false)
  const error = ref<Error | null>(null)
  
  async function execute() {
    loading.value = true
    error.value = null
    try {
      data.value = await asyncFn()
    } catch (e) {
      error.value = e
    } finally {
      loading.value = false
    }
  }
  
  return { data, loading, error, execute }
}
ts
// stores/user.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useAsync } from '@/composables/useAsync'

export const useUserStore = defineStore('user', () => {
  const user = ref<User | null>(null)
  
  // 使用组合式函数
  const { loading, error, execute: fetchUser } = useAsync(async () => {
    const response = await fetch('/api/user')
    user.value = await response.json()
    return user.value
  })
  
  const isLoggedIn = computed(() => !!user.value)
  
  function logout() {
    user.value = null
  }
  
  return {
    user,
    loading,
    error,
    isLoggedIn,
    fetchUser,
    logout
  }
})

私有属性和方法 #

Setup Store 可以定义私有属性(不返回):

ts
export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  
  // 私有变量
  const maxCount = 100
  
  // 私有方法
  function validate(value: number) {
    return value >= 0 && value <= maxCount
  }
  
  // 公共方法
  function increment() {
    if (validate(count.value + 1)) {
      count.value++
    }
  }
  
  // 只返回公共属性
  return {
    count,
    increment
  }
})

Store 命名约定 #

推荐使用 use 前缀命名 Store:

ts
// 推荐
export const useUserStore = defineStore('user', { /* ... */ })
export const useCartStore = defineStore('cart', { /* ... */ })
export const useProductStore = defineStore('product', { /* ... */ })

// 不推荐
export const userStore = defineStore('user', { /* ... */ })
export const UserStore = defineStore('user', { /* ... */ })

下一步 #

现在你已经学会了如何定义 Store,接下来让我们深入了解 State 的使用。

最后更新:2026-03-28