Pinia是一个比vuex更好用的状态管理库!!!
Pinia
Pinia是西班牙语piña(西班牙语中的“菠萝”)单词的形似。
它是一个状态管理的库,用于跨组件、页面进行状态共享(这点和Vuex、Redux一样),同时兼容Vue2、Vue3,也并不要求你使用Composition API;
Pinia开始于大概2019年,最初是作为一个实验,目的为了探索 Vuex 的下一次迭代会是什么样子,所以它结合了 Vuex 5 核心团队讨论中的许多想法,为Vue重新设计状态管理,让它用起来像组合式API(Composition API)。
最终,团队意识到Pinia已经实现了Vuex5中大部分内容,所以最终决定用Pinia来替代Vuex;
与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的仪式,提供了 Composition-API 风格的 API;
最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持;
和Vuex相比,Pinia有很多的优势:
- 比如mutations 不再存在;
- 更友好的TypeScript支持,Vuex之前对TS(TypeScript)的支持很不友好;
- 不再有modules的嵌套结构,
- 也不再有命名空间的概念。
01. 安装Pinia
npm install pinia
或者:
yarn add pinia
02. 案例入门
目录结构。
与vuex类似,对于pinia的使用,编程习惯上一般创建“stores”文件夹。
也常见取名为“pinia”。
在index.js文件中,创建一个全局的pinia实例。
import {createPinia} from 'pinia'
const pinia = createPinia()
export default pinia
创建好之后,在全局的main.js文件中,使用这个实例。
import {createApp} from 'vue' // 不支持template选项
// import {createApp} from 'vue/dist/vue.esm-browser' // 支持template选项
import App from './App.vue'
import pinia from './stores/index'
createApp(App).use(pinia).mount('#app')
对于业务逻辑,可以根据模块的不同,将需要管理的state抽离,建立单独的文件,比如counter.js。
定义一个Store:
Store 是使用 defineStore() 定义的,
它需要一个唯一名称,作为第一个参数传递;第二个参数为具体的对象。
defineStore返回的是一个函数,在命名上,统一使用useX作为命名方案,这是约定的规范。
使用defineStore定义的store,默认会与创建的全局pinia关联,可以在组件中直接调用,无需其他操作。
import {defineStore} from 'pinia'
// defineStore第一个参数为name,也称为id,是必传参数。唯一标识Store
const useCounter = defineStore('counter',{
state:()=>({
count:99
})
})
export default useCounter
HomeCom.vue
<template>
<h1>Home Page</h1>
<!-- 注意这里与vuex不同 -->
<p>Home中的count计数:{{counterStore.count}}</p>
<button @click="addCount">count+1</button>
</template>
<script setup>
import useCounter from '@/stores/counter'
import {toRefs} from "vue";
import {storeToRefs} from "pinia";
// 执行函数,拿到Store
const counterStore = useCounter()
// pinia支持直接修改state,不必像vuex那样commit一个mutation
function addCount(){
counterStore.count++
}
// 需要注意的是,直接对Store进行解构,会失去响应式
// const {count} = counterStore
// 保留响应式,可以使用vue提供的方法
// const {count} = toRefs(counterStore)
// 保留响应式,pinia也专门提供了一个方法
const {count} = storeToRefs(counterStore)
</script>
<style scoped>
</style>
03. Store
在应用程序中,可以定义任意数量的Store来管理应用程序的状态,
Store有三个核心概念:state、getters、actions;
类似于组件的data、computed、methods;
Store在它被使用之前是不会创建的,使用defineStore函数定义Store时,返回的是一个函数,
调用这个函数,才会创建对应的 store 。
编程习惯:定义Store时,一般以useX的形式。
04. State
对于state,pinia支持直接修改,不同像vuex那样通过mutation。
pinia也支持一次性修改多个state,用 Store.$patch()
,传入一个对象。
对于Store的内部方法,以$开头。
pinia支持直接替换state对象和重置对象。
stores/user.js:
import { defineStore } from 'pinia'
const useUser = defineStore("user", {
state: () => ({
name: "Mark",
age: 18,
score: 100
})
})
export default useUser
Home.vue
<template>
<div class="home">
<h2>Home View</h2>
<h2>name: {{ name }}</h2>
<h2>age: {{ age }}</h2>
<h2>level: {{ score }}</h2>
<button @click="changeState">修改state</button>
<button @click="resetState">重置state</button>
</div>
</template>
<script setup>
import useUser from '@/stores/user'
import { storeToRefs } from 'pinia';
const userStore = useUser()
const { name, age, score } = storeToRefs(userStore)
function changeState() {
// 1.一个个修改状态
// userStore.name = "kobe"
// userStore.age = 20
// userStore.score = 200
// 2.一次性修改多个状态
// userStore.$patch({
// name: "james",
// age: 35
// })
// 3.替换state为新的对象
const oldState = userStore.$state
userStore.$state = {
name: "curry",
score: 200
}
console.log(oldState === userStore.$state) // true。pinia这个功能并不是真的直接替换。
}
function resetState() {
userStore.$reset()
}
</script>
05. Getters
Getters相当于Store的计算属性:
- 用 defineStore() 中的 getters 属性定义;
- getters中可以定义接受一个state作为参数的函数;
counter.js:
// 定义关于counter的store
import { defineStore } from 'pinia'
import useUser from './user'
const useCounter = defineStore("counter", {
state: () => ({
count: 99,
subjects: [
{ id: 111, name: "语文" },
{ id: 112, name: "英语" },
{ id: 113, name: "数学" },
]
}),
getters: {
// 1.基本使用
doubleCount(state) {
return state.count * 2
},
// 2.一个getter引入另外一个getter
doubleCountAddOne() {
// this是store实例
return this.doubleCount + 1
},
// 3.getters也支持返回一个函数
getFriendById(state) {
return function(id) {
for (let i = 0; i < state.subjects.length; i++) {
const subject = state.friends[i]
if (subject.id === id) {
return subject
}
}
}
},
// 4.getters中用到别的store中的数据
showMessage(state) {
// 1.获取user信息
const userStore = useUser()
// 2.获取自己的信息
// 3.拼接信息
return `name:${userStore.name}-count:${state.count}`
}
},
})
export default useCounter
06. Actions
Actions 相当于组件中的 methods。
- 使用 defineStore() 中的 actions 属性定义。
一般网络请求相关的state管理(异步操作),就使用action完成。
import { defineStore } from 'pinia'
const useHome = defineStore("home", {
state: () => ({
banners: [],
recommends: []
}),
actions: {
async fetchHomeMultidata() {
const res = await fetch("http://123.207.32.32:8000/home/multidata")
const data = await res.json()
this.banners = data.data.banner.list
this.recommends = data.data.recommend.list
// return new Promise(async (resolve, reject) => {
// const res = await fetch("http://123.207.32.32:8000/home/multidata")
// const data = await res.json()
// this.banners = data.data.banner.list
// this.recommends = data.data.recommend.list
// resolve("bbb")
// })
}
}
})
export default useHome