1. stores/index.ts
文件
在 index.ts
中创建 store
实例并封装了注册逻辑,这样可以方便地将整个 Pinia 实例注册到 Vue 应用中。代码如下:
import type { App } from 'vue'
import { createPinia } from 'pinia'
const store = createPinia()
// 全局注册 store
export function setupStore(app: App<Element>) {
app.use(store)
}
export * from './modules/blockAuthor'
createPinia
实例化了一个 Pinia 状态管理对象。setupStore
函数将 store 注册到传入的 Vue 应用实例app
上,使得整个应用都可以访问到 store。export * from './modules/blockAuthor'
导出了blockAuthor
模块中定义的内容,这样其他文件就可以直接从stores
中引入它。
2. stores/modules/blockAuthor.ts
文件
blockAuthor.ts
定义了一个具体的 store,用于管理用户的“网格权限”。具体代码如下:
import { defineStore } from 'pinia'
import { StoreName } from '../storesName'
import { ref } from 'vue'
import { sysGridAPI } from '@/api/gridBlock/api'
export const useBlockAuthorStore = defineStore(StoreName.BLOCKAUTHOR, () => {
/**
* 用户网格权限
*/
const authorList = ref([])
/**
* 更新 userBlockAuthor
*/
async function updateUserBlockAuthor() {
try {
const res = await sysGridAPI.findGridBlockByUser()
authorList.value = res.data.map((item) => item.id)
console.log('pinia-store: updateUserBlockAuthor', updateUserBlockAuthor)
} catch (error) {
console.error('Failed to update user block author:', error)
}
}
updateUserBlockAuthor()
return {
authorList
}
})
authorList
是一个ref
类型的响应式数组,用于保存用户的权限列表。updateUserBlockAuthor
是一个异步函数,调用sysGridAPI.findGridBlockByUser()
接口获取用户权限数据并将其存入authorList
中。- 最后返回了
authorList
,使得在其他组件中可以访问。
3. 在组件中使用
你在组件中这样使用 useBlockAuthorStore
:
import { useBlockAuthorStore } from '@/stores'
const authorList = authorStore.authorList
useBlockAuthorStore
通过 import
引入,并使用后即可访问 authorList
等数据,组件内就可以通过 authorList
实现响应式的数据展示或操作。
4. stores/storesName.ts
这种做法有助于集中管理 store 名称,使得在多个文件中引用 store 时保持一致,避免了硬编码字符串带来的拼写错误风险。这种方式非常适合在项目中有多个 store 模块时使用,便于维护和扩展。
export const enum StoreName {
BLOCKAUTHOR = 'blockAuthor'
}
解释
const enum
是 TypeScript 提供的特性,用于定义常量枚举,它会在编译时直接内联字符串值(如'blockAuthor'
),减少运行时的额外代码开销。BLOCKAUTHOR
是一个键,'blockAuthor'
是它的值。通过这种方式,BLOCKAUTHOR
可以直接代表'blockAuthor'
字符串。
好处
- 避免硬编码字符串:在代码中不直接写
'blockAuthor'
,而是通过StoreName.BLOCKAUTHOR
来引用,这样减少了手动输入字符串带来的错误。 - 提升可读性:通过在枚举中添加注释,可以清晰表达每个值的含义。
- 提高性能:
const enum
在编译时会直接替换为对应的字符串,不会生成多余的对象,因此性能更好。
面试题
1. Pinia 是什么?为什么要用它?
- 答:Pinia 是 Vue 3 的官方状态管理库,它是 Vuex 的替代品,专为 Vue 3 和 Composition API 设计。与 Vuex 相比,Pinia 提供了更简洁的 API,更好的 TypeScript 支持,以及更小的体积和更高的性能。它允许我们定义 store、管理状态和处理逻辑,使得 Vue 应用的状态管理更加灵活、模块化和可扩展。
2. 如何在 Vue 3 中使用 Pinia?
- 答:在 Vue 3 中使用 Pinia,首先需要创建 Pinia 实例并将其传递给 Vue 应用。然后,可以通过
defineStore
定义 store,并通过useStore
在组件中访问 store。- 步骤:
- 在
main.ts
中创建 Pinia 实例并注册:import { createPinia } from 'pinia' const pinia = createPinia() app.use(pinia)
2. 使用
defineStore
定义一个 store:import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ name: 'John Doe', age: 30 }), actions: { setName(name: string) { this.name = name } } })
3. 在组件中使用 store:
import { useUserStore } from '@/stores/user' const userStore = useUserStore() userStore.setName('Jane Doe') console.log(userStore.name)
3. Pinia 与 Vuex 有什么区别?
- 答:Pinia 和 Vuex 都是用于管理全局状态的库,但它们有一些关键区别:
- 响应式系统:Pinia 使用 Vue 3 的 Proxy 实现响应式,性能和灵活性上更优。Vuex 使用
Object.defineProperty
,相比之下不如 Pinia 高效。 - API 设计:Pinia 的 API 更简洁、直观。它使用 Composition API 风格,支持
defineStore
和直接返回对象的方式来定义 store,而 Vuex 更加偏向于 options API 风格。 - TypeScript 支持:Pinia 提供了更好的 TypeScript 支持,能够更好地推导类型,减少手动声明类型的需求。
- 模块化:Pinia 的 store 是通过
defineStore
定义的,每个 store 都是一个独立的模块,具有清晰的结构。
- 响应式系统:Pinia 使用 Vue 3 的 Proxy 实现响应式,性能和灵活性上更优。Vuex 使用
-
4. 如何在 Pinia 中使用异步操作?
- 答:在 Pinia 中,异步操作通常通过
actions
来处理。在actions
中定义异步函数,可以像操作同步数据一样处理异步逻辑。示例如下:import { defineStore } from 'pinia' import { api } from '@/api' export const useUserStore = defineStore('user', { state: () => ({ userData: null }), actions: { async fetchUserData() { try { const response = await api.getUserData() this.userData = response.data } catch (error) { console.error('Failed to fetch user data', error) } } } })
5. 如何在 Pinia 中使用 getters?
- 答:Pinia 支持使用 getters 来派生状态数据。getters 允许我们定义基于状态的计算值。例如,如果你有一个
user
的 store,你可以定义一个 getter 来计算用户的全名:import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ firstName: 'John', lastName: 'Doe' }), getters: { fullName: (state) => `${state.firstName} ${state.lastName}` } })