Pinia
上面这个网站不是官网,因为官网没有中文。是国内有人翻译的。
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 写的 我们定义的到时候写会有提示的
我用了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
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 之后发现不简单。
发现要引入那个模块 并且 模块返回是一个 函数 这里我前面说了是重点 只要你 引入使用函数 才有下面的标志出现 。说明你 当前使用了 pinia 管理数据
如果你没有使用函数直接调用 模块 就会打印一大堆东西 出来的
如果是 你引入了 页面有效果 但是控制台没有 菠萝 ,可能是你控制台没有刷新出来 vue 插件需要
关闭控制台 重新刷新在打开
<script setup>
import { storeToRefs } from 'pinia';
import useCounterStore from './store/counter';
const counterStore = useCounterStore()
</script>
<template>
<!-- 变量 -->
<h1>{{ counterStore.count }}</h1>
</template>
当你使用这个模块返回函数时候 返回就是 我们定义的 state getters actions 都会在第一层里面
如果是 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
可以 解构 计算属性
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>
全局引入 pinia
当我们模块越来越多,每一个vue要引入太麻烦了
能不能像 vuex一样 在全部注册到一个 vuex里面呢?
在搞一个 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
这个bug只是没有 提示,如果想要有提示 需要 箭头函数里面参数 传入 state这样写法会更好一点
在vue 模板里面会有提示的 ,this是没有的
getters: {
checkAll() {
return this.list.every(v => v.done)
},
// 推荐的完整写法
quantity: (state) => {
return state.list.filter(v => !v.done).length
}
},
$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 里面就会 报错下面这个错误
如果是 绑定 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" />