首先,可以先講一下我們為何需要狀態管理。
當我們在進行一個專案時,常常需要資料在元件或頁面間互通有無,我們可能會使用props、emit,或者 provide、inject,又或者直接用 mitt 套件來傳 XD
如果是小專案的話那還好,專案架構一大、你 mitt 來 mitt 去的東西越來越多、越來越亂,更不要說 mitt 很難除錯了XDD
這時候我們就可以透過狀態管理工具,將 store 中的資料分成各類的state 集中管理,當一處的資料改動,其他頁面的資料也會同時更動拉 ~
以往 Vue 所使用的狀態管理工具通常為 Vuex,這次所學習的 pinia 算是 Vuex 的後繼者,詳情可見 這篇介紹文 ~
今天來記錄的是,如何在 vue 的環境中引入 pinia。
首先,當然是先 install 下來XD
npm install pinia
接著來到 你的 main.js
import { createApp } from 'vue'
// 匯入 createPinia 方法
import { createPinia } from 'pinia' // here
// 進行初始化
const pinia = createPinia() // here
const app = createApp(App)
app.use(pinia) // here
app.use(router)
app.mount('#app')
建立一個叫做 stores 的資料夾,然後在裡面開一個 user.js
// 匯入 defineStore 的方法
import { defineStore } from 'pinia'
// 這裡帶入兩個參數,一個是store 名稱、另一個是屬性參數
export default defineStore('userStore', {
// 對應 data
state: () => ({
name: 'Jenny',
wallet: '300'
}),
// 對應 computed (物件形式)
getters: {
getUserName: (state) => `我的名字叫${state.name}`
},
// 對應 methods (物件形式)
actions: {
updateName () {
this.name = 'Angel'
}
}
})
接著找一個頁面,分別在 methods, computed 中展開 mapState 和 mapActions,並帶入兩個參數。
{{name}} {{getUserName}}
<button type="button" @click="updateName">按我</button>
<script>
// 匯入 mapState、 mapActions 方法
import { mapState, mapActions } from 'pinia'
// 匯入 你剛剛建立好的store
import userStore from '@/stores/user'
export default {
methods:{
// 這裡帶入兩個參數 : 一個是Store,另一個是要帶入的actions
...mapActions(userStore, ['updateName']),
},
computed: {
// 這裡帶入兩個參數 : 一個是Store,另一個是要帶入的state,getters
...mapState(userStore, ['name', 'getUserName'])
},
}
</script>
基本上,我們在 store 所定義的資料,都是 reactive 的
{{user.name}} {{user.wallet}}
{{name}} {{wallet}}
<button type="button" @click="updateName">按我</button>
<button type="button" @click="updateData">updateData</button>
<button type="button" @click="reset">reset</button>
<script>
// 匯入 你剛剛建立好的store
import userStore from '@/stores/user'
import { storeToRefs } from "pinia"
export default {
setup(){
const user = userStore()
// 也可以直接修改從 store 來的資料
// user.name = "Lia"
// 也可以直接把值從 store 給取出來與修改
const { name,wallet,getUserName } = storeToRefs(user);
name.value = "Lia"
// 如果不須雙向綁定(如方法),可以從 user 取
const { updateName } = user
function updateData(){
// 這是另一種修改來自 store 值的方法
user.$patch({
name:"Lia",
wallet:1000
})
}
function reset(){
// 我們可以透過 $reset 來重置資料內容。
user.$reset();
}
}
return {
user,
name,
wallet,
getUserName,
updateName
}
}
</script>
最後渲染畫面看看有沒有吃到資料~
同時,我們也可以打開開發者工具來看看 :
這裡的 status 主要是放一些過渡小動畫。
假如要在 store 裡面引用,如以下用法 :
// 匯入 defineStore 的方法
import { defineStore } from 'pinia'
import statusStore from './statusStore'// 新增
const status = statusStore() // 新增
// 這裡帶入兩個參數,一個是store 名稱、另一個是屬性參數
export default defineStore('userStore', {
// 對應 data
state: () => ({
name: 'Jenny',
wallet: '300'
}),
// 對應 computed (物件形式)
getters: {
getUserName: (state) => `我的名字叫${state.name}`
},
// 對應 methods (物件形式)
actions: {
updateName () {
this.name = 'Angel'
status.isBounced = true // 取用statusStore的 state(data) (新增)
}
}
})
在使用 pinia 後使用路由跳轉時,會出現以下錯誤。
Uncaught ReferenceError: Cannot access 'useAuthStore' before initialization
at axiosroot.ts
根據這篇的解釋,我們需要更改一下 main.js的設定
import { createApp, markRaw } from 'vue'
// 匯入 createPinia 方法
import { createPinia } from 'pinia' // here
// 進行初始化
const pinia = createPinia() // here
const app = createApp(App)
app.use(pinia) // here
// 在每個store 添加 pinia
pinia.use(({ store }) => {
store.$router = markRaw(router)
})
app.use(router)
app.mount('#app')
寫得很清楚!謝謝教學。
另外想問,有看過有人在 options API 寫 setup() 來引入 store,這個用法跟 data() 雷同,實務上是可以這樣寫的嗎?