23年用vuex進行狀態管理out了,都開始用pinia啦!

2023-03-03 18:01:02

1 Vue2專案中,Vuex狀態管理工具,幾乎可以說是必不可少的了。而在Vu3中,尤大大推薦我們使用pinia(拍你啊)進行狀態管理,咱得聽話,用就完了。

使用之前我們來看一下,使用 pinia 給我們帶來哪些好處?

官網介紹:Pinia 是 Vue 的儲存庫,它允許您跨元件/頁面共用狀態
個人理解:在我看來就是變態版 vuex,聽說是為了尊重原作者,所以給改名了叫 pinia(拍你啊)

看看都有啥變態的
  1. 相容 vue2、vue3,該說不說這一點 vuex 也支援
  2. Typescript 支援更友好
  3. 體積小,1kb 左右,vuex 350k 以上
  4. 支援外掛擴充套件和伺服器端渲染 ssr
  5. pinia 只有 state、getter、action,拋棄了 Vuex 中的 Mutation。
    1. mutation 幹啥了?vuex 中改/處理變資料來源 store 資料的集合(唯一的),必須是同步。
      1. 我們需要在元件裡面呼叫 dispatch() 方法提交 Actions
      2. Actions 再通過 commit 方法提交 mutations
      3. 通過 mutations 裡面的方法修改 state(資料)
      4. 最後 render 到元件裡面
    2. 為啥同步?
      1. mutation 中如果寫非同步 Le,commit 在觸發 mutation 事件時,非同步的回撥函數不知道是什麼時候執行的,所以在 devtools 中難以追蹤變化了
  6. pinia 中 action 支援同步和非同步。上面也說了下為啥,還不知道為啥可以搜一下為啥。
  7. 不用建立各個模組巢狀了。Vuex 中才分不同模組我們需要用到 modules:模組化拆分。在 pinia 中每個 store 都是獨立的,互相不影響,沒啥事。

用 vue3 demo 講解 pinia 的使用。

1、搭建專案、安裝以來

Vue3 + TS + Vite 專案
參考:Vue3.x 全家桶+vite+TS-搭建 Vue3.x 專案
參考:vue3 學習

1). 建立專案

npm create vite@latest my-vite-app --template vue-ts

2). 安裝 pinia 依賴

npm install pinia

3).全域性掛在 pinia

我們在 main.ts 主檔案中,引入 pinia 的createPinia 方法, 建立根儲存,以方便全域性呼叫。

import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia";
const pinia = createPinia();
const app = createApp(App);
app.use(pinia);
app.mount("#app");

2、搞正事,建立資料倉儲 store

應用場景:
. 多個檢視依賴於同一狀態。
. 來自不同檢視的行為需要變更同一狀態。
我們需要用到 pinia 的defineStore() 方法,建立一個 store。

defineStore:

  • 引數 1:(name) store 的唯一標識 id。也就是 vuex 中 modules:模組化拆分的模組標識
  • 引數 2:(options) 相關設定,通俗來講就是資料,和修改資料的一系列方法 state、getter、action 都在這裡

建立後編寫程式碼如下:

/src/store/piniaStore.ts

import { defineStore } from 'pinia'
export const useUsersStore = defineStore('pinia_id', {
})

3、元件中呼叫 pinia

我們在上一步已經引入了 pinia,並且在 store 檔案下建立了一個叫 piniaStore 的 store,接下來讓我們看看在元件裡如何呼叫

<script setup lang="ts">
    import { useUsersStore } from "../src/store/piniaStore";
    const store = useUsersStore();
    console.log(store);
</script>

方法很簡單一個 useUsersStore 方法搞定,我們看一下列印內容:

$dispose: f $dispose()$id: "pinia_id"
$onAction: f ()
$patch: f $patch(partialStateOrMutator)
$reset: f Sreset()
$subscribe: Ssubscribe(callback, options2 = )  const removeSubscription = addSuoscription(suhotUpdate: (newStore) => f...]

也就這些哈,沒啥玩楞。

4、state 屬性

我們往 store 里加點資料來源 state 吧!

export const useUsersStore = defineStore("pinia_id", {
  state: () => {
    return {
      age: 20,
      message:'我來存放公共資料哈!'
      sex: "男",
    };
  },
});

4.1 讀取 state 資料

回想一下 vuex 怎麼存取的

  • 元件中存取 State 中資料的第一種方式:
this.$store.state.全域性資料名稱
  • 元件中存取 State 中資料的第二種方式:
// 從vuex中按需匯入mapState函數
import { mapState } from 'vuex'
// 將全域性資料 因設為當前元件的計算屬性
computed: {
    ...mapState(['count'])
}

pinia 如何存取呢?
其實我們定義的 state 資料就在 useUsersStore 內,我們自行就可以直接拿到相關資料

<template>
  <p>描述:{{ message }}</p>
  <p>年齡:{{ age }}</p>
  <p>性別:{{ sex }}</p>
</template>
<script setup lang="ts">
    import { ref } from "vue";

    import { useUsersStore } from "../src/store/piniaStore";
    const store = useUsersStore();

    const message = ref<string>(store.message);
    const age = ref<number>(store.age);
    const sex = ref<string>(store.sex);
</script>

當然我們也可以用 es6 解構賦值來優化一下

const { message, age, sex } = store;

4.2 多個元件使用 state

我們使用 pinia 更想做到多元件公用統一資料,新建一個 Child.vue 其實用法很簡單直接呼叫使用即可

<template>
  <p>我是一個子元素</p>
  <p>描述:{{ message }}</p>
  <p>年齡:{{ age }}</p>
  <p>性別:{{ sex }}</p>
</template>
<script setup lang="ts">
    import { ref } from "vue";
    import { useUsersStore } from "../src/store/piniaStore";
    const store = useUsersStore();
    const { message, age, sex } = store;
</script>

4.3 修改 state 資料

我們在回想一下 vuex 怎麼修改的

  • 有人很暴力:直接 this.$store.state.全域性資料名稱 直接去修改,這種方式很難檢視資料是被什麼元件修改
  • mutation 修改
    ① 只能通過 mutation 變更 store 資料,不可以直接操作 store 中的資料
    ② 通過這種方式雖然操作起來繁瑣,但是可以集中監控所有資料的變化

看看 pinia
其實如果想要修改修改 store 中的資料,可以直接重新賦值

<template>
  <img alt="Vue logo" src="./assets/logo.png" />

  <p>年齡:{{ age }}</p>

  <button @click="changeAge">年齡不對</button>
</template>
<script setup lang="ts">
    import child from './child.vue';
    import { useUsersStore } from "../src/store/piniaStore";
    const store = useUsersStore();
    const { nage,} = store;
    const changeAge = () => {
        store.age = 26;
        console.log(store);
    };
</script>

列印可也看出。store 中 age 資料發生了改變,再看看,呦呵,頁面沒響應。
我們可能去監聽 store 資料變化然後重新整理頁面,但有個更好的方法等著我們
pinia 的storeToRefs 方法把我們的 state 變得具有響應

import { storeToRefs } from 'pinia';
const store = useUsersStore();
const { message, age, sex } = storeToRefs(store);

4.4 初始化/重置 state 資料

store 的$reset()方法為我們提供了 state 資料重置的可能

<button @click="onResetData">重置store</button>

const onResetData = () => {
  store.$reset();
};

4.5 store 的$patch 方法批次修改資料

  1. 全部調整:此方法需要一次性將 state 中的所有欄位例舉出。
 store.$patch({
    message: "批次搞事情",
    age: 100,
    sex: "女",
  });
  1. 部分修改: $patch 方法接收一個回撥函數,用來修改部分資料
store.$patch((state) => {
  state.items.push({ message: 'shoes', quantity: 1 })
  state.hasChanged = true
})

4.6 直接替換 state

store.$state = { counter: 666, name: 'yup' }

5、getter 屬性

Getter 用於對 Store 中的資料進行加工處理形成新的資料

  • Getter 可以對 Store 中已有的資料加工處理之後形成新的資料,類似 Vue 的計算屬性,不會影響到原本的資料
  • Store 中的資料發生變化,Getter 的資料也會跟著變化

5.1 新增 getter

export const useUsersStore = defineStore("pinia_id", {
  state: () => {
    return {

      age: 25,
      sex: "男",
    };
  },
  getters: {
    getAddAge: (state) => {
      return state.age + 100;
    },
  },
});

5.2 呼叫 getter

  1. 直接在標籤上使用 getter 定義的方法,這種方式是響應式的,可以直接監聽到變化,其實 state 標籤直接呼叫也是一樣相應的
<template>
  <p>新年齡:{{ store.getAddAge }}</p>
  <button @click="patchStore">批次修改資料</button>
</template>
<script setup lang="ts">
import { useUsersStore } from "../src/store/piniaStore";
const store = useUsersStore();
// 批次修改資料
const patchStore = () => {
  store.$patch({

    age: 100,
    sex: "女",
  });
};
</script>
  1. getter 中呼叫其它 getter
    這裡需要注意一下,不要箭頭函數
export const useUsersStore = defineStore("users", {
  state: () => {
    return {

      age: 25,
      sex: "男",
    };
  },
  getters: {
    getAddAge: (state) => {
      return state.age + 100;
    },
    getNameAndAge(): string {
      return this.name + this.getAddAge; // 呼叫其它getter
    },
  },
});

5.3 getter 傳參

上文提到 getter 與計算屬性差不多,想想計算屬性怎麼傳遞的呢?

computed:{
    getNewName(){
        function (str){
            return str+this.name
        }
    }
}

看一下 getter 的

 <p>新年齡:{{ store.getAddAge(1100) }}</p>
 getters: {
    getAddAge: (state) => {
      return (num: number) => state.age + num;
    },
    getNameAndAge(): string {
      return this.name + this.getAddAge; // 呼叫其它getter
    },
  },

6. actions 屬性

對資料進行邏輯加工,完成默寫特定的業務邏輯。和 vue 元件程式碼中的 methods 相似,存放一些處理業務邏輯的方法。

6.1 新增 actions

export const useUsersStore = defineStore("pinia_id", {
  state: () => {
    return {
      message: "",
      age: 25,
      sex: "男",
      name:'王'
    };
  },
  getters: {
    getAddAge: (state) => {
      return (num: number) => state.age + num;
    },
    getNameAndAge(): string {
      return this.name + this.getAddAge; // 呼叫其它getter
    },
  },
  actions: {
    saveName(name: string) {
      this.name = name;
    },
  },
});

6.2 使用 actions

const saveName = () => {
  store.saveName("我用來呼叫action內的方法");
};

至此文章結束!