{%hackmd BJrTq20hE %} ###### tags: `Vue` `composition API` # 從composition API 開始VUE的生活- Composition API Pinia的全域資料管理! 什麼是Pinia?與option API的寫法請參考 [Vue店商網頁-如何使用Pinia](https://hackmd.io/pkClhheMR9yZ9kqXaNDW_g?view) ## composition API Pinia起手式 1.在vite環境選用Pinia時就會自動建立store資料夾與檔案,如果不是則輸入 ``` npm install pinia ``` 2.在scr下建立stores資料夾 3.在stores建立檔案 4.在main中import pinia範例如下 ```javascript import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' import './assets/main.css' const app = createApp(App) app.use(createPinia()) app.mount('#app') ``` 5.命名規則官方建議以use開頭,我自己會再加個Store結尾。 ### 以vite所預設的pinia範例為例子 以下為vite所建立的count.js的Pinia為例 1.把檔名修改為useCountStore.js 2.基本上Pinia的composition API寫法與一般的composition API基本上相同 見下方範例 ```javascript= // import vue會用到的方法 import { ref, computed } from 'vue' // 以解構的方是import pinia中defineStore的方法 import { defineStore } from 'pinia' // 使用函式表達式命名與檔名一樣的變數並被defineStore('id',callback)賦值 export const useCounterStore = defineStore('counter', () => { // 等於option API的 state: () => { return {count:0}} const count = ref(0) // 等於option API的getters: {doubleCount: (state) => state.counter * 2} const doubleCount = computed(() => count.value * 2) // 等於option API的actions: { increment(){this.count ++}} function increment() { count.value++ } // 與composition一樣要return return { count, doubleCount, increment } }) ``` ### 如何在其他的component或page使用useCountStore.js 1.在想要使用的component import,範例如下 ```javascript= <script setup> // 以解構的方式import import { useCounterStore } from '../stores/useCounterStore.js' // 以解構的方式import storeToRefs這個方法 import { storeToRefs } from 'pinia' // 使用函式表達式給變數countStore賦值,這邊變數會習慣用import進來的檔案名稱並去除use命名,只要看到Store就是與pinia有關。 const countStore = useCounterStore() // 使用解構的方式取得useCounterStore()內的函式 const { increment } = countStore // 也可以使用 countStore.increment()來執行函式 // 使用useCounterStore()的變數,不能直接解構變數,會去除掉proxy也就是解包,會造成資料無法響應式,所以要用到storeToRefs這個方法把變數在塞回ref內 const { count, doubleCount } = storeToRefs(countStore) </script> ``` ```htmlembedded= <template> <h2>count:{{count}}</h2> <h2>doubleCount:{{doubleCount}}</h2> <button @click="increment">click add</button> </template> ``` 結果如下圖,點擊click add 數字就會增加 ![](https://i.imgur.com/TQ8bMhs.jpg) ### pinia使用axios取得資料 1.建立一個檔名為useGetApiDataStore 內容如下 基本上與起手是相同。 ```javascript= import { defineStore } from "pinia"; import { ref } from "vue"; import axios from "axios"; export const useGetApiDataStore = defineStore('getApiData',()=>{ const res = ref([]) // 把axios修改為async await函式 async function getData(url){ try { const data = await axios.get(url) res.value = data.data } catch (error) { console.log(error) } } return{ res, getData } }) ``` 2.在想要使用useGetApiDataStore import範例如下 ```javascript= <script setup> import { useGetApiDataStore } from '../stores/useGetApiDataStore.js' import { storeToRefs } from 'pinia' const url = 'https://randomuser.me/api/' const getApiStore = useGetApiDataStore() const { getData } = getApiStore const { res } = storeToRefs(getApiStore) // 打算以按鈕觸發取得API所以把getData包在函式內,注意參數url直接帶入getData(url)就好, // 如果由function getApi(url){}則會報錯,url不會真的被帶入。 // 原因還要再查查。 function getApi(){ getData(url) } </script> ``` ```htmlembedded= <template> <h2>{{res}}</h2> <button @click="getApi">GetData</button> </template> ``` 點GetData前 ![](https://i.imgur.com/zi3oNUq.jpg) 點GetData就會收到資料 ![](https://i.imgur.com/UUA8jlu.jpg) ### 在store也可以使用其他store的資料 1.建立一個useSetNameStore 2.這個useSetNameStore打算使用上述範例useCountStore的count 3.點擊後名子會從Jay改為May 4.在使用useCountStore的increment函式後count也會增加 ```javascript= // import pinia的方法 import { defineStore, storeToRefs } from "pinia"; import { computed, ref } from "vue"; // 以解構的方是import所要用到的useCounterStore import { useCounterStore } from './useCounterStore' export const useSetNameStore = defineStore('setName', () => { const counterStore = useCounterStore() const { count } = storeToRefs(counterStore) const name = ref('Jay') // 在nameComputed內使用count的內容,因為是proxy所以要用.value解包 const nameComputed = computed(() => { return `${name.value}computed ${count.value}` }) function setName(data) { name.value = data } // 可以把從useCounterStore()的拿到的count在return出去 return { name, setName, nameComputed, count } }) ``` 4.使用useSetNameStore與useCountStore,useSetNameStore是為了變數name、nameComputed、count與setName函式,useCountStore是為了increment函式而已 ```javascript= <script setup> import { useCounterStore } from '../stores/useCounterStore.js' import { useSetNameStore } from '../stores/useSetNameStore.js' const countStore = useCounterStore() const { increment } = countStore const setNameStore = useSetNameStore() const { setName } = setNameStore const { name, nameComputed, count } = storeToRefs(setNameStore) </script> ``` ```htmlembedded= <template> <h2>{{name}}</h2> <h2>computed:{{nameComputed}}</h2> <button @click="setName('May')">setName</button> <h2>count:{{count}}</h2> <button @click="increment">click add</button> </template> ``` 結果如下 什麼沒觸發的時候 ![](https://i.imgur.com/wmYgi3W.jpg) 只觸發useCounterStore()的increment時,useCounterStore()的count的數字會+1,這個影響也會傳送到useSetNameStore(),並且從畫面表現出來 ![](https://i.imgur.com/olPGh5R.jpg) 只觸發useSetNameStore()的setName() ![](https://i.imgur.com/6fha8hF.jpg) 兩者都觸發後 ![](https://i.imgur.com/b97RYvG.jpg) ### pinia的$subscribe與watch 1.$subscribe是pinia的方法可以用來監控pinia目前的狀態與所傳送的內容。 2.watch則是vue所提供監控變數改變時可以執行函式或是通知的方法。 以useSetNameStore為例子在import後在page或component加入watch與$subscribe,範例如下 ```javascript= <script setup> import { useCounterStore } from '../stores/useCounterStore.js' import { useSetNameStore } from '../stores/useSetNameStore.js' const countStore = useCounterStore() const { increment } = countStore const setNameStore = useSetNameStore() const { setName } = setNameStore const { name, nameComputed, count } = storeToRefs(setNameStore) // 監控count這個變數為例子 watch(count,(newValue, oldValue)=>{ console.log('newValue',newValue) console.log('oldValue',oldValue) },{deep:true}) // pinia的$subscribe則是以整個storage為例子監控,參數分別是mutation:pinia的狀態、state:pinia所傳送的值 setNameStore.$subscribe((mutation, state)=>{ console.log('mutation',mutation) console.log('state',state) }) </script> ``` ```htmlembedded= <template> <h2>{{name}}</h2> <h2>computed:{{nameComputed}}</h2> <button @click="setName('May')">setName</button> <h2>count:{{count}}</h2> <button @click="increment">click add</button> </template> ``` 結果點click add出現如下 watch ![](https://i.imgur.com/0dnnxLX.jpg) $subscrib ![](https://i.imgur.com/EdNWMHP.jpg) 其中mutation有mutation.type、mutation.storeId、mutation.payload 不過我還沒搞懂mutation.type、mutation.storeId、mutation.payload這三個是幹嘛用的? state則是pinia所傳送的值,透過state.name、state.count就可以取得了 ![](https://i.imgur.com/kkA7S4b.jpg) 參考資料 --- [Vue3 + Vite 快速上手 Get Startrd EP6 - Pinia 的全域資料管理!](https://www.youtube.com/watch?v=_Vvi9EVtuW4)