Pinia - vue 的状态管理库

1. 介绍

Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态。

2. 安装和注册Pinia

  • 安装pinia
yarn add pinia
  • 注册pinia
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const pinia = createPinia()
const app = createApp(App)

app.use(pinia)
app.mount('#app')

3. 使用pinia

  • 定义store
import { defineStore } from 'pinia'

export const useTodosStore = defineStore('todos', {
  // 类似于data
  state: () => ({
    todos: [],
  }),
  // 类似于computed计算属性
  getters: {
    finishedTodos(state) {
      return state.todos.filter((todo) => todo.isFinished)
    }
  },
  // 类似于vue2中的methods
  actions: {
    addTodo(text) {
      this.todos.push({ text })
    },
  },
})
  • 使用store中的数据和方法
<script setup>
import { useTodosStore } from '@/stores/todos'
import { storeToRefs } from 'pinia';

// 可以在组件中的任意位置访问 `store` 变量 
const todosStore = useTodosStore();

// 通过storeToRefs 辅助函数解构 state 和 getters, 使其具有响应性, store 是reactive包装的对象,对于结构出的对象的属性访问,则需要加.value。
const {todos, finishedTodos} = storeToRefs(todosStore);

// action 可以直接从store 解构
const { addTodo } = todosStore;

// 通过computed计算属性,也可以使其具有相应性
const finishedTodos = computed(() => todosStore.finishedTodos);
</script>

4. 定义store的方式

Store (如 Pinia) 是一个保存状态和业务逻辑的实体,它并不与你的组件树绑定。换句话说,它承载着全局状态。它有点像一个永远存在的组件,每个组件都可以读取和写入它。
一个 Store 应该包含可以在整个应用中访问的数据。另一方面,你应该避免在 Store 中引入那些原本可以在组件中保存的本地数据

  • Option Store
    state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),而 actions 则是方法 (methods)。
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  },
})
  • Setup Store
    ref() 就是 state 属性, computed() 就是 getters, function() 就是 actions
export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)
  function increment() {
    count.value++
  }

  return { count, doubleCount, increment }
});

5. 解构store

使用storeToRefs解构store中的数据,以保持其响应性

<script setup>
import { storeToRefs } from 'pinia'
const store = useCounterStore()
// `name` 和 `doubleCount` 是响应式的 ref
// 同时通过插件添加的属性也会被提取为 ref
// 并且会跳过所有的 action 或非响应式 (不是 ref 或 reactive) 的属性
const { name, doubleCount } = storeToRefs(store)
// 作为 action 的 increment 可以直接解构
const { increment } = store
</script>

6. 操作state

  • 访问state
    默认情况下,你可以通过 store 实例访问 state,直接对其进行读写。
const store = useStore()
store.count++
  • 重置state
const store = useStore()
store.$reset()
  • 变更state
store.$patch({
  count: store.count + 1,
  age: 120,
  name: 'DIO',
})

store.$patch((state) => {
  state.items.push({ name: 'shoes', quantity: 1 })
  state.hasChanged = true
})
  • 替换state
store.$state = { count: 24 }
store.$patch({ count: 24 })
// 替换整个应用的初始state
pinia.state.value = {}
  • 订阅state
<script setup>
const someStore = useSomeStore()
// 此订阅器即便在组件卸载之后仍会被保留
someStore.$subscribe(callback, { detached: true })
</script>

7. 定义与使用getters

Getter 完全等同于 store 的 state 的计算值。 它将接收 state 作为第一个参数, 或者通过this访问其他getter。

  • 定义getter
export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
  }),
  getters: {
    doubleCount: (state) => state.count * 2,
    doubleCountPlusOne() {
      return this.doubleCount + 1
    },
  },
});
  • 访问getter
<script setup>
import { useCounterStore } from './counterStore'

const store = useCounterStore()
</script>

<template>
  <p>Double count is {{ store.doubleCount }}</p>
</template>

8. 定义与使用actions

Action 相当于组件中的 method, 用来定义业务逻辑。action 也可通过this访问整个 store 实例, 并且可以是异步的。

  • 定义action
export const useCounterStore = defineStore('main', {
  state: () => ({
    count: 0,
  }),
  actions: {
    increment() {
      this.count++
    },
    randomizeCounter() {
      this.count = Math.round(100 * Math.random())
    },
  },
})
  • 使用action
<script setup>
const store = useCounterStore()
// 将 action 作为 store 的方法进行调用
store.randomizeCounter()
</script>
<template>
  <!-- 即使在模板中也可以 -->
  <button @click="store.randomizeCounter()">Randomize</button>
</template>
  • 访问其他store的action
import { useAuthStore } from './auth-store'

export const useSettingsStore = defineStore('settings', {
  state: () => ({
    preferences: null,
    // ...
  }),
  actions: {
    async fetchUserPreferences() {
      const auth = useAuthStore()
      if (auth.isAuthenticated) {
        this.preferences = await fetchPreferences()
      } else {
        throw new Error('User must be authenticated')
      }
    },
  },
})

9. 订阅action

你可以通过 store.$onAction() 来监听 action 和它们的结果,这些函数对于追踪运行时错误非常有用.

const unsubscribe = someStore.$onAction(
  ({
    name, // action 名称
    store, // store 实例,类似 `someStore`
    args, // 传递给 action 的参数数组
    after, // 在 action 返回或解决后的钩子
    onError, // action 抛出或拒绝的钩子
  }) => {
    // 为这个特定的 action 调用提供一个共享变量
    const startTime = Date.now()
    // 这将在执行 "store "的 action 之前触发。
    console.log(`Start "${name}" with params [${args.join(', ')}].`)

    // 这将在 action 成功并完全运行后触发。
    // 它等待着任何返回的 promise
    after((result) => {
      console.log(
        `Finished "${name}" after ${
          Date.now() - startTime
        }ms.\nResult: ${result}.`
      )
    })

    // 如果 action 抛出或返回一个拒绝的 promise,这将触发
    onError((error) => {
      console.warn(
        `Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
      )
    })
  }
)

// 手动删除监听器
unsubscribe()
posted @ 2024-04-05 13:57  箫笛  阅读(25)  评论(0编辑  收藏  举报