Vue 3 组合式 API 规范配合 Pinia

实现效果:
根据pinia中存储的不同状态,
点击不同的按钮,切换不同的弹窗和标题

1. Pinia Store(组合式写法)

// stores/dataStore.ts
import {
   
    defineStore } from 'pinia'
import {
   
    reactive } from 'vue'

type DialogType = 'type1' | 'type2'
//定义一个接口规范
interface DialogState {
   
   
  id: number
  name: string
  visible: boolean
}

export const useDataStore = defineStore('dataType', () => {
   
   
  // 状态定义 两个不同的状态
  const state = reactive({
   
   
    dialogs: {
   
   
      type1: {
   
    id: 1, name: '数据1', visible: false },
      type2: {
   
    id: 2, name: '数据2', visible: false }
    } as Record<DialogType, DialogState>,
    showDownloadList: false
  })

  // Actions
  const toggleDialog = (type: DialogType, visible: boolean) => {
   
   
    // 在切换之前-关闭所有对话框
    Object.keys(state.dialogs).forEach(key => {
   
   
      state.dialogs[key as DialogType].visible = false
    })
    // 再打开目标对话框
    state.dialogs[type].visible = visible
  }

  return {
   
   
    ...toRefs(state), // 保持响应式解构
    toggleDialog
  }
})

2. 按钮组件(组合式写法)

<!-- Buttons.vue -->
<template>
  <div class="header-box">
    <el-button type="primary" size="large" @click="openDialog('type1')">
      类型弹窗1
    </<
<think>我们使用组合式API风格来创建Pinia store。在Pinia中,我们可以使用`defineStore`函数,第二个参数可以是一个函数(组合式风格),类似于Vue的`setup()`函数。 在组合式风格的store中: - `ref()` 就是 state 属性 - `computed()` 就是 getters - `function()` 就是 actions 下面是一个使用组合式API风格创建Pinia store的示例: ### 创建组合式API风格的store (userStore) ```typescript // src/stores/user.ts import { defineStore } from &#39;pinia&#39; import { ref, computed, reactive } from &#39;vue&#39; import type { Ref } from &#39;vue&#39; // 定义用户类型 export interface User { id: number name: string email: string role: &#39;admin&#39; | &#39;user&#39; | &#39;guest&#39; } // 使用组合式API风格 export const useUserStore = defineStore(&#39;user&#39;, () => { // state (使用ref或reactive) const currentUser: Ref<User | null> = ref(null) const loading = ref(false) const error: Ref<string | null> = ref(null) // getters (使用computed) const isAdmin = computed(() => { return currentUser.value?.role === &#39;admin&#39; }) const fullUserInfo = computed(() => { return currentUser.value ? `${currentUser.value.name} (${currentUser.value.email})` : &#39;未登录&#39; }) // actions (普通函数) async function login(email: string, password: string) { loading.value = true error.value = null try { // 模拟API调用 const response = await new Promise<{ user: User }>((resolve) => { setTimeout(() => { resolve({ user: { id: 1, name: &#39;张三&#39;, email: email, role: email === &#39;admin@example.com&#39; ? &#39;admin&#39; : &#39;user&#39; } }) }, 1000) }) currentUser.value = response.user } catch (err) { error.value = (err as Error).message || &#39;登录失败&#39; } finally { loading.value = false } } function logout() { currentUser.value = null } function updateUser(partialUser: Partial<User>) { if (currentUser.value) { currentUser.value = { ...currentUser.value, ...partialUser } } } // 必须返回所有需要暴露的状态和函数 return { currentUser, loading, error, isAdmin, fullUserInfo, login, logout, updateUser } }) ``` ### 在组件中使用组合式风格的store 在组件中使用时,和选项式store的用法完全一致,因为Pinia在底层做了统一处理。 ```vue <!-- src/components/UserProfile.vue --> <script setup lang="ts"> import { useUserStore } from &#39;@/stores/user&#39; import { storeToRefs } from &#39;pinia&#39; const userStore = useUserStore() // 使用storeToRefs来解构保持响应性 const { currentUser, isAdmin, fullUserInfo, loading } = storeToRefs(userStore) const handleLogin = () => { userStore.login(&#39;user@example.com&#39;, &#39;password123&#39;) } </script> <template> <!-- 模板代码与之前相同 --> </template> ``` ### 组合式API风格的优势 1. **更灵活**:可以像编写Vue组件的`setup()`一样组织代码 2. **更好的类型推断**:TypeScript类型推断更自然 3. **可组合性**:可以在store内部使用其他组合函数 4. **更细粒度的响应式控制**:可以使用`ref`、`reactive`等响应式API ### 注意事项 1. **必须返回所有需要暴露的状态和方法**,否则在组件中无法访问 2.组合式函数中,**不能使用`this`访问state**,而是直接使用变量 3. 推荐使用`storeToRefs`在组件中解构store,以保持响应性 4. 对于复杂的store,可以将部分逻辑抽取到独立的组合函数中 ### 组合式风格与选项式风格的选择 - **组合式风格**:适合复杂逻辑、需要组合多个功能、或者需要重用部分逻辑的场景 - **选项式风格**:适合简单store、或者习惯于Vuex风格开发的场景 Pinia 同时支持两种风格,开发者可以根据项目需求和个人偏好选择[^1]。 ### 相关问题 1.组合式Pinia store中如何处理异步操作和错误处理? 2. 如何在不同组件之间共享Pinia store的状态? 3.组合式Pinia store中如何实现模块化? 4. 组合式Pinia store与Vue组合函数有什么区别和联系? 5. 如何在组合式Pinia store中使用其他store?</think># Vue3 + Pinia + TypeScript 组合式API风格Store示例 在Vue3中,Pinia提供了两种创建store的方式:选项式API组合式API组合式API风格更接近Vue3的`setup()`语法,提供了更好的类型推断和代码组织能力。 ## 组合式API风格Store示例 ### 用户Store (组合式API风格) ```typescript // src/stores/user.ts import { defineStore } from &#39;pinia&#39; import { ref, computed, reactive } from &#39;vue&#39; import type { User } from &#39;@/types&#39; export const useUserStore = defineStore(&#39;user&#39;, () => { // 状态定义 (使用ref或reactive) const currentUser = ref<User | null>(null) const loading = ref(false) const error = ref<string | null>(null) // 计算属性 (getters) const isAdmin = computed(() => { return currentUser.value?.role === &#39;admin&#39; }) const fullUserInfo = computed(() => { return currentUser.value ? `${currentUser.value.name} (${currentUser.value.email})` : &#39;未登录&#39; }) // 异步操作 (actions) async function login(email: string, password: string) { loading.value = true error.value = null try { // 模拟API调用 const response = await mockApiCall(email, password) currentUser.value = response.user } catch (err) { error.value = (err as Error).message || &#39;登录失败&#39; } finally { loading.value = false } } function logout() { currentUser.value = null } function updateUser(partialUser: Partial<User>) { if (currentUser.value) { currentUser.value = { ...currentUser.value, ...partialUser } } } // 模拟API调用 function mockApiCall(email: string, password: string): Promise<{ user: User }> { return new Promise((resolve, reject) => { setTimeout(() => { if (email.includes(&#39;@&#39;)) { resolve({ user: { id: Math.floor(Math.random() * 1000), name: &#39;张三&#39;, email, role: email.includes(&#39;admin&#39;) ? &#39;admin&#39; : &#39;user&#39; } }) } else { reject(new Error(&#39;无效的邮箱地址&#39;)) } }, 1000) }) } // 暴露状态和方法 return { currentUser, loading, error, isAdmin, fullUserInfo, login, logout, updateUser } }) ``` ### 类型定义 (types.ts) ```typescript // src/types/index.ts export interface User { id: number name: string email: string role: &#39;admin&#39; | &#39;user&#39; | &#39;guest&#39; } export interface Product { id: number name: string price: number description: string } ``` ### 在组件中使用组合式Store ```vue <!-- src/components/UserProfileComposition.vue --> <script setup lang="ts"> import { useUserStore } from &#39;@/stores/user&#39; import { storeToRefs } from &#39;pinia&#39; import { ref } from &#39;vue&#39; const userStore = useUserStore() const { currentUser, isAdmin, loading } = storeToRefs(userStore) // 表单状态 const email = ref(&#39;user@example.com&#39;) const password = ref(&#39;password123&#39;) // 登录函数 const handleLogin = () => { userStore.login(email.value, password.value) } </script> <template> <div class="user-profile"> <h2>组合式API风格示例</h2> <div v-if="loading">登录中...</div> <template v-else> <div v-if="currentUser"> <h3>{{ currentUser.name }}</h3> <p>邮箱: {{ currentUser.email }}</p> <p>角色: {{ currentUser.role }} <span v-if="isAdmin">(管理员)</span></p> <div class="form-group"> <label>更新用户名:</label> <input type="text" :value="currentUser.name" @input="userStore.updateUser({ name: ($event.target as HTMLInputElement).value })" /> </div> <button @click="userStore.logout()">退出登录</button> </div> <div v-else class="login-form"> <div class="form-group"> <label>邮箱:</label> <input type="email" v-model="email" /> </div> <div class="form-group"> <label>密码:</label> <input type="password" v-model="password" /> </div> <button @click="handleLogin">登录</button> </div> </template> </div> </template> <style scoped> .user-profile { padding: 20px; border: 1px solid #42b983; border-radius: 8px; max-width: 400px; margin: 20px auto; } .form-group { margin: 10px 0; } label { display: block; margin-bottom: 5px; font-weight: bold; } input { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; } button { margin-top: 15px; padding: 8px 16px; background-color: #42b983; color: white; border: none; border-radius: 4px; cursor: pointer; transition: background-color 0.3s; } button:hover { background-color: #3aa876; } </style> ``` ## 组合式API风格的优势 1. **更好的类型推断**:组合式API天然支持TypeScript,提供更精确的类型推断 2. **代码组织更灵活**:可以像编写Vue组件一样组织store代码 3. **更好的可组合性**:可以轻松提取和重用逻辑片段 4. **更细粒度的响应式控制**:直接使用`ref`、`computed`等响应式API 5. **更简洁的代码**:减少了`this`的使用,代码更简洁 ## 组合式API最佳实践 1. **使用`storeToRefs`**:在组件中解构store时保持响应性 ```typescript const { currentUser, loading } = storeToRefs(userStore) ``` 2. **封装复杂逻辑**:将复杂逻辑提取为独立函数 ```typescript // 在store内部 function fetchUserData() { // 复杂数据获取逻辑 } ``` 3. **使用reactive处理对象**:当状态是对象时,使用`reactive`代替多个`ref` ```typescript const userProfile = reactive({ name: &#39;&#39;, email: &#39;&#39;, preferences: {} }) ``` 4. **组合多个store**:在需要时组合多个store的功能 ```typescript const userStore = useUserStore() const cartStore = useCartStore() const combinedData = computed(() => ({ user: userStore.currentUser, cartItems: cartStore.items })) ``` ## 组合式API vs 选项式API | 特性 | 组合式API | 选项式API | |------|-----------|-----------| | 类型支持 | ⭐⭐⭐⭐ | ⭐⭐⭐ | | 代码组织 | ⭐⭐⭐⭐ | ⭐⭐⭐ | | 学习曲线 | ⭐⭐⭐ | ⭐⭐⭐⭐ | | 逻辑复用 | ⭐⭐⭐⭐ | ⭐⭐ | | 响应式控制 | ⭐⭐⭐⭐ | ⭐⭐⭐ | | 与Vue3一致性 | ⭐⭐⭐⭐ | ⭐⭐ | 组合式API风格是Pinia推荐的现代方式,特别适合Vue3项目和TypeScript用户[^1]。 ## 相关问题 1. 如何在组合式Pinia store中处理跨store的依赖关系? 2. 组合式API风格与Vue的`setup()`函数有何异同? 3. 在大型项目中如何组织多个组合式Pinia store? 4. 如何为组合式Pinia store编写单元测试? 5. 组合式Pinia store在服务端渲染(SSR)中的最佳实践是什么?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值