测试Store策略
理解Store策略的核心概念
Store策略在Vue.js中通常指状态管理方案,尤其是Pinia或Vuex这类库的使用模式。状态管理库的核心目标是解决组件间状态共享问题,当多个组件需要访问同一数据源时,直接通过props传递会变得复杂。Store提供集中式存储,允许任何组件读取或修改状态。
// 基础Pinia store示例
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++
}
}
})
状态初始化与类型安全
创建Store时,类型安全能显著减少运行时错误。在Pinia中,可以通过接口明确state结构。对于复杂应用,建议将状态划分为多个模块化store,每个store负责特定领域的状态。
// 带类型的Pinia store
interface UserState {
name: string
age: number
permissions: string[]
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({
name: '',
age: 0,
permissions: []
}),
actions: {
setUser(user: Partial<UserState>) {
this.$patch(user)
}
}
})
状态持久化实践
浏览器刷新会导致Store状态丢失,需要持久化方案。常见做法是配合localStorage或sessionStorage,可以使用插件如pinia-plugin-persistedstate
简化流程。
// 持久化配置示例
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
// store配置
export const useAuthStore = defineStore('auth', {
state: () => ({
token: null
}),
persist: {
key: 'app-auth',
storage: window.localStorage
}
})
复杂状态变更处理
当多个操作需要修改同一状态时,应该使用actions封装业务逻辑。避免直接在组件中修改store状态,这会使状态变更难以追踪。
// 购物车store示例
export const useCartStore = defineStore('cart', {
state: () => ({
items: [],
discount: 0
}),
actions: {
addItem(product) {
const existing = this.items.find(item => item.id === product.id)
if (existing) {
existing.quantity++
} else {
this.items.push({ ...product, quantity: 1 })
}
this.calculateDiscount()
},
calculateDiscount() {
if (this.items.length > 5) {
this.discount = 0.1
}
}
}
})
组件与Store的交互模式
组件可以通过多种方式与Store交互。计算属性适合派生状态,watch可以响应状态变化执行副作用。对于大型应用,建议使用map辅助函数保持代码整洁。
<script setup>
import { useCounterStore } from './stores/counter'
import { storeToRefs } from 'pinia'
const counter = useCounterStore()
// 使用storeToRefs保持响应性
const { count } = storeToRefs(counter)
function handleClick() {
counter.increment()
}
</script>
<template>
<button @click="handleClick">
Count is: {{ count }}
</button>
</template>
测试Store的策略
单元测试Store需要验证状态变更和actions行为。使用Vitest或Jest时,可以创建store实例并直接调用方法进行断言。
import { setActivePinia, createPinia } from 'pinia'
import { useCounterStore } from './counter'
describe('Counter Store', () => {
beforeEach(() => {
setActivePinia(createPinia())
})
test('increment action', () => {
const counter = useCounterStore()
expect(counter.count).toBe(0)
counter.increment()
expect(counter.count).toBe(1)
})
})
性能优化技巧
大型应用中的Store可能包含大量数据,需要优化策略。使用getters缓存计算结果,对大型数组使用分页加载,考虑使用浅响应式减少不必要的依赖追踪。
export const useProductStore = defineStore('products', {
state: () => ({
allProducts: []
}),
getters: {
featuredProducts(state) {
// 使用计算属性缓存结果
return state.allProducts.filter(p => p.featured)
}
},
actions: {
async loadProducts(page = 1) {
const response = await fetch(`/api/products?page=${page}`)
this.allProducts = [...this.allProducts, ...response.data]
}
}
})
错误处理模式
Store中的异步操作需要统一错误处理机制。可以创建基础store类封装错误处理逻辑,或使用拦截器处理API调用错误。
// 带错误处理的store示例
export const useApiStore = defineStore('api', {
state: () => ({
error: null,
loading: false
}),
actions: {
async callApi(fn) {
this.loading = true
this.error = null
try {
return await fn()
} catch (err) {
this.error = err.message
throw err
} finally {
this.loading = false
}
}
}
})
// 使用示例
const apiStore = useApiStore()
const userStore = useUserStore()
apiStore.callApi(async () => {
await userStore.loadUser()
})
插件开发与扩展
Pinia的插件系统允许扩展store功能。常见插件包括持久化、日志、时间旅行调试等。插件可以访问store实例并拦截各种操作。
// 简单的日志插件
function piniaLogger() {
return ({ store }) => {
store.$onAction(({ name, args, after, onError }) => {
console.log(`Action "${name}" called with`, args)
after(result => {
console.log(`Action "${name}" completed with`, result)
})
onError(error => {
console.warn(`Action "${name}" failed with`, error)
})
})
}
}
const pinia = createPinia()
pinia.use(piniaLogger())
服务层集成模式
将API调用与store逻辑分离可以提高可测试性。创建独立的服务模块处理数据获取,store只负责状态管理。
// api服务模块
export const UserService = {
async getProfile() {
const response = await fetch('/api/profile')
return response.json()
}
}
// store中使用服务
export const useUserStore = defineStore('user', {
actions: {
async loadProfile() {
this.profile = await UserService.getProfile()
}
}
})
响应式状态设计
设计store状态时需要考虑响应式系统的特性。避免直接替换整个state对象,使用$patch进行批量更新。对于嵌套对象,确保使用reactive保持响应性。
export const useNestedStore = defineStore('nested', {
state: () => ({
user: {
name: '',
address: {
city: '',
street: ''
}
}
}),
actions: {
updateAddress(address) {
// 正确做法 - 使用$patch
this.$patch({
user: {
address: {
...this.user.address,
...address
}
}
})
// 错误做法 - 失去响应性
// this.user.address = address
}
}
})
跨Store通信方案
多个store之间可能需要共享数据或协调操作。可以通过在actions中调用其他store实现,或创建顶层store协调子store。
export const useOrderStore = defineStore('order', {
actions: {
async submitOrder() {
const cart = useCartStore()
const user = useUserStore()
if (!user.isLoggedIn) {
throw new Error('需要登录')
}
const response = await OrderService.create({
items: cart.items,
userId: user.id
})
cart.clear()
return response
}
}
})
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:TypeScript深度集成
下一篇:性能优化建议