Vue3---(8)Pinia

目录

简介

安装

 创建pinia实例

Store

Option Store

 Setup Store

使用Store

Store解构

 State

访问State

通过store实例直接访问

重置State

选项式API通过$reset()方法将state重置为初始值

组合式Setup中,需要自己创建重置方法

修改State

直接修改

批量修改$patch

通过action修改

订阅state($subscribe)

 Getter

Action 

访问其他 store 的 action

订阅 action($onAction)

面试重点

Pinia 和 Vuex 的区别是什么?

 Pinia 如何实现响应式?

如何优雅处理异步操作的错误?​

在组件外使用 Pinia Store得应用场景


简介

符合直觉的 Vue.js 状态管理库,类型安全、可扩展性以及模块化设计。 甚至让你忘记正在使用的是一个状态库。

安装

yarn add pinia
# 或者使用 npm
npm install pinia

 创建pinia实例

src/main.ts 中创建并使用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')

Store

Store (如 Pinia) 是一个保存状态和业务逻辑的实体,它并不与你的组件树绑定。换句话说,它承载着全局状态。它有点像一个永远存在的组件,每个组件都可以读取和写入它。它有三个概念,state,getter和 action,这些概念相当于组件中的 data、 computed 和 methods

只有整个应用都要访问的数据才适合使用store,不要滥用strore

  • Store使用defineStore()定义,第一个参数必须是独一无二且必需
  • 第二各参数可以接受两类值:setup函数或option对象(组合式,选项式) 

Option Store

 也可以传入一个带有 stateactions 与 getters 属性的 Option 对象

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

 Setup Store

与 Vue 组合式 API 的 setup 函数相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。

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

  return { count, doubleCount, increment }
})
  • ref() 就是 state 属性
  • computed() 就是 getters
  • function() 就是 actions

注意: 要让 pinia 正确识别 state,你必须在 setup store 中返回 state 的所有属性。这意味着,你不能在 store 中使用私有属性。

使用Store

 使用 

Store解构

使用storeToRefs()对Store提取属性并保持响应式,和toRefs的区别在于storeToRefs会跳过所有的action以及非响应式的属性

 State

在Option Store中state是一个返回初始状态的函数,在setup Store中,你定义的每一项数据都是state

import { defineStore } from 'pinia'

const useStore = defineStore('storeId', {
  // 为了完整类型推理,推荐使用箭头函数
  state: () => {
    return {
      // 所有这些属性都将自动推断出它们的类型
      count: 0,
      name: 'Eduardo',
      isAdmin: true,
      items: [],
      hasChanged: true,
    }
  },
})

访问State

通过store实例直接访问

const store = useStore()

store.count++

重置State

选项式API通过$reset()方法将state重置为初始值

const store = useStore()

store.$reset()

组合式Setup中,需要自己创建重置方法

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)

  function $reset() {
    count.value = 0
  }

  return { count, $reset }
})

修改State

直接修改

countStore.sum = 666

批量修改$patch

countStore.$patch({
  sum:999,
  name:'测试'
})

通过action修改

import { defineStore } from 'pinia'

export const useCountStore = defineStore('count', {
  actions: {
    //加
    increment(value:number) {
      if (this.sum < 10) {
        //操作countStore中的sum
        this.sum += value
      }
    },
    //减
    decrement(value:number){
      if(this.sum > 1){
        this.sum -= value
      }
    }
  },
})

组件中调用action

// 使用countStore
const countStore = useCountStore()

// 调用对应action
countStore.incrementOdd(n.value)

订阅state($subscribe)

通过 store 的 $subscribe() 方法侦听 state 及其变化。比起普通的 watch(),使用 $subscribe() 的好处是 subscriptions 在 patch 后只触发一次

cartStore.$subscribe((mutation, state) => {
  // import { MutationType } from 'pinia'
  mutation.type // 'direct' | 'patch object' | 'patch function'
  // 和 cartStore.$id 一样
  mutation.storeId // 'cart'
  // 只有 mutation.type === 'patch object'的情况下才可用
  mutation.payload // 传递给 cartStore.$patch() 的补丁对象。

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

 Getter

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

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

 注意:可以通过 this 访问到整个 store 实例但(在 TypeScript 中)必须定义返回类型

Action 

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

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

 action可以是异步的,实际开发中可能通过async await 调用api接口之类的业务请求,并且可以通过this访问整个store实例

访问其他 store 的 action

将其他store引入,然后得到对应引入的store

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')
      }
    },
  },
})

订阅 action($onAction)

通过 store.$onAction() 来监听 action 和它们的结果。传递给它的回调函数会在 action 本身之前执行。after 表示在 promise 解决之后,允许你在 action 解决后执行一个回调函数。同样地,onError 允许你在 action 抛出错误或 reject 时执行一个回调函数。

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()

面试重点

Pinia 和 Vuex 的区别是什么?

  • 取消 mutations,直接通过 actions 同步/异步修改状态
  • 基于 Composition API 设计,天然支持 TypeScript
  • 模块化无需嵌套,每个 Store 独立
  • 更简洁的 API(defineStore + ref/reactive

 Pinia 如何实现响应式?

  • 底层使用 reactive() 包裹 state
  • Getters 自动缓存计算结果(类似 computed

如何优雅处理异步操作的错误?​

 在 Action 中统一捕获,try,catch进行错误捕获

在组件外使用 Pinia Store得应用场景

在路由守卫中判断用户是否登录,或者工具函数中进行状态处理 

你可能感兴趣的:(vue,javascript,vue.js,pinia)