Pinia狀態(tài)管理
Pinia和Vuex的對比
Pinia(發(fā)音為/pi?nj?/,如英語中的“peenya”)是最接近pi?a(西班牙語中的菠蘿)的詞;
Pinia開始于大概2019年,最初是
作為一個實驗為Vue重新設(shè)計狀態(tài)管理
,讓它用起來適合組合式API(Composition API)。從那時到現(xiàn)在,最初的設(shè)計原則依然是相同的,并且目前同時兼容Vue2、Vue3,也并不要求你使用Composition API;
Pinia本質(zhì)上依然是一個狀態(tài)管理的庫,用于
跨組件、頁面
進行狀態(tài)共享(這點和Vuex、Redux一樣);
那么我們不是已經(jīng)有Vuex了嗎?為什么還要用Pinia呢?
Pinia 最初是為了探索 Vuex 的下一次迭代會是什么樣子,結(jié)合了 Vuex 5 核心團隊討論中的許多想法;
最終,團隊意識到Pinia已經(jīng)實現(xiàn)了Vuex5中大部分內(nèi)容,所以最終決定用Pinia來替代Vuex;
與 Vuex 相比,Pinia 提供了一個更簡單的 API,具有更少的儀式,
提供了 Composition-API 風(fēng)格的 API
;最重要的是,在與 TypeScript 一起使用時具有可靠的類型推斷支持;
和Vuex相比,Pinia有很多的優(yōu)勢:
優(yōu)勢一: mutations 不再存在:
- 他們經(jīng)常被認為是非常冗長;
- 他們最初帶來了 devtools 集成,但這不再是問題;
優(yōu)勢二: 更友好的TypeScript支持,Vuex之前對TS的支持很不友好;
優(yōu)勢三: 不再有modules的嵌套結(jié)構(gòu):
- 你可以靈活使用每一個store,它們是通過扁平化的方式來相互使用的;
優(yōu)勢四: 也不再有命名空間的概念,不需要記住它們的復(fù)雜關(guān)系;
Pinia基本使用
??創(chuàng)建Pinia
使用Pinia之前,我們需要先對其進行安裝:
yarn add pinia
npm install pinia
使用pinia我們需要在單獨的js文件中創(chuàng)建一個pinia, 并且在main.js中將其注冊, 如下:
這樣我們項目中就已經(jīng)存在pinia了
import { createPinia } from "pinia";
// 創(chuàng)建pinia
const pinia = createPinia()
// 導(dǎo)出pinia
export default pinia
import { createApp } from 'vue'
import App from './App.vue'
// 導(dǎo)入pinia
import pinia from './stores'
const app = createApp(App)
// 注冊pinia
app.use(pinia)
app.mount('#app')
??創(chuàng)建Store
什么是Store?
一個 Store (如 Pinia)是一個實體,它會持有為綁定到你組件樹的狀態(tài)和業(yè)務(wù)邏輯,也就是保存了全局的狀態(tài);
它有點像始終存在,并且每個人都可以讀取和寫入的組件;
你可以在你的應(yīng)用程序中定義
任意數(shù)量的Store
來管理你的狀態(tài);
Store有三個核心概念(接下來會一一講到):
state、getters、actions;
等同于組件的data、computed、methods;
一旦 store 被實例化,你就
可以直接在 store 上訪問 state、getters 和 actions 中定義的任何屬性
;
定義一個Store:
Store 是使用 defineStore() 定義的, 我們一般都會在一個單獨的js的文件創(chuàng)建store, 不同組件的數(shù)據(jù), 我們會定義在不同的js文件中創(chuàng)建不同的store
由于pinia中可以定義多個store, 所以每一個store它都需要一個
唯一名稱
,作為第一個參數(shù)傳遞;
// 定義關(guān)于counter的store
import { defineStore } from "pinia"
// 調(diào)用defineStore定義store, defineStore返回一個函數(shù)
const useCounter = defineStore("counter", {
state: () => ({
counter: 101
})
})
// 將useCounter函數(shù)導(dǎo)出
export default useCounter
第一個參數(shù) name,也稱為 id,
是必要的
,Pinia 使用它來將 store 連接到 devtools。defineStore()返回的函數(shù)統(tǒng)一使用
useXXX
作為命名方案, 且XXX一般就使用傳入的id,這是約定的規(guī)范;調(diào)用defineStore()返回的函數(shù)才會創(chuàng)建store
Store在它被使用之前是不會創(chuàng)建的,我們可以通過調(diào)用use函數(shù)來使用Store:
<template>
<!-- 展示counterStore.counter的狀態(tài) -->
<h2>{{ counterStore.counter }}</h2>
</template>
<script setup>
// 導(dǎo)入我們自定義關(guān)于counter的store
import useCounter from '../stores/counter';
// 調(diào)用函數(shù)才會創(chuàng)建store, 不調(diào)用不會創(chuàng)建
const counterStore = useCounter()
</script>
注意Store獲取到后, 如果我們想要對其解構(gòu), 不能直接解構(gòu),直接解構(gòu)的話會失去響應(yīng)式:
為了從 Store 中提取屬性同時保持其響應(yīng)式我們有兩種方式
方式一: 解構(gòu)時包裹一層toRefs
方式二: pinia給我們提供了一個方法, 使用
storeToRefs()
方法可以保持數(shù)據(jù)的響應(yīng)式。
方式一
<script setup>
import { toRefs } from 'vue';
import useCounter from '../stores/counter';
const counterStore = useCounter()
// 包裹一層toRefs
const { counter } = toRefs(counterStore)
</script>
方式二
<script setup>
import { storeToRefs } from 'pinia';
import useCounter from '../stores/counter';
const counterStore = useCounter()
// 包裹一層storeToRefs
const { counter } = storeToRefs(counterStore)
</script>
Pinia核心概念State
??state基本使用
state 是 store 的核心部分,因為store是用來幫助我們管理狀態(tài)的。
在 Pinia 中,狀態(tài)被定義為返回初始狀態(tài)的函數(shù);
前面我們創(chuàng)建了一個counter.js文件用于定義counter的store, 接下來我們創(chuàng)建一個urse.js文件, 定義一個用戶信息的store來演示state
在pinia中state和vuex中一樣, state是一個函數(shù), 返回一個對象
import { defineStore } from "pinia"
const useUser = defineStore("user", {
// state定義狀態(tài)
state: () => ({
name: "chenyq",
age: 18,
height: 1.88
})
})
export default useUser
將定義的Store展示到組件中
<template>
<div class="home">
<!-- 展示userStore中的狀態(tài) -->
<h2>{{ name }}</h2>
<h2>{{ age }}</h2>
<h2>{{ height }}</h2>
</div>
</template>
<script setup>
import { storeToRefs } from 'pinia';
// 導(dǎo)入我們自定義的store
import useUser from "../stores/user"
// 調(diào)用函數(shù)創(chuàng)建store
const userStore = useUser()
// 將store中的狀態(tài)解構(gòu)出來
const { name, age, height } = storeToRefs(userStore)
</script>
??state其他操作
讀取和寫入 state
默認情況下,您可以通過 store 實例訪問狀態(tài)來直接讀取, 剛剛我們就是這樣讀取狀態(tài)的
寫入狀態(tài)其實也同理, 通過store實例訪問狀態(tài)直接修改
<template>
<div class="home">
<!-- 展示userStore中的狀態(tài) -->
<h2>{{ name }}</h2>
<h2>{{ age }}</h2>
<h2>{{ height }}</h2>
<button @click="changeInfo">修改信息</button>
</div>
</template>
<script setup>
import { storeToRefs } from 'pinia';
import useUser from "../stores/user"
const userStore = useUser()
const { name, age, height } = storeToRefs(userStore)
function changeInfo() {
// 使用實例訪問狀態(tài), 進行修改
userStore.name = "王老五"
userStore.age = 20
userStore.height = 1.89
}
</script>
重置 State
當我們對某些狀態(tài)進行了修改之后, 我們可以通過調(diào)用 store 上的 $reset() 方法將狀態(tài)
重置到其初始值
;$reset()方法會將所有的狀態(tài)重置到初始值
<template>
<div class="home">
<!-- 展示userStore中的狀態(tài) -->
<h2>{{ name }}</h2>
<h2>{{ age }}</h2>
<h2>{{ height }}</h2>
<button @click="changeInfo">修改信息</button>
<button @click="resetInfo">重置信息</button>
</div>
</template>
<script setup>
import { storeToRefs } from 'pinia';
import useUser from "../stores/user"
const userStore = useUser()
const { name, age, height } = storeToRefs(userStore)
function changeInfo() {
userStore.name = "王老五"
userStore.age = 20
userStore.height = 1.89
}
function resetInfo() {
// 重置狀態(tài)
userStore.$reset()
}
</script>
同時修改多個狀態(tài)
可以調(diào)用 $patch 方法 , 它允許您使用部分“state”對象同時應(yīng)用多個更改;
<template>
<div class="home">
<!-- 展示userStore中的狀態(tài) -->
<h2>{{ name }}</h2>
<h2>{{ age }}</h2>
<h2>{{ height }}</h2>
<button @click="changeInfo">修改信息</button>
</div>
</template>
<script setup>
import { storeToRefs } from 'pinia';
import useUser from "../stores/user"
const userStore = useUser()
const { name, age, height } = storeToRefs(userStore)
function changeInfo() {
// $patch一次性修改多個狀態(tài)
userStore.$patch({
name: "羅三炮",
age: 50,
height: 1.58
})
}
</script>
Pinia核心Getters
??getters基本使用
Getters相當于Store的計算屬性:
它們可以用 defineStore() 中的 getters 屬性定義;
getters中可以定義接受一個state作為參數(shù)的函數(shù);
- 在defineStore中定義getters
import { defineStore } from "pinia"
const useCounter = defineStore("counter", {
state: () => ({
counter: 101
}),
// 定義getters
getters: {
doubleCounter(state) {
return state.counter * 2
}
}
})
export default useCounter
- 直接通過store對象就可以訪問當前store的Getters
<template>
<!-- 訪問當前store的Getters -->
<h2>{{ counterStore.doubleCounter }}</h2>
</template>
<script setup>
import useCounter from "../stores/counter"
const counterStore = useCounter()
</script>
??getters其他操作
Getters中訪問自己的其他Getters
我們可以通過
this來訪問到當前store實例的所有其他屬性
;this相當于是綁定的store實例
- 例如在getter中訪問自己的doubleCounter
getters: {
doubleCounter(state) {
return state.counter * 2
},
doubleCounterAddOne() {
return this.doubleCounter + 1
}
}
Getters也可以返回一個函數(shù),這樣就可以接受參數(shù):
const useCounter = defineStore("counter", {
state: () => ({
counter: 101,
friend: [
{id: 111, name: "chenyq"},
{id: 112, name: "王老五"},
{id: 113, name: "羅三炮"},
]
}),
getters: {
// getter可以返回一個函數(shù)
getfriendById() {
return (id) => {
return this.friend.find(item => item.id == id)
}
}
}
})
<h2>{{ counterStore.getfriendById(111) }}</h2>
<h2>{{ counterStore.getfriendById(112) }}</h2>
當前Getters訪問其他store中的state/getters
// 導(dǎo)入usrUser
import useUser from "./user"
const useCounter = defineStore("counter", {
state: () => ({
counter: 101
}),
getters: {
showMessage(state) {
// 拿到userStore對象, 獲取userStore中的信息
const userStore = useUser()
// 返回自己store的信息拼接上userStore中的信息
return `${state.counter}${userStore.name}`
}
}
})
<h2>{{ counterStore.showMessage }}</h2>
Pinia核心Actions
??Actions基本使用
Actions 相當于組件中的 methods。
可以使用 defineStore() 中的 actions 屬性定義,并且它們非常適合定義業(yè)務(wù)邏輯;
和getters一樣,在action中可以通過
this訪問整個store實例的所有操作
;
const useCounter = defineStore("counter", {
state: () => ({
counter: 101
}),
actions: {
increment() {
this.counter++
}
}
})
<h2>{{ counterStore.counter }}</h2>
<button @click="changeState">+1</button>
<script setup>
import useCounter from "../stores/counter"
const counterStore = useCounter()
function changeState() {
// 通過store實例調(diào)用即可
counterStore.increment()
}
</script>
??Actions異步操作
Actions中是支持異步操作的,并且我們可以編寫異步函數(shù),在函數(shù)中使用await
例如在Actions發(fā)生網(wǎng)絡(luò)請求文章來源:http://www.zghlxwxcb.cn/news/detail-672490.html
import { defineStore } from 'pinia'
const useHome = defineStore("home", {
state: () => ({
// 定義空數(shù)組用于接收網(wǎng)絡(luò)請求數(shù)據(jù)
banners: [],
recommends: []
}),
actions: {
// 支持異步操作
async fetchHomeMultidata() {
// 發(fā)送網(wǎng)絡(luò)請求獲取數(shù)據(jù)
const res = await fetch("http://123.207.32.32:8000/home/multidata")
const data = await res.json()
// 將獲取的數(shù)據(jù)添加到state中
this.banners = data.data.banner.list
this.recommends = data.data.recommend.list
}
}
})
export default useHome
展示網(wǎng)絡(luò)請求獲取到homeStore中的數(shù)據(jù)文章來源地址http://www.zghlxwxcb.cn/news/detail-672490.html
<template>
<div class="about">
<ul v-for="item in homeStore.banners" :key="item.acm">
<li>{{ item.title }}</li>
</ul>
</div>
</template>
<script setup>
import useHome from "../stores/home"
const homeStore = useHome()
// 告知發(fā)送網(wǎng)絡(luò)請求
homeStore.fetchHomeMultidata()
</script>
到了這里,關(guān)于Vue中的Pinia狀態(tài)管理工具 | 一篇文章教會你全部使用細節(jié)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!