Bootstrap

Pinia第二章:Pinia 安装与基础配置

2.1 安装流程全解析
# 使用 npm 安装(推荐用于标准项目)
npm install pinia --save

# 使用 yarn 安装(推荐用于 Monorepo 项目)
yarn add pinia

# 针对 Vite 项目的优化安装(自动处理 Tree-shaking)
npm install pinia@next -D

安装注意事项:

  1. 需要 Vue3 运行时环境(要求 Vue >= 3.2)
  2. 与 Vue Router 无版本冲突
  3. 生产环境会自动启用 Tree-shaking

2.2 基础配置详解
// main.js(核心配置文件)
import { createApp } from 'vue'
import { createPinia } from 'pinia'  // 导入 Pinia 工厂函数
import App from './App.vue'

/**
 * 创建 Pinia 实例(重要!必须先于 Vue 实例创建)
 * 功能:
 * 1. 管理所有 Store 的注册表
 * 2. 维护插件系统
 * 3. 提供 devtools 集成
 */
const pinia = createPinia()

// 创建 Vue 应用实例
const app = createApp(App)

/**
 * 安装 Pinia 插件(关键步骤!)
 * 作用:
 * 1. 注入 $pinia 到所有组件上下文
 * 2. 启用开发工具支持
 * 3. 激活响应式系统集成
 */
app.use(pinia)

// 挂载到 DOM
app.mount('#app')

配置要点说明:

  1. 必须确保 createPinia() 在根组件挂载前调用
  2. app.use(pinia) 会在所有组件中注入 useStore() 方法
  3. 可在此处添加全局插件(后文会详细讲解)

2.3 创建第一个 Store 的两种方式

方式一:选项式 Store(推荐新手使用)

// stores/counter.js
import { defineStore } from 'pinia'

/**
 * defineStore 参数说明:
 * @param {string} 'counter' → Store 唯一ID(必须全局唯一)
 * @param {object} 配置对象 → 包含 state/getters/actions
 */
export const useCounterStore = defineStore('counter', {
  /**
   * state 定义规范:
   * 1. 必须使用工厂函数返回初始状态
   * 2. 支持嵌套对象结构
   * 3. 类型推导会自动工作
   */
  state: () => ({
    count: 0,
    history: [] // 记录操作历史
  }),

  /**
   * Getters 定义规则:
   * 1. 接收 state 作为第一个参数
   * 2. 可通过 this 访问整个 store 实例
   * 3. 支持返回函数实现带参数的计算属性
   */
  getters: {
    doubleCount: (state) => state.count * 2,
    formattedHistory: (state) => (separator = ', ') => state.history.join(separator)
  },

  /**
   * Actions 定义要点:
   * 1. 可以是同步或异步函数
   * 2. 通过 this 直接访问和修改 state
   * 3. 支持调用其他 actions
   */
  actions: {
    increment() {
      // 直接修改状态
      this.count++
      // 调用其他 action
      this.recordAction('increment')
    },
    async asyncIncrement() {
      await new Promise(resolve => setTimeout(resolve, 1000))
      this.increment()
    },
    recordAction(actionName) {
      this.history.push({
        action: actionName,
        timestamp: new Date().toISOString()
      })
    }
  }
})

方式二:组合式 Store(推荐 TypeScript 项目)

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

export const useUserStore = defineStore('user', () => {
  // State 定义(使用 ref/reactive)
  const username = ref('')
  const age = ref(0)
  const loginHistory = ref<Date[]>([])

  // Getters(使用 computed)
  const isAdult = computed(() => age.value >= 18)
  const lastLogin = computed(() => {
    return loginHistory.value.length > 0 
      ? loginHistory.value.slice(-1)[0]
      : null
  })

  // Actions(普通函数)
  function login() {
    loginHistory.value.push(new Date())
    localStorage.setItem('userToken', 'demo_token')
  }

  async function fetchProfile() {
    const res = await fetch('/api/profile')
    const data = await res.json()
    username.value = data.name
    age.value = data.age
  }

  return { 
    username, 
    age, 
    loginHistory,
    isAdult,
    lastLogin,
    login,
    fetchProfile 
  }
})

两种方式的对比说明:

特性选项式 Store组合式 Store
适用场景简单状态逻辑复杂业务逻辑
TypeScript 支持自动类型推断更精确的类型控制
代码组织按功能分类(state/getters/actions)按逻辑相关性组织
响应式系统自动处理需要显式使用 ref/computed
可复用性较低更高(可提取逻辑函数)

2.4 配置进阶技巧

多环境配置示例:

// 开发环境配置
const pinia = createPinia()

if (import.meta.env.MODE === 'development') {
  pinia.use(devToolsPlugin)    // 添加开发工具插件
  pinia.use(loggerPlugin)      // 添加状态变更日志
}

// 生产环境配置
if (import.meta.env.PROD) {
  pinia.use(performancePlugin) // 添加性能监控
}

SSR 配置示例:

// server.js(服务端)
import { createPinia } from 'pinia'

export default function createSSRApp() {
  const app = createApp(App)
  const pinia = createPinia()
  
  // 为每个请求创建新实例
  app.use(pinia)
  
  return { app, pinia }
}

// client.js(客户端)
const pinia = createPinia()
pinia.state.value = window.__PINIA_STATE__ // 同步服务端状态

2.5 常见问题排查

问题1:Store 未正确注册

// 错误现象:Cannot access 'useXxxStore' before initialization

// 解决方案:确保在组件中使用前完成注册
// ✅ 正确使用顺序:
const app = createApp(App)
app.use(createPinia())
app.mount('#app')

问题2:响应性丢失

// 错误写法:
const { count } = useCounterStore() // 失去响应性!

// ✅ 正确做法:
const counter = useCounterStore()
// 在模板中直接使用 counter.count
// 或使用 storeToRefs:
import { storeToRefs } from 'pinia'
const { count } = storeToRefs(counter)

问题3:循环依赖

// stores/a.js 和 stores/b.js 互相引用

// ✅ 解决方案:
// 在 action 中动态导入
actions: {
  async fetchData() {
    const { useBStore } = await import('./b.js')
    const bStore = useBStore()
    // ...
  }
}

本章核心要点:

  1. 安装顺序必须确保 Pinia 在 Vue 实例挂载前注册
  2. Store 定义可根据项目复杂度选择选项式或组合式
  3. SSR 配置需要特殊处理状态同步
  4. 响应性维护需注意解构操作的影响
  5. 环境区分可针对不同环境添加插件

通过本章的学习,应该能够正确完成 Pinia 的环境搭建,并理解不同 Store 定义方式的适用场景。接下来第三章将深入讲解核心概念的具体使用方法。

;