Pinia

782 阅读5分钟

Pinia

上面这个网站不是官网,因为官网没有中文。是国内有人翻译的。

image-20220213181825857.png Pinia是干嘛的,和vuex一样是全局状态管理的。

为什么学习pinia?

  • pinia和vuex4一样,也是vue官方的状态管理工具(作者是 Vue 核心团队成员)
  • pinia相比vuex4,对于vue3的兼容性更好
  • pinia相比vuex4,具备完善的类型推荐
  • pinia同样支持vue开发者工具,最新的开发者工具对vuex4支持不好
  • Pinia 的 API 设计非常接近 Vuex 5提案

pinia核心概念

  • state: 状态
  • actions: 修改状态(包括同步和异步,pinia中没有mutations)
  • getters: 计算属性

上面这些是历史说的发的笔记上面的。

pinia是按照 vuex 5 的 提案 做的,更加好用。扁平化、可以独立创建 defineStore 对象互相独立

底层是 ts 写的 我们定义的到时候写会有提示的

1656897552382.png

我用了pinia一下,之后感觉这就是 vue2 的写法 。

安装到了 pinia里面,同样的整一个 defineStore 函数 里面传入参数中对象的this 指向整一个对象 意思是 vue 2中script this指向 当前vue中的 script

1、state 对应是 vue2的 data,

2、getters 对应是 vue2的 computed ,

3、actions 对应是 vue2的 methods

使用pinia

1、肯定是下载 yarn add pinia

2、在 store 下的 index.js 里面引入

import { createPinia } from 'pinia';
// 创建 Pinia 实例
const pinia = createPinia()
// 导出这个实例去  main.js中注册
export default pinia

3、在main.js 中 引入 注册

import store from './store/index';
​
const app = createApp(App)
// 在挂载之前注册 pinia
app.use(store)
app.mount('#app')

4、创建独立的 js 文件 存放 pinia 数据 counter.js 其实在这里开始就我们做 后台系统的 vuex 的独立管理数据的样子了 也就是 默认开启了 vuex 3 版本中的 namespaced :true 1656848582839.png

1656899528928.png 1656848675940.png

1656848936192.png

import { defineStore } from 'pinia';
// 建议规范命名:useXxxxStore   这里XXXX是  唯一标识
// defineStore 需要传入两个参数 一个是唯一标识(我觉得是命名,把这个匿名函数命名)因为我们之后要用到这个管理 数据的 ,没有名字的变量怎么调用呢?,另一个是对象。


// 而且返回值是一个函数这里是重点


const useCounterStore = defineStore('counter', {
    // state 相当于 data, 🎯需写成函数内部返回对象写法
    // state: () => {
    //     return {
    //     }
    // }

    // 在我们学习小程序时候,解构时候。如果想箭头函数 =>  这里不是函数体就需要用  括号包起来 
    state: () => (
        {
            count: 1
        }
    ),
    // getters 相当于 computed, 也支持缓存  在之前vuex中  需要调用state就需要传参数 当然这个参数是vuex自动传过来的
    // 但是这里是pinia相当于  之前的vue2  ,我们之前vue2  怎么用 data的变量  就是this. 就可以了
    getters: {
        double() {
            return this.count * 2
        }
    },
    actions: {
        // 同步修改
        changeCount(payload) {
            this.count += payload
        },
        // 异步修改
        changeCountSync(payload) {
            setTimeout(() => {
                this.count += payload
            }, 1000)
        }
    }
})
export default useCounterStore

##### pinia细节 重点

5、如果是vuex4 我们就会认为这个 在 vue 里面直接使用就好了,但是我看了看 vuex 3 之后发现不简单。

1656850008542.png

发现要引入那个模块 并且 模块返回是一个 函数 这里我前面说了是重点 只要你 引入使用函数 才有下面的标志出现 。说明你 当前使用了 pinia 管理数据

1656850532204.png 如果你没有使用函数直接调用 模块 就会打印一大堆东西 出来的

如果是 你引入了 页面有效果 但是控制台没有 菠萝 ,可能是你控制台没有刷新出来 vue 插件需要

关闭控制台 重新刷新在打开

1656850700718.png

<script setup>
import { storeToRefs } from 'pinia';
import useCounterStore from './store/counter';

const counterStore = useCounterStore()
</script>

<template>
  <!-- 变量 -->
  <h1>{{ counterStore.count }}</h1>
  </template>

当你使用这个模块返回函数时候 返回就是 我们定义的 state getters actions 都会在第一层里面

1656850855743.png 如果是 vue 3 模块化管理 就是要 this.$store.state.app.sidebar 前面点两下 才能 拿到我们想要 的值 但是这里 直接第一层就可以拿到了就会 方便一些 方法也是一样的

<template>
  <!-- 变量 -->
  <h1>{{ counterStore.count }}</h1>
  <!-- 计算属性 -->
  <h1>{{ counterStore.double }}</h1>

  <!-- 同步方法  而且 vuex4 中 同步方法需要 commit  如果是模块还需要  模块名字/ 
   await this.$store.commit('user/logout')   才能 调用-->
  <button @click="counterStore.changeCount(1)">点击同步+1</button>

  <!-- 异步方法  而且 vuex4 中 同步方法需要 dispatch  如果是模块还需要  模块名字/ 
   await this.$store.dispatch('user/logout')   -->
  <button @click="counterStore.changeCountSync(1)">点击异步+1</button>

  <h1>=========================</h1>
  <div>{{ counter }}</div>
  <div>{{ useUser }}</div>
</template>

<style scoped>
</style>

和 vue 中解构 一样 如果是 解构 出来一个普通变量就会 和之前的对象没有关联了 通过了 toRefs 就算是

解构了也会和之前对象有关联的

细节 这里只能 解构 变量不能 解构 方法否则 解构出来是 changeDone undefined

可以 解构 计算属性

1656898002561.png

const { count: storecount, double: storedouble } = storeToRefs(counterStore)

<template>
      <h1>=========================</h1>
  <h1>普通解构{{ count }}</h1>
  <h1>普通解构{{ double }}</h1>
  <h1>=========================</h1>
  <h1>storeToRefs解构{{ storecount }}</h1>
  <h1>storeToRefs解构{{ storedouble }}</h1>
  <button @click="counterStore.changeCount(1)">点击同步+1</button>
</template>

1656851689857.png

全局引入 pinia

当我们模块越来越多,每一个vue要引入太麻烦了

能不能像 vuex一样 在全部注册到一个 vuex里面呢?

1656852341984.png

在搞一个  pinia  模块

import { defineStore } from 'pinia'

const useUserStore = defineStore('user', {
    state: () => {
        return {
            name: 'zs',
            age: 100,
        }
    },
})

export default useUserStore

import { defineStore } from 'pinia';
import useCounterStore from './counter';
import useUserStore from './user'
const useStore = defineStore('useStore', {
    state: () => ({
        // 因为返回是导入的是方法,这些需要调用这些方法
        useCounter: useCounterStore(),
        useUser: useUserStore(),
    })
})
// 导出这个对象  到时候在app.vue中引入这个对象就好了
export default useStore

// 这个导入同样是方法需要调用
const main = useStore()
// 解构出来
const { useCounter: counter, useUser } = storeToRefs(main)


 <div>{{ counter }}</div>
  <div>{{ useUser }}</div>
Pinia的 getters的bug

1656901103779.png 这个bug只是没有 提示,如果想要有提示 需要 箭头函数里面参数 传入 state这样写法会更好一点

在vue 模板里面会有提示的 ,this是没有的

 getters: {
        checkAll() {
            return this.list.every(v => v.done)
        },
        // 推荐的完整写法
        quantity: (state) => {
            return state.list.filter(v => !v.done).length
        }
    },

1656901428185.png

$subscribe

类似于 vue 的 监听器 watch , 如果使用 vue的监听器就会存储方法也存储进去。 而且哪里引入哪里都要进行 watch 也很麻烦。

// store 实例的 $subscribe 方法,相当于 watch 侦听器,侦听 store 变化
// PS: 订阅写在哪个组件都可以,因为 Pinia 是全局状态管理,所以订阅也是全局的
todos.$subscribe(() => {
  localStorage.setItem('todos-pinia', JSON.stringify(todos.list))
})

Pinia的 getters 和 vuex 的 getters

如果我们把 getters 返回的值 绑定 在 v-model 里面就会 报错下面这个错误

1656902654722.png 如果是 绑定 v-model 是双向绑定 不仅仅可以获取 还可以 设置 但是 vuex和 pinia 是 getters 只能获取的

如果需要绑定 v-model 就需要通过 vue的 computed 进行过滤操作 有 set 改变了值就会传过去 在调用之前pinia修改 b的值方法 ,change事件就不需要咯。 因为 v-model会帮我们知道完成这个方法

const { activeList, checkAll } = storeToRefs(todos)

这里我们需要传入全选的状态进去,可是我们的全选的状态 是通过 计算属性得到的  直接传入计算属性取反就好了
如果是在  pinia里面获取就会  发生改变,需要我们提前存储这个变量  但是就会多此一举  还不如直接  传递参数过去 -->
    
<input :checked="checkAll" @change="setCheckAll(!checkAll)" id="toggle-all" class="toggle-all" type="checkbox" />
    
    vue的 computed  加工
    const compCheckAll = computed({
  get() {
    return checkAll
  },
  set(val) {
    todos.setCheckAll(val)
  }
})
    
      如果是  绑定  v-model  是双向绑定  不仅仅可以获取 还可以 设置 但是  vuex和  pinia  是 getters  只能获取的
    如果需要绑定  v-model  就需要通过  vue的  computed  进行过滤操作  有 set  改变了值就会传过去 在调用之前pinia修改  b的值方法 ,change事件就不需要咯。  因为 v-model会帮我们知道完成这个方法  
    <input :checked="compCheckAll" v-model="compCheckAll" id="toggle-all" class="toggle-all" type="checkbox" />