Pinia

安装

1
npm install pinia

创建 Pinia 实例

1
2
3
4
5
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';

createApp(App).use(createPinia()).mount('#app');

定义 Store

选项式

1
2
3
4
5
6
7
8
9
10
11
12
13
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0, name: 'xiaowhang' }),
  getters: {
    doubleCount: state => state.count * 2,
  },
  actions: {
    increment() {
      this.count++;
    },
  },
});

可以认为 state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),而 actions 则是方法 (methods)。

组合式

1
2
3
4
5
6
7
8
9
10
11
12
13
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0);
  const name = 'xiaowhang';
  const doubleCount = computed(() => count.value * 2);
  function increment() {
    count.value++;
  }

  return { count, doubleCount, increment };
});

在组合式 Store 中:

  • ref() 就是 state 属性
  • computed() 就是 getters
  • function() 就是 actions

State

state 是 store 的数据 (data)

定义 State

1
2
3
4
5
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0, name: 'xiaowhang' }),
});

在组件中使用

1
2
3
4
5
6
7
8
9
10
11
<template>
  <h2>当前计数为:{{ countStore.count }}</h2>
</template>

<script setup lang="ts">
  // 引入对应的useXxxxxStore
  import { useCounterStore } from '@/store/counter';

  // 调用useXxxxxStore得到对应的store
  const countStore = useCounterStore();
</script>

修改 State

直接修改

1
2
3
const countStore = useCounterStore();

countStore.count++

Patch 补丁

1
2
3
4
5
store.$patch({
  count: store.count + 1,
  age: 120,
  name: 'DIO',
})

Action

Action 相当于组件中的 method。它们可以通过 defineStore() 中的 actions 属性来定义,并且它们也是定义业务逻辑的完美选择。

定义 Action

1
2
3
4
5
6
7
8
9
10
11
12
13
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  actions: {
    increment() {
      this.count++;
    },
    randomizeCounter() {
      this.count = Math.round(100 * Math.random());
    },
  },
});

调用 Action

1
2
3
4
5
6
7
8
9
10
<template>
  <h2>当前计数为:{{ countStore.count }}</h2>
  <button @click="countStore.increment()">计数增加</button>
</template>

<script setup lang="ts">
  import { useCounterStore } from '@/store/counter';

  const countStore = useCounterStore();
</script>

Getter

Getter 完全等同于 store 的 state 的计算值。可以通过 defineStore() 中的 getters 属性来定义它们。推荐使用箭头函数,并且它将接收 state 作为第一个参数

定义 Getter

1
2
3
4
5
6
7
8
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 1 }),
  getters: {
    doubleCount: state => state.count * 2,
  },
});

在组件中使用

1
2
3
4
5
6
7
8
9
10
<template>
  <h2>当前计数为:{{ countStore.count }}</h2>
  <h2>翻倍后为: {{ countStore.doubleCount }}</h2>
</template>

<script setup lang="ts">
  import { useCounterStore } from '@/store/counter';

  const countStore = useCounterStore();
</script>

storeToRefs

借助 storeToRefsstore 中的数据转为 ref 对象,方便在模板中使用(保留响应式)。

1
2
3
4
5
6
7
8
9
10
11
12
<template>
  <h2>当前计数为:{{ count }}</h2>
  <h2>翻倍后为: {{ doubleCount }}</h2>
</template>

<script setup lang="ts" name="Count">
  import { useCounterStore } from '@/store/counter';
  import { storeToRefs } from 'pinia';

  const countStore = useCounterStore();
  const { count, doubleCount } = storeToRefs(countStore);
</script>

订阅 state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
  <h2>当前计数为:{{ count }}</h2>
  <h2>翻倍后为: {{ doubleCount }}</h2>
</template>

<script setup lang="ts" name="Count">
  import { useCounterStore } from '@/store/counter';
  import { storeToRefs } from 'pinia';

  const countStore = useCounterStore();
  const { count, doubleCount } = storeToRefs(countStore);

  countStore.$subscribe((mutation, state) => {
    console.log(mutation, state);

    // 每当状态发生变化时,将整个 state 持久化到本地存储。
    localStorage.setItem('cart', JSON.stringify(state));
  });
</script>

Pinia
http://xiaowhang.github.io/archives/1960634197/
作者
Xiaowhang
发布于
2024年10月16日
许可协议