Vue生態(tài)的新成員Pinia的詳細(xì)介紹
Pinia是Vue應(yīng)用程序的狀態(tài)管理方案,是Vuex核心團(tuán)隊(duì)成員開發(fā)。感覺更像是常規(guī)的舊 javascript 導(dǎo)入模塊,實(shí)現(xiàn)了很多Vuex5的提案。
Pinia同時(shí)支持Vue2和Vue3,不過下面展示的例子都是使用Vue3,Pinia的版本是Pinia@2.0.9。
Vue2和Vue3使用的Pinia版本有一點(diǎn)不同,因此請查看官方 Pinia 文檔以獲取更多信息。
安裝和配置
可以使用npm或者yarn安裝Pinia
yarn add pinia@next # 或者 使用npm npm install pinia@next
安裝完畢后,找到Vue應(yīng)用程序的文件 main.js (vue-cli初始化的項(xiàng)目,一般都命名為main.js)。從Pinia的npm包中導(dǎo)入createPinia,然后使用Vue的use方法作為插件使用
//main.js import { createApp } from 'vue' import App from './App.vue' import router from './router' import { createPinia } from 'pinia' createApp(App) ? .use(router) ? .use(createPinia()) ? .mount('#app')
接下來,我們?nèi)?chuàng)建一個(gè) store
Store核心
Pinia的Store不同于Vuex的Store。使用Vuex的時(shí)候,整個(gè)應(yīng)用程序只有一個(gè)主要Store(雖然你可以拆成不同的模塊,但是還是一個(gè)主存儲(chǔ)出口)。然而,今天的主角Pinia則是開箱即用的模塊化設(shè)計(jì),不在需要一個(gè)主要的Store,可以創(chuàng)建不同的Store。例如,我們可以創(chuàng)建一個(gè)登錄用戶Store。
// store/loggedInUser.js import { defineStore } from 'pinia' export const useLoggedInUserStore = defineStore({ // id 是必填的,并且所有 Store 中唯一。因?yàn)镻inia會(huì)將它在devtools顯示 ? id: 'loggedInUser', ? state () { ? ? return { ? ? ? name: '太涼', ? ? ? age: 18, ? ? ? email: 'fake@email.com' ? ? } ? }, ? getters: {}, ? actions: {} })
上面我們創(chuàng)建了一個(gè)登錄用戶Store,接下來我們可以直接在組件中引用,然后在setup函數(shù)中調(diào)用useLoggedInUserStore
<template> ? <div>PiniaApage</div> ? <div>姓名:{{user.name}}</div> ? <div>年齡:{{user.age}}</div> </template> <script> import { useLoggedInUserStore } from '@/store/loggedInUser.js' export default { ? name: 'PiniaDemoPage1', ? setup () { ? ? const user = useLoggedInUserStore() ? ? return { ? ? ? user ? ? } ? } } </script>
這與 Vuex 有很大不同,Vuex 的Store 會(huì)自動(dòng)掛載到 當(dāng)前Vue的實(shí)例,可以通過this.$store的方式調(diào)用。然而,Pania 方式也讓開發(fā)人員 更清楚Store來自哪里,因?yàn)樗菢?biāo)準(zhǔn)的 Javascript 模塊導(dǎo)入,可以看到是從哪個(gè)文件導(dǎo)入的。
假如,你不用 Composition API的方式,你仍然可以借助一些輔助函數(shù)使用Pinia,下面將會(huì)詳細(xì)說,這里先提一嘴。
State
我們將 Store 中的 state 屬性設(shè)置為一個(gè)函數(shù),該函數(shù)返回一個(gè)包含不同狀態(tài)值的對(duì)象。這與我們在組件中定義data的方式非常相似。事實(shí)上,唯一的區(qū)別是屬性名稱:狀態(tài)與數(shù)據(jù)
import { defineStore } from 'pinia' export const useLoggedInUserStore = defineStore({ // id 是必填的,并且所有 Store 中唯一。因?yàn)镻inia會(huì)將它在devtools顯示 ? id: 'loggedInUser', ? state () { ? ? return { ? ? ? name: '太涼', ? ? ? age: 18, ? ? ? email: 'fake@email.com' ? ? } ? }, ? getters: {}, ? actions: {} })
現(xiàn)在,為了從組件中訪問 loginUserStore 的狀態(tài),我們只需要引用我們需要的Store,這種方式非常優(yōu)雅。完全不需要像Vuex那樣從嵌套對(duì)象中找到我們需要的Store。
<template> ? <div>PiniaApage</div> ? <div>姓名:{{user.name}}</div> ? <div>年齡:{{user.age}}</div> </template> <script> import { useLoggedInUserStore } from '@/store/loggedInUser.js' export default { ? name: 'PiniaDemoPage1', ? setup () { ? ? //不用在想以前那個(gè) user.state.name的方式獲取了 ? ? const user = useLoggedInUserStore() ? ? return { ? ? ? user ? ? } ? } } </script>
警告,不能結(jié)構(gòu)user,因?yàn)槟菢訒?huì)失去響應(yīng)式。下面的方式是錯(cuò)誤的。
? const {name, email} = useLoggedInUserStore()
如果你使用的不是Composition API的方式,而是Option API的方式。可以通過Pinia的mapState函數(shù)獲取 State數(shù)據(jù)。Pinia的mapState函數(shù) 和Vuex的mapState雖然名字相同,但是使用方式完全不同。
Pinia的mapState函數(shù)的第一個(gè)參數(shù)是必須是之前創(chuàng)建的Store,第二個(gè)參數(shù)是Store中的state的屬性值??创a
//PageComponent.vue <template> ? <h1>你好, 我是 {{name}},我來自地球</h1> ? <h2>聯(lián)系郵箱:{{email}}</h2> </template> <script> import {mapState} from 'pinia' export default{ ? computed:{ ? ? ...mapState(useLoggedInUserStore, ['name','email']) ? } } </script>
總結(jié):
- 定義Pinia的state,和組件的data的方式是一樣
- 需要在組件間中手動(dòng)導(dǎo)入我們需要的Store模塊,這樣做的好處是明確知道數(shù)據(jù)的來源,更符合標(biāo)準(zhǔn)的 Javascript
Getters
Pinia中的getters和Vuex中的getters 的作用是相同的,都是作為組件的計(jì)算屬性(computed)。
創(chuàng)建getters的方式有兩種,一種是通過this關(guān)鍵字,一種是通過state具體看代碼
//store/usePostsStore.js import { defineStore } from 'pinia' export const usePostsStore = defineStore({ ? id: 'PostsStore', ? state: ()=>({ posts: ['post 1', 'post 2', 'post 3', 'post 4'] }), ? getters:{ ? ? // 傳統(tǒng)函數(shù)方式 ? ? postsCount: function(){ ? ? ? return this.posts.length ? ? }, ? ? // 傳統(tǒng)函數(shù)簡寫方式 ? ? postsCount2(){ ? ? ? return this.posts.length ? ? }, ? ? // 箭頭函數(shù) ? ? postsCount3:(state)=>state.posts.length, ? ? // ? 不能用箭頭函數(shù)+this的方式,這種方式this指向不對(duì) ? ? // postsCount: ()=> this.posts.length ? } })
接下來看Composition API方式的組件中如何使用創(chuàng)建的getters,其實(shí)用法和state相同。看代碼
<template> ? <div>PiniaBpage</div> ? <div> 總數(shù):{{postsStore.postsCount}}</div> </template> <script> import { usePostsStore } from '@/store/usePostsStore.js' export default { ? name: 'PiniaBpage', ? setup () { ? ? const postsStore = usePostsStore() ? ? return { ? ? ? postsStore ? ? } ? } } </script>
如果是Option API 的組件,不能像Vuex那樣通過mapGetters輔助函數(shù)獲取。因?yàn)镻inia沒有mapGetters輔助函數(shù),Pinia中消費(fèi)getters還是借助 mapState輔助函數(shù)
<template> ? <div> 總數(shù):{{postsCount}}</div> </template> <script> import { mapState } from 'pinia' import { usePostsStore } from "@/store/PostsStore"; export default { ? computed:{ ? ? ...mapState(usePostsStore, ['postsCount']) ? } }; </script>
Actions
Pinia不同于Vuex,Pinia提供了單一的方式更改state的值,在Pinia中沒有mutations,只有action方式。先來看一下Pinia的action怎么用吧。上代碼
- 直接通過this找到對(duì)應(yīng)的state修改
- 通過.$patch 函數(shù)方法
- 通過.$patch 對(duì)象方法
import { defineStore } from 'pinia' export const usePostsStore = defineStore({ ? id: 'PostsStore', ? state: () => ({ ? ? posts: ['post 1', 'post 2', 'post 3', 'post 4'], ? ? user: { postsCount: 2 }, ? ? age:18, ? ? errors: [] ? }), ? getters: { ? ? postsCount: function () { ? ? ? return this.posts.length ? ? }, ? ? postsCount2 () { ? ? ? return this.posts.length ? ? }, ? ? // 箭頭函數(shù) ? ? postsCount3: (state) => state.posts.length ? }, ? actions: { ? ? insertPost () { ? ? ? //方式1:直接通過this找到對(duì)應(yīng)的state修改 ? ? ? this.posts.push(`post_${Date.now()}`) ? ? ? this.user.postsCount++ ? ? }, ? ? removePost () { ? ? ? //方式2:通過.$patch 函數(shù)方法 ? ? ? this.$patch((state) => { ? ? ? ? state.posts.shift() ? ? ? ? state.user.postsCount++ ? ? ? }) ? ? ?? ? ? ? //通過.$patch 對(duì)象方法 ? ? ? this.$patch({ ? ? ? ? age:30 ? ? ? }); ? ? } ? } })
以上展示了三種更改Pinia的State方式。
如果是 Composition API使用方式
<template> ? <div>PiniaBpage</div> ? <div> 總數(shù):{{postsStore.postsCount}}</div> ? <ul> ? ? <li ? ? ? v-for="item in postsStore.posts" ? ? ? :key="item" ? ? > ? ? ? {{item}} ? ? </li> ? </ul> ? <button @click="handleAdd">新增</button> ? <button @click="handleRemove">刪除</button> ? <button @click="handleBeforeAdd">新增到前面</button> </template> <script> import { usePostsStore } from '@/store/usePostsStore.js' ?? export default { ? name: 'PiniaBpage', ? setup () { ? ? const postsStore = usePostsStore() ? ? // 新增 ? ? const handleAdd = () => { ? ? ? postsStore.insertPost() ? ? } ? ? // 刪除 ? ? const handleRemove = () => { ? ? ? postsStore.removePost() ? ? } ? ? // 新增到前面,也可以在這里通過$patch修改,同樣這里也可以直接修改 ? ?const ?handleBeforeAdd=()=>{ ? ? ?postsStore.$patch((state) => { ? ? ? ? state.posts.shift() ? ? ? ? state.user.postsCount++ ? ? ?}) ? ?} ? ? return { ? ? ? postsStore, ? ? ? handleAdd, ? ? ? handleRemove, ? ? ? handleBeforeAdd ? ? } ? } } </script>
如果是 Options API使用方式,需要借助 輔助函數(shù) mapActions
// PostEditorComponent.vue <template> ? <input type="text" v-model="post" /> ? <button @click="insertPost(post)">保存</button> </template> <script> import {mapActions} from 'pinia' import { usePostsStore } from '@/store/PostsStore'; export default{ ? data(){ ? ? return { post: '' } ? },? ? methods:{ ? ? ...mapActions(usePostsStore, ['insertPost']) ? } } </script>
其實(shí)Pinia的action使用非常靈活
- 可以在組件或者其他actions里面調(diào)用
- 可以在其他的Store的actions里面調(diào)用
import { useAuthStore } from './auth-store' export const useSettingsStore = defineStore('settings', { ? state: () => ({ ? ? // ... ? }), ? actions: { ? ? async fetchUserPreferences(preferences) { ? ? ? const auth = useAuthStore() ? ? ? if (auth.isAuthenticated) { ? ? ? ? this.preferences = await fetchPreferences() ? ? ? } else { ? ? ? ? throw new Error('User must be authenticated') ? ? ? } ? ? }, ? }, })
- 支持同步和異步
- 可以支持靈活的參數(shù)
- ...............
Vue Devtools
在 Vue 2 中,Pania 支持在 Vuex 選項(xiàng)卡中查看狀態(tài),甚至可以看到時(shí)間軌跡。時(shí)間軌跡的標(biāo)簽幾乎沒有在 Vuex 中使用時(shí)那么好。
至于 Vue 3,Pania 僅支持在 devtools 中查看狀態(tài),不支持時(shí)間軌跡功能。然而,這實(shí)際上比 Vuex 為 Vue 3 提供的要多,因?yàn)樗谧钚碌拈_發(fā)工具中根本不支持。
最后
快速回顧一下 Pinia 最顯著的功能,以幫助你去快速了解Pinia,并應(yīng)用于項(xiàng)目中
- 由 Vue.js 核心團(tuán)隊(duì)成員維護(hù)
- 感覺更像是常規(guī)的舊 javascript 導(dǎo)入模塊,將操作為方法調(diào)用,直接在store上訪問狀態(tài)等。
- 不再mutations
- 與 Vue Devtools 集成
結(jié)論
Pinia雖然是Vue生態(tài)的新成員,但是事實(shí)證明Pinia是最優(yōu)前途的狀態(tài)管理解決方案,具有直觀的API,模塊化,清晰導(dǎo)入來源。
參考文獻(xiàn)
Pinia, an Alternative Vue.js Store
官網(wǎng)
到此這篇關(guān)于Vue生態(tài)的新成員Pinia的詳細(xì)介紹的文章就介紹到這了,更多相關(guān)Vue Pinia內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue實(shí)現(xiàn)歌手列表字母排序下拉滾動(dòng)條側(cè)欄排序?qū)崟r(shí)更新
這篇文章主要介紹了vue實(shí)現(xiàn)歌手列表字母排序,下拉滾動(dòng)條側(cè)欄排序?qū)崟r(shí)更新,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-05-05詳解基于vue-cli3快速發(fā)布一個(gè)fullpage組件
這篇文章主要介紹了詳解基于vue-cli3快速發(fā)布一個(gè)fullpage組件,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-03-03關(guān)于elementUi表格合并行數(shù)據(jù)并展示序號(hào)
這篇文章主要介紹了關(guān)于elementUi表格合并行數(shù)據(jù)并展示序號(hào),通過給table傳入span-method方法可以實(shí)現(xiàn)合并行或列,方法的參數(shù)是一個(gè)對(duì)象,感興趣的朋友可以學(xué)習(xí)一下2023-04-04vue2安裝vue-router報(bào)錯(cuò)的解決方法
vue-router的安裝不是理想化的,會(huì)出現(xiàn)問題,需要靜下心認(rèn)真研究,熬過去就會(huì)懂得更多,這篇文章主要給大家介紹了關(guān)于vue2安裝vue-router報(bào)錯(cuò)的解決方法,需要的朋友可以參考下2022-03-03vuex獲取state對(duì)象中值的所有方法小結(jié)(module中的state)
這篇文章主要介紹了vuex獲取state對(duì)象中值的所有方法小結(jié)(module中的state),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04element-ui中up-load組件實(shí)現(xiàn)圖片上傳回顯
在項(xiàng)目開發(fā)的時(shí)候很多人都會(huì)用到圖片上傳,本文主要介紹了element-ui中up-load組件實(shí)現(xiàn)圖片上傳回顯,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01