pinia是一個vue的狀態存儲庫,你可以使用它來存儲、共用一些跨組件或者頁面的數據,使用起來和vuex非常類似。pina相對Vuex來說,更好的ts支持和代碼自動補全功能。本篇隨筆介紹pinia的基礎用法以及持久化存儲的一些用法,供參考學習。 pinia在2019年11月開始時候是一個實驗項目,目... ...
pinia是一個vue的狀態存儲庫,你可以使用它來存儲、共用一些跨組件或者頁面的數據,使用起來和vuex非常類似。pina相對Vuex來說,更好的ts支持和代碼自動補全功能。本篇隨筆介紹pinia的基礎用法以及持久化存儲的一些用法,供參考學習。
pinia在2019年11月開始時候是一個實驗項目,目的就是重新設計一個與組合API匹配的vue狀態存儲。基本原則和原來還是一樣的,pinia同時支持vue2和vue3,且不要求你必須使用Vue3的組合API。不管是使用vue2或者vue3,pinia的API是相同的,文檔是基於vue3寫的。Pinia 是 Vuex4 的升級版,也就是 Vuex5; Pinia 極大的簡化了Vuex的使用,是 Vue3的新的狀態管理工具;Pinia 對 ts的支持更好,性能更優, 體積更小,無 mutations,可用於 Vue2 和 Vue3;Pinia支持Vue Devtools、 模塊熱更新和服務端渲染。
1、pinia的安裝和使用
安裝pinia(https://pinia.vuejs.org/)
npm install pinia
在main.j或者main.ts中引入使用
import { createPinia } from 'pinia'
app.use(createPinia())
下麵就是使用pinia的一個例子。這樣你就創建了一個狀態存儲。
// stores/counter.js import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { state: () => { return { count: 0 } }, // 也可以這樣定義狀態 // state: () => ({ count: 0 }) actions: { increment() { this.count++ }, }, })
在組件中使用:
import { useCounterStore } from '@/stores/counter' export default { setup() { const counter = useCounterStore() counter.count++ // 編輯器會有代碼提示 counter.$patch({ count: counter.count + 1 }) // 也可以使用action來代替 counter.increment() }, }如果你不是很喜歡setup函數和組合API,pinia也有類似vuex的map的功能。你可以用上面的方式定義你的store,但是使用時用mapStores(), mapState(),或者 mapActions():
const useCounterStore = defineStore('counter', { state: () => ({ count: 0 }), getters: { double: (state) => state.count * 2, }, actions: { increment() { this.count++ } } }) const useUserStore = defineStore('user', { // ... }) export default { computed: { // 其他計算屬性 // ... // 可以使用 this.counterStore 和 this.userStore獲取 ...mapStores(useCounterStore, useUserStore) // 可以使用 this.count 和this.double獲取 ...mapState(useCounterStore, ['count', 'double']), }, methods: { // 可以使用 this.increment()調用 ...mapActions(useCounterStore, ['increment']), }, }
與vue4之前的版本相比,pinia的API是有很多不同的,即:
- 去掉了mutation。因為好多人認為mutation是多餘的。以前它方便devtools集成,現在這不是個問題了。
- 不用在寫複雜的ts類型包裝,所有的都是有類型的,API設計的都是儘量符合ts的類型推斷
- 不再使用一個莫名其妙的字元串了,只需要導入一個函數,調用他們就行了,同時還有代碼自動補全
- 不需要動態添加store了,因為它們現在本來就是動態。如果你想,你隨時可以手動去寫一個store。
- 沒有複雜的嵌套模塊了。你仍然可以在一個store中導入其他的store來實現嵌套模塊,但是pinia還是推薦使用一個扁平的結構。但是即使你使用迴圈依賴也沒關係。
- 不再需要命名空間了。因為現在store本來就是扁平結構了。你也可以理解為所有的store本來就有命名空間了。
import { defineStore } from 'pinia' export const useStore = defineStore('main', { // other options... })
上面只是定義了store,在setup函數中調用了useStore()時,才會創建store:
import { useStore } from '@/stores/counter' export default { setup() { const store = useStore() return { // 你可以返回store這個對象,然後就可以在template中使用了 store, } }, }
在store實例化以後,你就可以調用到store中定義的state、getters和actions了。為了讓解構的值還保持響應式,你需要用到storeToRefs()方法。它會給響應式的數據創建ref。
import { storeToRefs } from 'pinia' export default defineComponent({ setup() { const store = useStore() // `name` 和 `doubleCount` 是響應式的 // 插件增加的屬性也會創建ref // 但是會自動跳過action或者不是響應性的屬性 const { name, doubleCount } = storeToRefs(store) return { name, doubleCount } }, })
預設情況下,你可以在store實例上直接獲取或者修改state:
const store = useStore()
store.counter++
也可以調用$reset()方法來把state恢復為初始值:
const store = useStore()
store.$reset()
除了直接修改store里的值store.counter++,你也可以是用$patch方法。你可以同時修改多個值:
store.$patch({ counter: store.counter + 1, name: 'Abalam', })
或者$patch接收一個函數作為參數,來簡化改變數組的寫法:
store.$patch((state) => { state.items.push({ name: 'shoes', quantity: 1 }) state.hasChanged = true })
2、pinia的持久化存儲處理
你可以用$subscribe()來偵聽state的改變,持久化一般存儲在localStorage和sessionStorage。localStorage和sessionStorage差別
localStorage和sessionStorage一樣都是用來存儲客戶端臨時信息的對象。
他們均只能存儲字元串類型的對象(雖然規範中可以存儲其他原生類型的對象,但是目前為止沒有瀏覽器對其進行實現)。
localStorage生命周期是永久,這意味著除非用戶顯示在瀏覽器提供的UI上清除localStorage信息,否則這些信息將永遠存在。
sessionStorage生命周期為當前視窗或標簽頁,一旦視窗或標簽頁被永久關閉了,那麼所有通過sessionStorage存儲的數據也就被清空了。
不同瀏覽器無法共用localStorage或sessionStorage中的信息。相同瀏覽器的不同頁面間可以共用相同的 localStorage(頁面屬於相同功能變數名稱和埠),但是不同頁面或標簽頁間無法共用sessionStorage的信息。這裡需要註意的是,頁面及標 簽頁僅指頂級視窗,如果一個標簽頁包含多個iframe標簽且他們屬於同源頁面,那麼他們之間是可以共用sessionStorage的。
JSON對象提供的parse和stringify將其他數據類型轉化成字元串,再存儲到storage中就可以了,操作的方式:
存:
var obj = {"name":"xiaoming","age":"16"}
localStorage.setItem("userInfo",JSON.stringify(obj));
取:
var user = JSON.parse(localStorage.getItem("userInfo"))
刪除:
localStorage.remove("userInfo);
清空:
localStorage.clear();
pnia 使用訂閱機制subscribe來實現數據的持久化存儲的代碼如下所示。
const instance = useMainStore(); // 訂閱數據變化,變化時存儲 instance.$id 這是storeId instance.$subscribe((mutation, state) => { localStorage.setItem(instance.$id, JSON.stringify(state)); }); //init 初始的時候獲取 const val = localStorage.getItem(instance.$id); if (val) { instance.$state = JSON.parse(val); }
也可以通過watch實現
watch( pinia.state, (state) => { // persist the whole state to the local storage whenever it changes localStorage.setItem('piniaState', JSON.stringify(state)) }, { deep: true } )
但是需要註意,這種方式持久化會提示pinia未安裝掛載,所以需要在pinia掛載後再調用,這裡可以將它封裝成方法導出,在掛載後調用
xport const initStore = () => { const instance = useMainStore(); // 訂閱數據變化,變化時存儲 instance.$id 這是storeId instance.$subscribe((mutation, state) => { localStorage.setItem(instance.$id, JSON.stringify(state)); }); //init 初始的時候獲取 const val = localStorage.getItem(instance.$id); if (val) { instance.$state = JSON.parse(val); } }預設情況下,state偵聽會和組件綁定在一起(如果store是在組件的setup中)。這意味著,當組件卸載時,偵聽會自動被移除。如果你需要在組件被卸載時,偵聽仍然保持,需要給$subscribe()方法傳遞第二個參數true:
export default { setup() { const someStore = useSomeStore() // 組件卸載後,偵聽也會有 someStore.$subscribe(callback, true) // ... }, }
或者watch狀態的變化
watch( pinia.state, (state) => { // 在state改變時,保存在localStorage中 localStorage.setItem('piniaState', JSON.stringify(state)) }, { deep: true } )
3、使用pinia插件持久化存儲
pinia plugin persist官方網站:pinia-plugin-persist
持久化存儲也可以通過安裝插件的方式,安裝 pinia-plugin-persist 來實現。
npm i pinia-plugin-persist --save
使用main.js
import { createPinia } from 'pinia' import piniaPluginPersist from 'pinia-plugin-persist' const store = createPinia() store.use(piniaPluginPersist) createApp(App).use(store).mount('#app')
在對應的store中開啟,數據預設存在 sessionStorage 里,並且會以 storeId 作為 key
import { defineStore } from 'pinia' // 'main' 是storeId export const useMainStore = defineStore('main', { state: () => ({ counter: 2, name: 'Eduardo', isAdmin: true }), // …… // 開啟數據緩存 persist: { enabled: true } })
如果需要自定義key和存儲位置,則修改參數即可。
persist: { enabled: true, strategies: [ //使用插件自定義存儲 { key: 'settings', // key可以自己定義,不填的話預設就是這個store的ID storage: localStorage, } ] },
4、在實際項目中使用pinia
一般項目開發,實際上存儲的內容會比較多,可能根據不同的鍵值模塊進行區分,因此把它們放在一個store/modules裡面,方便的使用引用它來存取設置數據即可。
我們這裡簡單以一個settings的配置信息進行介紹,其中index.ts是一個統一的創建pinia的對象並掛接到全局App上的。
其中index.ts的代碼如下所示。
import type { App } from "vue"; import { createPinia } from "pinia"; import piniaPluginPersist from 'pinia-plugin-persist';//使用插件持久化 const store = createPinia(); store.use(piniaPluginPersist) //使用插件持久化 export function setupStore(app: App<Element>) { app.use(store); } export { store };
因此在main.js裡面引入並掛接pinia即可。
import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import 'normalize.css' // css初始化 import App from './App.vue' import { setupStore } from "/@/store"; const app = createApp(App) setupStore(app) app.use(ElementPlus) app.mount('#app')
這樣我們就可以再次定義一個模塊化的配置信息,以便於管理存儲各種不同類型的內容。
如下麵我們定義一個程式配置信息setttings.ts
import { defineStore } from "pinia"; import { store } from "/@/store"; export type settingType = { title: string; fixedHeader: boolean; hiddenSideBar: boolean; }; export const useSettingStore = defineStore({ id: "settings", state: (): settingType => ({ title: "Vue3 + TypeScript + Element", fixedHeader: false, hiddenSideBar: false }), persist: { enabled: true, strategies: [ //使用插件自定義存儲 { key: 'settings', // key可以自己定義,不填的話預設就是這個store的ID storage: localStorage, } ] }, getters: { getTitle() { return this.title; }, getFixedHeader() { return this.fixedHeader; }, getHiddenSideBar() { return this.HiddenSideBar; } }, actions: { CHANGE_SETTING({ key, value }) { // eslint-disable-next-line no-prototype-builtins if (this.hasOwnProperty(key)) { this[key] = value; } }, changeSetting(data) { this.CHANGE_SETTING(data); } } }); export function useSettingStoreHook() { return useSettingStore(store); }
然後在組件視圖vue或者app.vue中使用即可
<script lang="ts"> import { defineComponent } from "vue"; import { useSettingStoreHook } from "/@/store/modules/settings"; import { storeToRefs } from "pinia"; export default defineComponent({ name: "app", components: { }, setup() { const store = useSettingStoreHook(); const { fixedHeader, title } = storeToRefs(store); return { fixedHeader, title, }; }, methods: { setTitle() { this.title = "Vue3 + TypeScript + Element + Edit"; console.log(this.title); }, }, }); </script>
查看數據修改後,存儲在本地存儲空間中的內容,如下所示。
主要研究技術:代碼生成工具、會員管理系統、客戶關係管理軟體、病人資料管理軟體、Visio二次開發、酒店管理系統、倉庫管理系統等共用軟體開發
專註於Winform開發框架/混合式開發框架、Web開發框架、Bootstrap開發框架、微信門戶開發框架的研究及應用。
轉載請註明出處:
撰寫人:伍華聰 http://www.iqidi.com