Pinia状态管理器学习笔记,持续记录

15,092次阅读
没有评论

共计 5311 个字符,预计需要花费 14 分钟才能阅读完成。

Pinia

1. 介绍

  • 足够轻量,Pinia 重约 1kb,甚至会忘记它的存在!
  • 去除 Mutation,Actions 支持同步和异步(Actions 一个顶俩,写起来简洁);
  • 无需手动注册 Store,Store 仅需要时才自动注册。如果从不使用,则永远不会“注册”(省心);
  • 没有模块嵌套,只有 Store 的概念,Store 之间可以自由使用,更好的代码分割;
  • Vue2 和 Vue3 都能支持;
  • 支持大型项目迁移期间,Pinia 和 Vuex 混合使用(贴心迁移);
  • 更完美的 typescript 的支持;
  • 与 Vue devtools 挂钩,Vue2 和 Vue3 开发体验更好;
  • 支持插件扩展功能;
  • 支持模块热更新,无需加载页面可以修改容器,可以保持任何现有的状态;
  • 支持服务端渲染;

2. 安装

npm install pinia

提示

如果您的应用使用 Vue 2,您还需要安装组合 API:@vue/composition-api。

2.1 Vue3.x 版本使用

import {createPinia} from 'pinia'
app.use(createPinia())

2.2 Vue2.x 版本使用

import {createPinia, PiniaVuePlugin} from 'pinia'

Vue.use(PiniaVuePlugin)
const pinia = createPinia()

new Vue({
  el: '#app',
  // 其他选项...
  // ...
  // 注意同一个 `pinia` 实例可以在多个 Vue 应用程序中使用
  // 同一个页面
  pinia,
})

使用 Pinia 

1.  store

一个 Store(如 Pinia)是一个实体,它持有未绑定到您的组件树的状态和业务逻辑。换句话说,它托管全局状态。它有点像一个始终存在并且每个人都可以读取和写入的组件。它有三个概念,state、getters 和 actions 并且可以安全地假设这些概念等同于组件中的“数据”、“计算”和“方法”。

提示

Pinia 的目录通常被称为 stores 而不是 store,这是为了强调 Pinia 使用多个 store,而不是 Vuex 中的单个 store,同时也有迁移期间 Pinia 和 Vuex 混合使用的考虑。

1.1 创建 store

// src/stores/index.js
// 引入 Store 定义函数
import {defineStore} from 'pinia'

// 定义 Store 实例并导出,useStore 可以是任何东西,比如 useUser, useCart
// 第一个参数,唯一不可重复,字符串类型,作为仓库 ID 以区分仓库
// 第二个参数,以对象形式配置仓库的 state,getters,actions
export const useStore = defineStore('main', {
  // state 推荐箭头函数,为了 TS 类型推断
  state: () => {
    return {
      name: '张三',
      counter: 0
    }
  },
  getters: {},
  actions: {}})

1.2 setup 内使用 store




1.3 修改 state

// 单个参数修改 state
store.counter++
// 多个参数修改 state
store.$patch({
  counter: store.counter + 1,
  name: 'Abalam',
})
// 全部修改 state
store.$state = {counter: 666, name: 'Paimon'}
pinia.state.value = {}
// 重置 State,将状态重置为初始值
const store = useStore()
store.$reset()

1.3.1 $patch、$reset

pinia.state.value = {}; // 置空
store.$state = {counter: 666, name: 'Paimon'};   // 全部修改 state
store.$reset();// 重置到初始状态

1.4 mapstate

import {mapState} from 'pinia'
import {useCounterStore} from '../stores/counterStore'

export default {
  computed: {...mapState(useCounterStore, ['counter'])
    // 或
    ...mapState(useCounterStore, {
      myOwnName: 'counter',
      double: store => store.counter * 2,
      magicValue(store) {return store.someGetter + this.counter + this.double},
    }),
  },
}

mapstate 状态下的 state 是只读的,无法进行修改;如需修改需要使用 mapWritableState

import {mapWritableState} from 'pinia'
import {useCounterStore} from '../stores/counterStore'

export default {
  computed: {
    // 允许访问组件内的 this.counter 并允许设置它
    // this.counter++
    // 与从 store.counter 读取相同
    ...mapWritableState(useCounterStore, ['counter'])
    // 与上面相同,但将其注册为 this.myOwnName
    ...mapWritableState(useCounterStore, {myOwnName: 'counter',}),
  },

提示

对于像数组这样的集合,您不需要 mapWritableState(),除非您用 cartItems = [] 替换整个数组,mapState() 仍然允许您调用集合上的方法。

1.5 订阅

可以通过 store 的 $subscribe() 方法查看状态及其变化,类似于 Vuex 的 subscribe 方法。与常规的 watch() 相比,使用 $subscribe() 的优点是 subscriptions 只会在 patches 之后触发一次;

const subscribe = store.$subscribe((mutation, state) => {
      /*
      * mutation 主要包含三个属性值:*   events:当前 state 改变的具体数据,包括改变前的值和改变后的值等等数据
      *   storeId:是当前 store 的 id
      *   type:用于记录这次数据变化是通过什么途径,主要有三个分别是
      *“direct”:通过 action 变化的”patch object“:通过 $patch 传递对象的方式改变的“patch function”:通过 $patch 传递函数的方式改变的
      *
      * */
      // 我们就可以在此处监听 store 中值的变化,当变化为某个值的时候,去做一些业务操作之类的
      console.log(mutation)
      console.log(state.baseUrl)
      if (state.baseUrl === afterChangeUrl) isShow.value = true
      else isShow.value = false
    }, {detached: false})  // 第二个参数 options 对象,是各种配置参数
    //detached: 布尔值,默认是 false,正常情况下,当订阅所在的组件被卸载时,订阅将被停止删除,// 如果设置 detached 值为 true 时,即使所在组件被卸载,订阅依然在生效
    // 参数还有 immediate,deep,flush 等等参数 和 vue3 watch 的参数是一样的,多的就不介绍了,用到再看文档吧

    // 停止订阅
    // subscribe()  // 调用上方声明的变量值,示例(subscribe),即可以停止订阅

2. getter

Getter 完全等同于 Store 状态的 计算值。它们可以用 defineStore() 中的 getters 属性定义。他们接收“state”作为第一个参数 , 在函数内可以使用 this 访问其他 getter;

getter 中的值有缓存特性,类似于 computed,如果值没有改变,多次使用也只会调用一次。

export const useStore = defineStore('main', {state: () => ({counter: 0,}),
  getters: {doubleCount: (state) => state.counter * 2,
    // 自动推导返回类型
    doubleCount(state) {return state.counter * 2},
    // 依赖 getters 返回参数,则需要显性的设置返回类型
    doublePlusOne(): number {return this.doubleCount + 1},
  },
})

3. actions

不同于 vuex,pinia 的 actions 可以同步也可以异步,内部可以使用 this 访问整个 store 对象;

actions 内的函数可以使用 async 标记。

 export const useUserStore = defineStore({'user',
  actions: {increment() {this.counter++},
    increase() {
      // 调用另一个 action 的方法
      this.increment()},
  }
})

可以使用 store.$onAction() 订阅 action 及其结果。传递给它的回调在 action 之前执行。after 处理 Promise 并允许您在 action 完成后执行函数。以类似的方式,onError 允许您在处理中抛出错误。这些对于在运行时跟踪错误很有用,类似于 Vue 文档中的这个提示

const unsubscribe = someStore.$onAction(
  ({
    name, // action 的名字
    store, // store 实例
    args, // 调用这个 action 的参数
    after, // 在这个 action 执行完毕之后,执行这个函数
    onError, // 在这个 action 抛出异常的时候,执行这个函数
  }) => {
    // 记录开始的时间变量
    const startTime = Date.now()
    // 这将在 `store` 上的操作执行之前触发
    console.log(`Start "${name}" with params [${args.join(',')}].`)

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

    // 如果 action 抛出或返回 Promise.reject,onError 将触发
    onError((error) => {
      console.warn(`Failed "${name}" after ${Date.now() - startTime}ms.nError: ${error}.`
      )
    })
  }
)

// 手动移除订阅
unsubscribe()

默认情况下,action subscriptions 绑定到添加它们的组件(如果 store 位于组件的 setup() 内)。意思是,当组件被卸载时,它们将被自动删除。

export default {setup() {const someStore = useSomeStore()
    // 此订阅将在组件卸载后保留
    someStore.$onAction(callback, true)
    // ...
  },
}

4. 相关函数

4.1 mapState

import {mapState} from 'pinia'
import {useCounterStore} from '../stores/counterStore'

export default {
  computed: {
    // 允许访问组件内部的 this.counter
    // 与从 store.counter 读取相同
    ...mapState(useCounterStore, {
      myOwnName: 'counter',
      // 您还可以编写一个访问 store 的函数
      double: store => store.counter * 2,
      // 它可以正常读取“this”,但无法正常写入...
      magicValue(store) {return store.someGetter + this.counter + this.double},
    }),
  },
}

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2022-06-19发表,共计5311字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)