B站教学视频: 抛弃 Vuex,使用 Pinia
介绍
Pinia 最初是在 2019 年 11 月左右重新设计使用 Composition API 。
Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态。类似vuex
Vuex 3.x 是 Vuex 的 Vue 2 而 Vuex 4.x 是 Vue 3
Pinia API 与 Vuex ≤4 有很大不同,即:
- mutations 不再存在
- 无需创建自定义复杂包装器来支持 TypeScript,所有内容都是类型化的,并且 API 的设计方式尽可能利用 TS 类型推断。
- 不再需要注入、导入函数、调用函数、享受自动完成功能!
- 无需动态添加 Store,默认情况下它们都是动态的
- 不再有 modules 的嵌套结构
- 没有 命名空间模块
搭建项目
- 新建一个新的vite项目
yarn create vite
按提示选择vue、typscript
- 安装依赖
yarn
- 安装pinia
yarn add pinia
pinia基本使用
src/main.ts
中创建pinia实例并且挂在vue实例上
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
//创建pinia实例
const pinia = createPinia()
const app = createApp(App)
//挂载pinia实例
app.use(pinia)
app.mount('#app')
src/store/index.ts
主要是定义容器
import { defineStore } from 'pinia'
//定义容器
export const useMainStore = defineStore('main',{
/**
* 类似与组件的data, 用来存储全局状态
* 1.必须是函数:这样是为了在服务端渲染的时候避免交叉请求导致的数据状态污染(客户端其实无所谓)
* 2.必须是箭头函数:为了更好的ts类型推导
* 返回值:一个函数,调用该函数即可得到容器实例
*/
state: () => ({
count: 666,
name: 'tom',
arr: [1,2,3]
}),
/**
* 类似于组件的computed,用来封装计算属性,有【缓存】功能
*/
getters: {
},
/**
* 完全类比于Vue2组件中的methods(可以直接用this),用来【封装业务逻辑】,修改state
*/
actions: {
}
})
src/components/HelloWorld.vue
<template>
{{ mainStore.count }}
</template>
<script setup lang="ts">
//导入上面定义的store
import { useMainStore } from '../store'
//获取容器中的state
const mainStore = useMainStore()
//从store中取值
console.log(mainStore.count)
</script>
src/App.vue
删除不必要的内容,保留HelloWorld组件
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
<HelloWorld />
</template>
解构访问pinia数据
不能直接使用解构,这样会丢失响应式,因为pinia在底层将state用reactive做了处理
src/components/HelloWorld.vue
<template>
{{ count }}-{{ name }}-{{ arr }}
<div><button @click="addOne">count+1</button></div>
</template>
<script setup lang="ts">
import { useMainStore } from '../store'
import { storeToRefs } from 'pinia'
//获取容器中的state
const mainStore = useMainStore()
//修改容器中的state
const addOne = () => {
mainStore.count += 1
}
//不能直接使用解构,这样会丢失响应式,因为pinia在底层将state用reactive做了处理
// const {count,name} = mainStore
//若想使用解构,则需要用storeToRefs将结构出的数据做ref响应式代理
const {count,name} = storeToRefs(mainStore)
</script>
状态更新和Actions
状态更新的四种方式
src/components/HelloWorld.vue
<template>
{{ count }}-{{ name }}-{{ arr }}
<div><button @click="addOne">count+1</button></div>
</template>
<script setup lang="ts">
import { useMainStore } from '../store'
import { storeToRefs } from 'pinia'
//获取容器中的state
const mainStore = useMainStore()
//修改容器中的state
const addOne = () => {
//方式一:直接修改
//mainStore.count += 1
//方式二:使用 $patch(对象) 批量修改,建议使用,底层做了性能优化
// mainStore.$patch({
// count: mainStore.count + 1,
// name: 'jerry',
// arr: [...mainStore.arr,4]
// })
//方式三:使用 $patch(回调函数) 【推荐】
//回调函数中的state参数,就是Store定义时里面的state!
// mainStore.$patch(state => {
// state.count ++
// state.name = 'lucy'
// state.arr.push(5)
// })
// 方式四:逻辑较为复杂时,应封装到Store的actions中,并对外暴露接口
mainStore.addN(10)
}
const {count,name,arr} = storeToRefs(mainStore)
</script>
src/store/index.ts
import { defineStore } from 'pinia'
//定义容器
export const useMainStore = defineStore('main',{
state: () => ({
count: 666,
name: 'tom',
arr: [1, 2, 3]
}),
getters: {
},
//注意:不能使用箭头函数定义actions!因为箭头函数绑定了外部this
actions: {
addN(num: number){
//单个修改--->直接使用this,类似vue2
// this.count += num
// this.name = 'linda'
// this.arr.push(6)
//批量修改--->建议使用patch做优化
this.$patch(state => {
state.count += num
state.name = 'jack'
state.arr.push(8)
})
}
}
})
Getters的使用
和计算属性类似,带有缓存功能
src/components/HelloWorld.vue
<template>
{{ count }}-{{ name }}-{{ arr }}-{{ count10 }}-{{ count20 }}
<div><button @click="addOne">count+1</button></div>
</template>
<script setup lang="ts">
import { useMainStore } from '../store'
import { storeToRefs } from 'pinia'
//获取容器中的state
const mainStore = useMainStore()
//修改容器中的state
const addOne = () => {
mainStore.addN(10)
}
const {count,name,arr,count10,count20} = storeToRefs(mainStore)
</script>
src/store/index.ts
import { defineStore } from 'pinia'
//定义容器
export const useMainStore = defineStore('main',{
state: () => ({
count: 666,
name: 'tom',
arr: [1, 2, 3]
}),
//类似于组件的computed,用来封装计算属性,有【缓存】功能
getters: {
//不带state参数:此时就不能对返回值类型做自动推导了,必须手动指定
count10(): number{
console.log('count10')
return this.count + 10
},
//带state参数:对返回值类型做自动推导 【推荐这种】
count20(state){
return state.count + 20
}
},
//注意:不能使用箭头函数定义actions!因为箭头函数绑定了外部this
actions: {
addN(num: number){
this.$patch(state => {
state.count += num
state.name = 'jack'
state.arr.push(8)
})
}
}
})
pinia和vue devtools
第一种查看方式
第二种查看方式: 组件内部