一文帶你上手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(根存儲),并傳遞給應(yīng)用程序。
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
// 1-創(chuàng)建一個 pinia(根存儲)
import { createPinia } from 'pinia'
const app = createApp(App)
// 2-告訴應(yīng)用程序,我們將使用pinia
const pinia = createPinia();
// 以插件形式傳遞給app
app.use(pinia);
app.mount('#app');方式二(推薦):單獨(dú)開個 .ts 文件引入pinia
在根目錄下新建文件夾,這里我命名為 store ,再在文件夾下新建一個 index.ts 文件( src/store/index.ts ),用以配置和引入pinia。
// 1-創(chuàng)建一個 pinia(根存儲)
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)建一個文件夾store ( src/store ),在store文件夾中可以創(chuàng)建你的倉庫,比如下面我創(chuàng)建了一個名為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-定義倉庫,注意第二個參數(shù)需要傳入一個函數(shù),函數(shù)需要返回一個對象!
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)一個對象,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ù),可以對倉庫狀態(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 方法,可以對 state 同時應(yīng)用多個更改。
<script setup lang="ts">
......
function changeState() {
console.log(userStore.token); // '5201314'
console.log(userStore.password); // '123456'
// $patch()接收一個對象,對象內(nèi)的屬性是 需要變更的state
// 注意是變更,新增state是無效的!
userStore.$patch({
token: '1024',
password: '654321'
});
console.log(userStore.token); // '1024'
console.log(userStore.password); // '654321'
}
</script>上面的方法每次進(jìn)行批量修改都需要傳入一個新對象,有時候使用起來并不方便。下面是另一種寫法, $patch 接受一個函數(shù)來批量修改集合內(nèi)部分對象。(推薦)
<script setup lang="ts">
......
function changeState() {
// 這里的any是typescript的類型標(biāo)注,可以不用理會
// 回調(diào)函數(shù)的參數(shù)state就是 倉庫目前的state
userStore.$patch((state: any) => {
state.token = '1024';
state.password = '654321';
});
}
</script>整體替換(不推薦)
通過倉庫實(shí)例(如 userStore )的 $state 屬性,來為新對象替換倉庫的整個狀態(tài)。
pinia官網(wǎng)提到整體替換state的方法,但并未說明是否保留數(shù)據(jù)響應(yīng)式。經(jīng)筆者實(shí)踐,這種方法會丟失數(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ì)心的你會發(fā)現(xiàn),倉庫 state 并沒有重置,然后你打開你的的控制臺,你會驚訝地發(fā)現(xiàn)它報(bào)了這么一個錯誤:

這時候請你不要慌,先冷靜地看一下報(bào)錯信息。
這里翻譯一下: Store "user"是使用setup語法構(gòu)建的,不實(shí)現(xiàn)$reset() 。(猜測是pinia的缺陷)
所以,根據(jù)報(bào)錯信息,這里提供下面兩種解決方案。
使用選項(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)錯 -->
<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>重寫一個 $reset() 方法(推薦)
原理:自定義pinia插件(Plugins),利用 $patch() 重置整個 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的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue 使用Jade模板寫html,stylus寫css的方法
這篇文章主要介紹了vue 使用Jade模板寫html,stylus寫css的方法,文中還給大家提到了使用jade注意事項(xiàng),需要的朋友可以參考下2018-02-02
ElementUI下拉組件el-select一次從后端獲取選項(xiàng)并設(shè)置默認(rèn)值方式
這篇文章主要介紹了ElementUI下拉組件el-select一次從后端獲取選項(xiàng)并設(shè)置默認(rèn)值方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01
vuejs2.0子組件改變父組件的數(shù)據(jù)實(shí)例
本篇文章主要介紹了vuejs2.0子組件改變父組件的數(shù)據(jù)實(shí)例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05
Vue3+Vite實(shí)現(xiàn)一個Markdown編輯器組件
在現(xiàn)代前端開發(fā)中,Markdown 編輯器廣泛應(yīng)用于博客,文檔,Wiki,代碼注釋等場景,本文將使用 Vue 3 構(gòu)建一個簡單的 Markdown 編輯器組件,感興趣的小伙伴可以了解下2025-04-04
ElementUI實(shí)現(xiàn)在下拉列表里面進(jìn)行搜索功能詳解
有時候需要用到下拉列表全選和搜索,下面這篇文章主要給大家介紹了關(guān)于ElementUI實(shí)現(xiàn)在下拉列表里面進(jìn)行搜索功能的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04
vue中使用animate.css實(shí)現(xiàn)炫酷動畫效果
這篇文章主要介紹了vue中使用animate.css實(shí)現(xiàn)動畫效果,我們使用它,只需要寫很少的代碼,就可以實(shí)現(xiàn)非常炫酷的動畫效果,感興趣的朋友跟隨小編一起看看吧2022-04-04
Vue路由重復(fù)點(diǎn)擊報(bào)錯問題及解決
這篇文章主要介紹了Vue路由重復(fù)點(diǎn)擊報(bào)錯問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04
vue3中ref在js中為什么需要.value才能獲取/修改值舉例詳解
這篇文章主要介紹了vue3中ref在js中為什么需要.value才能獲取/修改值的相關(guān)資料,Vue3中ref用于基本類型需.value訪問,reactive用于對象自動解構(gòu),Vue2用Object.defineProperty,Vue3用Proxy,需要的朋友可以參考下2025-05-05

