一文帶你上手vue3中的pinia
溫馨提示:本文以vue3+vite+ts舉例,vite配置和ts語法側(cè)重較少,比較適合有vuex或者vue基礎(chǔ)的小伙伴們兒查閱。
安裝pinia
1.yarn
yarn add pinia
2.npm
npm install pinia
3.pnpm
pnpm add pinia
1.開始
方式一:在main.ts中直接引入pinia
在 src/main.ts
中引入pinia(根存儲(chǔ)),并傳遞給應(yīng)用程序。
import { createApp } from 'vue' import './style.css' import App from './App.vue' // 1-創(chuàng)建一個(gè) pinia(根存儲(chǔ)) import { createPinia } from 'pinia' const app = createApp(App) // 2-告訴應(yīng)用程序,我們將使用pinia const pinia = createPinia(); // 以插件形式傳遞給app app.use(pinia); app.mount('#app');
方式二(推薦):單獨(dú)開個(gè) .ts 文件引入pinia
在根目錄下新建文件夾,這里我命名為 store
,再在文件夾下新建一個(gè) index.ts
文件( src/store/index.ts
),用以配置和引入pinia。
// 1-創(chuàng)建一個(gè) pinia(根存儲(chǔ)) import { createPinia } from 'pinia' // 2-定義pinia實(shí)例 const pinia = createPinia(); // 3-暴露pinia實(shí)例 export default pinia;
然后在 src/main.ts
中使用。
...... import pinia from '@/store/index.ts' app.use(pinia); ......
其實(shí)和方式一沒啥區(qū)別,只是為了保持 main.ts
文件整潔,并且方便配置pinia。
2.創(chuàng)建倉庫
pinia與vuex差不多,相比于vuex,少了 mutation
和 modules
。
pinia創(chuàng)建倉庫,有選項(xiàng)式寫法和組合式寫法。
選項(xiàng)式Options API寫法
在根目錄下創(chuàng)建一個(gè)文件夾store ( src/store
),在store文件夾中可以創(chuàng)建你的倉庫,比如下面我創(chuàng)建了一個(gè)名為user的倉庫 ( src/store/user.ts
)。
// 選項(xiàng)式寫法 // 1-引入api import { defineStore } from "pinia"; // 2-定義倉庫 const store = defineStore('user', { // 3-設(shè)置組件共享的狀態(tài),相當(dāng)于組件的data state: () => ({ userInfo: { name: '老劉', sex: '男', age: 17, isStudent: false }, token: '5201314', password: '123456', }), // 3-設(shè)置狀態(tài)計(jì)算值,相當(dāng)于組件的computed getters: { name: (state) => state.userInfo.name, sex: (state) => state.userInfo.sex, }, // 3-設(shè)置組件共享的方法,相當(dāng)于組件的methods actions: { addAge() { this.userInfo.age++; } } }); // 最后別忘了把倉庫暴露出去哦 export default store;
組合式Composition API寫法(推薦)
上面的倉庫 ( src/store/user.ts
)組合式寫法如下:
// 組合式寫法 // 1-引入pinia的api import { defineStore } from "pinia"; // 2-引入vue3相關(guān)api import { ref, reactive, computed } from 'vue'; // 3-定義倉庫,注意第二個(gè)參數(shù)需要傳入一個(gè)函數(shù),函數(shù)需要返回一個(gè)對(duì)象! const store = defineStore('user', () => { // 4-在這里面可以像在組件中一樣,使用vue3的API,定義響應(yīng)式數(shù)據(jù) const userInfo = reactive({ name: '老劉', sex: '男', age: 17, isStudent: false }); const token = ref('5201314'); const password = ref('123456'); // 這里computed的作用相當(dāng)于getters const name = computed(() => userInfo.name); const sex = computed(() => userInfo.sex); // 4-還可以定義方法 function addAge() { userInfo.age++; } // 5-然后把需要共享的數(shù)據(jù)或方法,裝進(jìn)一個(gè)對(duì)象,return出去 return { userInfo, token, password, name, sex, addAge } }); // 最后別忘了把倉庫暴露出去哦 export default store;
TIP
還可以在倉庫中使用 watch
、 watchEffect
等vue3的API喔~。
import { ref, reactive, computed, watch } from 'vue'; const store = defineStore('user', () => { const userInfo = reactive({ age: 17, }); // 使用vue3的watch()函數(shù),可以對(duì)倉庫狀態(tài)進(jìn)行監(jiān)聽 watch(() => userInfo.age, (val) => { console.log(val); }); });
3.使用pinia
完成了上面的工作后,我們就可以在組件中愉快地使用pinia啦!
下面以 src/App.vue
作為示例。
(1)引入倉庫
<template> </template> <script setup lang="ts"> // 1-引入剛剛自定義的倉庫,模塊名store 可以自定義 import store from '@/store/user.ts'; // 2-使用倉庫,倉庫實(shí)例名userStore 可以自定義 const userStore = store(); </script>
(2)在組件中訪問倉庫 state 和 getters
在模板和script中,state和getters可以看作倉庫實(shí)例(如 userStore
)的屬性,直接加 .
訪問即可。
<template> <div> <!-- 1-訪問倉庫的計(jì)算屬性getters --> <span>姓名:{{ userStore.name }}</span> <span>性別:{{ userStore.sex }}</span> <!-- 1-訪問倉庫的狀態(tài)state --> <span>年齡:{{ userStore.userInfo.age }}</span> <span>是否學(xué)生:{{ userStore.userInfo.isStudent ? '是' : '否' }}</span> </div> </template> <script setup lang="ts"> import store from '@/store/user.ts'; const userStore = store(); // 1-訪問state和getters,類似于reactive響應(yīng)式數(shù)據(jù)的訪問 console.log('userStore', userStore); console.log('姓名:', userStore.name); console.log('性別:', userStore.sex); console.log('年齡:', userStore.userInfo.age); console.log('是否學(xué)生:', userStore.userInfo.isStudent ? '是' : '否'); </script>
輸出
userStore Proxy(Object) {$id: 'user', $onAction: ƒ, $patch: ƒ, $reset: ƒ, $subscribe: ƒ, …}[[Handler]]: Object[[Target]]: Object[[IsRevoked]]: false
姓名: 老劉
性別: 男
年齡: 17
是否學(xué)生: 否
(3)在組件中使用倉庫 actions
使用倉庫方法與訪問倉庫state類似,倉庫實(shí)例后直接加 .
調(diào)用即可。
<template> <div> <!-- 按鈕點(diǎn)擊,年齡+1 --> <button @click="onAddAge">年齡+1</button> <span>年齡:{{ userStore.userInfo.age }}</span> </div> </template> <script setup lang="ts"> import store from '@/store/user.ts'; const userStore = store(); // 按鈕點(diǎn)擊觸發(fā) function onAddAge() { // 1-使用倉庫的actions userStore.addAge(); } </script>
(4)修改 state
直接修改
與vuex不同,pinia支持在組件中直接修改 state
。
<template> <div> <!-- 按鈕點(diǎn)擊,年齡+1 --> <button @click="onAddAge">年齡+1</button> <span>年齡:{{ userStore.userInfo.age }}</span> </div> </template> <script setup lang="ts"> import store from '@/store/user.ts'; const userStore = store(); // 按鈕點(diǎn)擊觸發(fā) function onAddAge() { // 1-直接修改state userStore.userInfo.age++; } </script>
利用倉庫 actions 進(jìn)行修改
src/store/user.ts
...... const store = defineStore('user', () => { ...... // 1-定義方法 function addAge() { userInfo.age++; } // 2-return出去 return { addAge } }); // 3-導(dǎo)出倉庫 export default store;
src/App.vue
<template> <div> <!-- 按鈕點(diǎn)擊,年齡+1 --> <button @click="onAddAge">年齡+1</button> <span>年齡:{{ userStore.userInfo.age }}</span> </div> </template> <script setup lang="ts"> import store from '@/store/user.ts'; const userStore = store(); // 按鈕點(diǎn)擊觸發(fā) function onAddAge() { // 4-使用倉庫的方法 userStore.addAge(); } </script>
批量變更
通過倉庫實(shí)例(如 userStore
)的 $patch
方法,可以對(duì) state
同時(shí)應(yīng)用多個(gè)更改。
<script setup lang="ts"> ...... function changeState() { console.log(userStore.token); // '5201314' console.log(userStore.password); // '123456' // $patch()接收一個(gè)對(duì)象,對(duì)象內(nèi)的屬性是 需要變更的state // 注意是變更,新增state是無效的! userStore.$patch({ token: '1024', password: '654321' }); console.log(userStore.token); // '1024' console.log(userStore.password); // '654321' } </script>
上面的方法每次進(jìn)行批量修改都需要傳入一個(gè)新對(duì)象,有時(shí)候使用起來并不方便。下面是另一種寫法, $patch
接受一個(gè)函數(shù)來批量修改集合內(nèi)部分對(duì)象。(推薦)
<script setup lang="ts"> ...... function changeState() { // 這里的any是typescript的類型標(biāo)注,可以不用理會(huì) // 回調(diào)函數(shù)的參數(shù)state就是 倉庫目前的state userStore.$patch((state: any) => { state.token = '1024'; state.password = '654321'; }); } </script>
整體替換(不推薦)
通過倉庫實(shí)例(如 userStore
)的 $state
屬性,來為新對(duì)象替換倉庫的整個(gè)狀態(tài)。
pinia官網(wǎng)提到整體替換state的方法,但并未說明是否保留數(shù)據(jù)響應(yīng)式。經(jīng)筆者實(shí)踐,這種方法會(huì)丟失數(shù)據(jù)的響應(yīng)式,所以不推薦使用。
<script setup lang="ts"> ...... function updateStore() { userStore.$state = { userInfo: { name: '老王', sex: '男', age: 66, isStudent: false } }; } </script>
(5)重置 state
通過調(diào)用倉庫實(shí)例上的 $reset()
方法將狀態(tài)重置到其初始值。
<script setup lang="ts"> ...... function resetStore() { userStore.$reset(); } </script>
$reset() 的坑
細(xì)心的你會(huì)發(fā)現(xiàn),倉庫 state
并沒有重置,然后你打開你的的控制臺(tái),你會(huì)驚訝地發(fā)現(xiàn)它報(bào)了這么一個(gè)錯(cuò)誤:
這時(shí)候請(qǐng)你不要慌,先冷靜地看一下報(bào)錯(cuò)信息。
這里翻譯一下: Store "user"是使用setup語法構(gòu)建的,不實(shí)現(xiàn)$reset()
。(猜測(cè)是pinia的缺陷)
所以,根據(jù)報(bào)錯(cuò)信息,這里提供下面兩種解決方案。
使用選項(xiàng)式Options API
使用選項(xiàng)式Options API編寫pinia倉庫,并且在組件中不能用script setup語法,要使用setup函數(shù)進(jìn)行開發(fā)。
src/store/user.ts
...... // 使用選項(xiàng)式Options API編寫倉庫 const store = defineStore('user', { // 3-設(shè)置組件共享的狀態(tài),相當(dāng)于組件的data state: () => ({ userInfo: { name: '老劉', sex: '男', age: 17, isStudent: false }, token: '5201314', password: '123456', }), }); export default store;
src/App.vue
<!-- 這里不能用script setup,否則依然報(bào)錯(cuò) --> <script lang="ts"> import store from '@/store/user.ts'; export default { setup() { const userStore = store(); function resetStore() { // 重置state userStore.$reset(); } // 把響應(yīng)式數(shù)據(jù)或方法return出去 return { userStore, resetStore } }, } </script>
重寫一個(gè) $reset() 方法(推薦)
原理:自定義pinia插件(Plugins),利用 $patch()
重置整個(gè) state
。
在之前創(chuàng)建的pinia配置文件中修改( src/store/index.ts
)。
import { createPinia } from 'pinia'; const pinia = createPinia(); // 1-使用pinia自定義插件 pinia.use(({ store }) => { // 2-獲取最開始的State const initialState = JSON.parse(JSON.stringify(store.$state)); // 3-重寫$reset()方法 store.$reset = () => { // 4-利用$patch()批量變更state,達(dá)到重置state的目的 store.$patch(initialState); } }); export default pinia;
推薦使用這種方法,這樣就可以在 script setup
中愉快地使用pinia啦!
完整示例
倉庫配置
src/store/index.ts
import { createPinia } from 'pinia'; const pinia = createPinia(); pinia.use(({ store }) => { const initialState = JSON.parse(JSON.stringify(store.$state)); store.$reset = () => { store.$patch(initialState); } }); export default pinia;
定義倉庫
src/store/user.ts
// 組合式寫法 import { defineStore } from "pinia"; import { ref, reactive, computed, watch } from 'vue'; const store = defineStore('user', () => { const userInfo = reactive({ name: '老劉', sex: '男', age: 17, isStudent: false }); const token = ref('5201314'); const password = ref('123456'); const name = computed(() => userInfo.name); const sex = computed(() => userInfo.sex); watch(() => userInfo.age, (val) => { console.log(val); }); function addAge() { userInfo.age++; } return { userInfo, token, password, name, sex, addAge } }); export default store;
父組件
src/App.vue
<template> <div> <button @click="resetStore">重置store</button> <h1>我是父組件</h1> <button @click="userStore.userInfo.age++">年齡+1</button> <span class="marginLeft60">姓名:{{ userStore.name }}</span> <span class="marginLeft60">性別:{{ userStore.sex }}</span> <span class="marginLeft60 red">年齡:{{ userStore.userInfo.age }}</span> <span class="marginLeft60 red">是否學(xué)生:{{ userStore.userInfo.isStudent ? '是' : '否' }}</span> <hr> <!-- 使用子組件A --> <son-a /> <hr> <!-- 使用子組件B --> <son-b /> <hr> </div> </template> <script setup lang="ts"> // 引入子組件 import SonA from './components/sonA.vue'; import SonB from './components/sonB.vue'; // 1-引入倉庫 import store from '@/store/user.ts'; // 2-使用倉庫 const userStore = store(); // 重置store function resetStore() { userStore.$reset(); } </script> <style> .marginLeft60 { margin-left: 60px; } .red { color: red; } </style>
子組件A
src/components/sonA.vue
<template> <div> <h2>我是子組件A</h2> <button @click="userStore.userInfo.isStudent = true">入學(xué)</button> <span class="marginLeft60">姓名:{{ userStore.name }}</span> <span class="marginLeft60">性別:{{ userStore.sex }}</span> <span class="marginLeft60 red">年齡:{{ userStore.userInfo.age }}</span> <span class="marginLeft60 red">是否學(xué)生:{{ userStore.userInfo.isStudent ? '是' : '否' }}</span> <grandson /> </div> </template> <script setup lang="ts"> import Grandson from './grandson.vue'; import store from '@/store/user.ts'; const userStore = store(); </script>
子組件B
src/components/sonB.vue
<template> <div> <h2>我是子組件B</h2> <button @click="userStore.userInfo.isStudent = false">畢業(yè)</button> <span class="marginLeft60">姓名:{{ userStore.name }}</span> <span class="marginLeft60">性別:{{ userStore.sex }}</span> <span class="marginLeft60 red">年齡:{{ userStore.userInfo.age }}</span> <span class="marginLeft60 red">是否學(xué)生:{{ userStore.userInfo.isStudent ? '是' : '否' }}</span> </div> </template> <script setup lang="ts"> import store from '@/store/user.ts'; const userStore = store(); </script>
孫組件
src/components/grandson.vue
<template> <div> <h3>我是孫組件</h3> <span class="marginLeft60">姓名:{{ userStore.name }}</span> <span class="marginLeft60">性別:{{ userStore.sex }}</span> <span class="marginLeft60 red">年齡:{{ userStore.userInfo.age }}</span> <span class="marginLeft60 red">是否學(xué)生:{{ userStore.userInfo.isStudent ? '是' : '否' }}</span> </div> </template> <script setup lang="ts"> import store from '@/store/user.ts'; const userStore = store(); </script>
效果圖
以上就是一文帶你上手vue3中的pinia的詳細(xì)內(nèi)容,更多關(guān)于vue3 pinia的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue+element實(shí)現(xiàn)動(dòng)態(tài)換膚的示例代碼
本文主要介紹了vue+element實(shí)現(xiàn)動(dòng)態(tài)換膚的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09vue+vant移動(dòng)端顯示table表格加橫向滾動(dòng)條效果
vant移動(dòng)端顯示table效果,增加復(fù)選框,可以進(jìn)行多選和全選,加橫向滾動(dòng)條,可以看全部內(nèi)容,下面通過本文給大家分享vue+vant移動(dòng)端顯示table表格加橫向滾動(dòng)條效果,感興趣的朋友跟隨小編一起看看吧2024-06-06vue中window.addEventListener(‘scroll‘,?xx)失效的解決
這篇文章主要介紹了vue中window.addEventListener(‘scroll‘,?xx)失效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07vue單頁面打包文件大?首次加載慢?nginx帶你飛,從7.5M到1.3M蛻變過程(推薦)
這篇文章主要介紹了vue單頁面打包文件大?首次加載慢?nginx帶你飛,從7.5M到1.3M蛻變過程,需要的朋友可以參考下2018-01-01vue中關(guān)于redirect(重定向)初學(xué)者的坑
這篇文章主要介紹了vue中關(guān)于redirect(重定向)初學(xué)者的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08vue實(shí)踐---根據(jù)不同環(huán)境,自動(dòng)轉(zhuǎn)換請(qǐng)求的url地址操作
這篇文章主要介紹了vue實(shí)踐---根據(jù)不同環(huán)境,自動(dòng)轉(zhuǎn)換請(qǐng)求的url地址操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09