Pinia | 青训营笔记

213 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的的第5天

在青训营的小组讨论中,队友提出了 pinia ,相比于 Vuex ,我认为 Pinia 更加的简洁,下面是我整理的一些知识点。

简介

一个全新用于 Vue 到状态管理库。

下一个版本的 Vue ,也就是 Vue5.0。

Pinia 已经被纳入官方账户下,github.com/vuejs/pinia.

核心概念:

Pinia 从使用角度和之前的 VueX 几乎是一样的,比 VueX 更简单了。

在 VueX 中有四个核心概念:

  • state
  • Getters
  • Mutations
  • Actions

在 Pinia 中:

  • state
  • Getters
  • Actions:同步异步都支持

Pinia VS VueX

关于版本问题:

  • VueX 当前最新的版本是 4x

    • Vuex 4 用于 Vue3
    • Vuex 3 用于 Vue2
  • Pinia 当前最新的版本是2x

    • 既支持 Vue2 也支持 Vue3

Pinia 可以认为就是 Vuex 5 ,因为它的作者是官方的开发人员,并且已经被官方接管了。

安装

//在已经准备好的项目中,执行
npm install pinia

使用

一个简单的例子

  1. main.js 中使用 pinia
import { createApp } from 'vue'
import App from './App.vue'// 导入 Pinia
import { createPinia } from 'pinia'// 创建 Pinia 实例
const pinia = createPinia()
const app = createApp(App)
​
// 挂载到 Vue 根实例
app.use(pinia)
​
app.mount('#app')
  1. 创建 store 文件
import { defineStore } from 'pinia'// 1.定义容器
// 参数1:容器的 ID ,必须唯一,将来 Pinia 会把所有的容器挂载到根容器
// 参数2:选项对象
// 返回值:一个函数,调用得到容器实例
export const useMainStore = defineStore('main',{
    // 类似于组件的 data ,用来存储全局状态的
    // 1.必须是函数:这样是为了在服务器端渲染的时候避免交叉请求导致的数据状态污染
    // 2.必须是箭头函数,这是为了更好的 TS 类型推倒
    state:()=>{
        return {
            count:100,
            foo:'bar',
        }
    },
​
    // 类似于组件的 computed ,用来封装计算属性,有缓存的功能
    getters:{},
​
    // 类似于组件的 methods ,封装业务逻辑,修改 state
    actions:{}
})
  1. App.vue 中使用 store 里面的数据
<template>
    <p>数据是:{{ mainStore.count }}</p>
    <p>文字是:{{ mainStore.foo }}</p>
</template><script setup>
import { useMainStore } from './store/index';
​
const mainStore = useMainStore()
​
console.log(mainStore.count)
​
</script>

解构访问Pinia容器数据

//App.vue
<template>
  <p>数据是:{{ mainStore.count }}</p>
  <p>文字是:{{ mainStore.foo }}</p>
  <hr />
  <p>数据是:{{ count }}</p>
  <p>文字是:{{ foo }}</p>
  <hr>
  <button @click="handleChangeState">修改数据</button>
</template><script setup>
import { useMainStore } from "./store/index";
​
const mainStore = useMainStore();
​
console.log(mainStore.count);
​
// 解构赋值
// 但这样是有问题的,因为这样拿到的数据不是响应式,是一次性的
const { count, foo } = mainStore;
​
const handleChangeState = ()=>{
  mainStore.count++;
}
</script>

pinia解构问题暴露.png

解决方法

//App.vue
<template>
  <p>数据是:{{ mainStore.count }}</p>
  <p>文字是:{{ mainStore.foo }}</p>
  <hr />
  <p>数据是:{{ count }}</p>
  <p>文字是:{{ foo }}</p>
  <hr>
  <button @click="handleChangeState">修改数据</button>
</template><script setup>
import { useMainStore } from "./store/index";
import { storeToRefs } from 'pinia'const mainStore = useMainStore();
​
console.log(mainStore.count);
​
// 解构赋值
// 但这样是有问题的,因为这样拿到的数据不是响应式,是一次性的
// const { count, foo } = mainStore;
// console.dir(count)// 解决办法
const { count, foo } = storeToRefs(mainStore);
console.dir(count)
​
const handleChangeState = ()=>{
  mainStore.count++;
}
</script>

pinia解耦问题解决.png

本质:

通过 const { count, foo } = mainStore; 这样解构出来的东西,打印解构是 一个数字

pinia解构本质2.png 而通过 const { count, foo } = storeToRefs(mainStore);解构出来的东西,打印出来是一个代理对象

pinia解构本质1.png

状态更新和Action

//App.vue
<template>
  <p>数据是:{{ mainStore.count }}</p>
  <p>文字是:{{ mainStore.foo }}</p>
  <hr />
  <p>数据是:{{ count }}</p>
  <p>文字是:{{ foo }}</p>
  <hr>
  <button @click="handleChangeState">修改数据</button>
</template><script setup>
import { useMainStore } from "./store/index";
import { storeToRefs } from 'pinia'const mainStore = useMainStore();
​
console.log(mainStore.count);
​
const { count, foo } = storeToRefs(mainStore);
console.dir(count)
​
const handleChangeState = ()=>{
  // 方式一:最简单的方式就是这样
  // mainStore.count++;
​
  // 方式二:如果需要修改多个数据,建议使用 $patch 批量更新
  // mainStore.$patch({
  //   count:mainStore.count+=1,
  //   foo:'hello'
  // })
​
  // 方式三:更好的批量更新方式 $patch 一个函数,批量更新
  // mainStore.$patch(state=>{
  //   state.count++
  //   state.foo='hello'
  // })
​
  // 方式四:逻辑比较多的时候可以封装到 action 做处理
  mainStore.changState(1);
}
</script>
//index.js
//只有上述用到方法四才定义在 action 中
import { defineStore } from 'pinia'export const useMainStore = defineStore('main',{
    state:()=>{
        return {
            count:100,
            foo:'bar',
        }
    },
​
    getters:{},
​
    // 类似于组件的 methods ,封装业务逻辑,修改 state
    actions:{
        // 可以接受参数,也可以不所接收
        // 不能使用箭头函数定义 action ,因为箭头函数绑定外部 this
        changState(number){
            this.count+=number;
            this.foo = 'hello';
            // 当然,在这里也可以写$patch
            // this.$patch({})
            // this.$patch(state=>{})
        }
    }
})

Getters的使用

//App.vue
<template>
  <p>数据是:{{ mainStore.count }}</p>
  <p>文字是:{{ mainStore.foo }}</p>
  <hr />
  <p>数据是:{{ count }}</p>
  <p>文字是:{{ foo }}</p>
  <hr>
  <button @click="handleChangeState">修改数据</button>
  <hr>
  //调用多次
  <p>{{mainStore.count10}}</p>
  <p>{{mainStore.count10}}</p>
  <p>{{mainStore.count10}}</p>
</template><script setup>
import { useMainStore } from "./store/index";
import { storeToRefs } from 'pinia'const mainStore = useMainStore();
​
console.log(mainStore.count);
​
const { count, foo } = storeToRefs(mainStore);
console.dir(count)
​
const handleChangeState = ()=>{
  mainStore.changState(1);
}
</script>
//index.js
import { defineStore } from 'pinia'export const useMainStore = defineStore('main',{
    state:()=>{
        return {
            count:100,
            foo:'bar',
        }
    },
​
    // 类似于组件的 computed ,用来封装计算属性,有缓存的功能
    getters:{
        // 函数接受一个可选参数:stste 状态对象
        // count10(state){
        //     console.log('count10 被调用了');
        //     return state.count+10
        // }
        
        // 也可以这样书写
        // 但是这样书写在 TS 中必须手动指定返回值的类型,否则类型推倒不出来
        count10(){
            console.log('count10 被调用了');
            return this.count+10
        }
       
    },
​
    actions:{
        changState(number){
            this.count+=number;
            this.foo = 'hello';
        }
    }
})

store之间的相互调用

首先,再创建一个 store 文件

//movie.js
import { defineStore } from 'pinia'export const useMovieStore = defineStore('movie',{
    state:()=>{
        return {
            movieList:['神奇宝贝','猫和老鼠','喜洋洋和灰太狼']
        }
    },
​
    getters:{},
    actions:{}
})

然后,在 index.js 中引入:

import { defineStore } from 'pinia'
import { useMovieStore } from './movie'export const useMainStore = defineStore('main',{
    state:()=>{
        return {
            count:100,
            foo:'bar',
        }
    },
​
    getters:{
       movieList:()=>{
            return useMovieStore().movieList
        }
    },
    actions:{}
})

这样,在调试工具中就可以显示出来了:

pinia store之间的互相调用.png pinia store之间的互相调用

Pinia和VueDevtools

打开 Pinia 调试工具

Pinia与VueDevtools1.png

查看所有的容器

Pinia与VueDevtools2.png 查看单个容器

pinia与VueDevtools3.png

结语

文章如果有不正确的地方,欢迎指正,共同学习,共同进步。

若有侵权,请联系作者删除。