Vue 项目中的状态优化一般都会用Pinia替代Vuex,Pinia 是 Vue 生态系统中的一个轻量级状态管理库,作为 Vuex 的替代品,它提供了更简洁的 API 和更好的性能。
模块化管理:使用 Pinia 时,建议将状态拆分为多个 store 模块,以避免单一状态树过于庞大和复杂。这不仅有助于维护,还能提升性能。
懒加载 Store:通过 Pinia 的 defineStore 动态创建 store,当某个 store 仅在特定页面或组件中需要时,可以延迟加载它。这样可以减少应用的初始加载时间。
State 持久化:如果某些状态需要在页面刷新后保持,可以使用 Pinia 的插件功能将状态持久化到 localStorage 或 sessionStorage,避免不必要的网络请求或重新计算。
避免不必要的深度响应:Pinia 允许你明确哪些状态需要响应式,哪些不需要。对于不需要响应式的复杂对象,可以使用 shallowRef 或 shallowReactive 来减少响应式开销。
1. 安装 Pinia 与配置
npm install pinia
设置 Pinia:
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
// 创建 Pinia 实例
const pinia = createPinia()
app.use(pinia)
app.mount('#app')
2. 创建模块化 Store
创建两个 Store 模块:userStore 和 productStore。其中 userStore 将使用状态持久化,productStore 将演示懒加载和避免不必要的深度响应。
stores/userStore.js:
// stores/userStore.js
import { defineStore } from 'pinia'
import { ref, computed, watch } from 'vue'
export const useUserStore = defineStore('user', () => {
// 初始化状态,如果 localStorage 中有存储,优先使用存储的状态
const name = ref(localStorage.getItem('user-name') || 'John Doe')
const age = ref(parseInt(localStorage.getItem('user-age')) || 25)
const doubleAge = computed(() => age.value * 2)
// 监听状态变化,并将其保存到 LocalStorage
watch(
() => name.value,
(newValue) => {
localStorage.setItem('user-name', newValue)
}
)
watch(
() => age.value,
(newValue) => {
localStorage.setItem('user-age', newValue.toString())
}
)
return {
name,
age,
doubleAge,
}
})
stores/productStore.js:
// stores/productStore.js
import { defineStore } from 'pinia'
import { shallowRef } from 'vue'
export const useProductStore = defineStore('product', () => {
// 使用 shallowRef 来避免不必要的深度响应
const products = shallowRef([])
const addProduct = (product) => {
products.value.push(product)
}
return {
products,
addProduct,
}
})
3. 懒加载 Store
productStore 仅在需要时加载,例如在某个特定组件中。
components/ProductList.vue:
<template>
<div>
<h2>Product List</h2>
<ul>
<li v-for="product in products" :key="product.id">{{ product.name }}</li>
</ul>
<button @click="addNewProduct">Add Product</button>
</div>
</template>
<script setup>
import { useProductStore } from '@ stores/productStore'
import { onMounted } from 'vue'
// 懒加载 productStore
const productStore = useProductStore()
const products = productStore.products
const addNewProduct = () => {
productStore.addProduct({ id: Date.now(), name: `Product ${products.length + 1}` })
}
onMounted(() => {
console.log('ProductList component mounted.')
})
</script>
4. 在其他组件中使用 userStore
components/UserProfile.vue:
<template>
<div>
<h2>User Profile</h2>
<p>Name: {{ name }}</p>
<p>Age: {{ age }}</p>
<p>Double Age: {{ doubleAge }}</p>
</div>
</template>
<script setup>
import { useUserStore } from '@/store/userStore'
// 使用 userStore,这个 Store 状态会被持久化
const userStore = useUserStore()
const { name, age, doubleAge } = userStore
</script>
5. 手动实现状态持久化
我们在 userStore 中通过 localStorage 手动实现了状态持久化。如果你需要更加通用的状态持久化插件,可以创建一个简单的 Pinia 插件。
plugins/persistedState.js:
// plugins/persistedState.js
export function createPersistedStatePlugin(options = {}) {
return ({ store }) => {
const { key = store.$id } = options
// 从 LocalStorage 初始化状态
const fromStorage = localStorage.getItem(key)
if (fromStorage) {
store.$patch(JSON.parse(fromStorage))
}
// 订阅状态变化,并将其保存到 LocalStorage
store.$subscribe((mutation, state) => {
localStorage.setItem(key, JSON.stringify(state))
})
}
}
注册插件
import { createPinia } from 'pinia'
import { createPersistedStatePlugin } from './plugins/persistedState'
const pinia = createPinia()
pinia.use(createPersistedStatePlugin())
const app = createApp(App)
app.use(pinia)
app.mount('#app')