Pinia 是 Vue 的一个官方库,用于状态管理。
首先创建一个 Vue3 项目,具体方式可以参考这里。
安装 Pinia:
npm install pinia
创建一个 pinia 实例 (根 store) 并将其传递给应用:
import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
import { defineStore } from "pinia"
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
通过函数defineStore
定义 Store,返回值useCounterStore
同样是一个函数,且通常以useXXXStore
的方式命名。第一个参数是 Store 的名称,第二个参数是对象,其中包含三部分:
state
:Store 的状态(数据)getters
:计算属性(类似于computed
的返回值)actions
:Store 的方法通常,会使用更简洁的组合式 API 进行定义:
import { defineStore } from "pinia";
import { computed,ref } from "vue";
export const useCountStore = defineStore('count', () => {
// 定义 state
const count = ref(0)
// 定义 getter
const doubleCount = computed(() => { return count.value * 2 })
// 定义 action
const increment = () => {
count.value++
}
return {count, doubleCount, increment}
})
<script setup>
import {useCountStore} from '@/stores/count'
const countStore = useCountStore()
</script>
<template>
<button @click="countStore.increment">{{ countStore.count }}</button>
<div>{{ countStore.doubleCount }}</div>
</template>
Store 的 Action 中不仅可以定义同步方法,也可以定义异步方法:
import axios from "axios";
import { defineStore } from "pinia";
import { ref } from "vue";
const API_URL = 'http://geek.itheima.net/v1_0/channels'
export const useListStore = defineStore('list', ()=>{
const list = ref([])
const getList = async ()=>{
const result = await axios.get(API_URL)
list.value = result.data.data.channels
}
return {list, getList}
})
使用异步方法的方式与同步方法相同:
<script setup>
import {useListStore} from '@/stores/list'
const listStore = useListStore()
listStore.getList()
</script>
<template>
<li v-for="channel in listStore.list" :id="channel.id">{{ channel.name }}</li>
</template>
像上面展示的那样,通常都需要在使用 Store 时使用.
操作符引用其属性或方法,你可能期望使用解析操作符对 Store 对象解析,以更方便地使用:
<script setup>
import {useCountStore} from '@/stores/count'
const countStore = useCountStore()
const {count, doubleCount, increment} = countStore
</script>
<template>
<button @click="increment">{{ count }}</button>
<div>{{ doubleCount }}</div>
</template>
实际上这段示例是有 bug 的,因为这里的countStore
解析后,获取的属性count
和doubleCount
都是普通数值,而不是响应式数据(Ref 对象),所以并不会随着点击按钮而发生变化。
要想从 Store 中解析出响应式数据,要使用storeToRefs
函数包裹 Store 实例:
<script setup>
import { useCountStore } from '@/stores/count'
import { storeToRefs } from 'pinia'
const countStore = useCountStore()
const { increment } = countStore
const { count, doubleCount } = storeToRefs(countStore)
</script>
<template>
<button @click="increment">{{ count }}</button>
<div>{{ doubleCount }}</div>
</template>
需要注意的是,storeToRefs
方法返回的对象中是不包含 Store 的 Action 的,所以要以解析的方式获取方法依然要解析原始的 Store 实例。
对 Store 相关的数据 debug 可以使用 Vue 的 debug 插件 Vue.js devtools - Microsoft Edge Addons: