安装 Pinia

Pinia Vite

pinia只支持vue项目

1
npm i  pinia

使用 Pinia

在vue项目中导入pinia

1
2
3
4
5
6
7
8
9
10
import { createApp } from 'vue'
import App from './App.vue'
// 导入pinia
import { createPinia } from 'pinia'
// 创建pinia实例
const pinia = createPinia()
// 挂载到 Vue实例
onst app = createApp(App)
app.use(pinia)
app.mount('#app')

options API

在选项式写法中:this 指的是 store 实例, 可以通过 this.state 访问 state
组合式写法中:setup中没有 this 是 undefined

创建 store 文件夹

创建index.ts 文件

创建仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import {defineStore} from 'pinia'
// 1.定义容器
// 参数1:容器的 ID,必须唯一,将来 Pinia 会把所有的容器挂载到根容器
// 参数2:选线对象(里面的各个模块)
// 返回值:一个函数,调用得到容器的实例
export const useMainStore = defineStore('mian',{
/*
* 类似于组件的 data,用来存储全局状态的
* 1.必须实函数:这样是为了在服务端渲染的时候避免交叉请求导致的数据状态污染
* 2.必须是箭头函数:这是为了更好的 TS 类型推导
*/
state:()=>{
return {
foo:100,
count:100,
arr:[1,2,3]
}
},
/*
* 类似于组件的 computed,用来封装计算属性,有缓存的功能
*/
getters:{

},
/*
* 类似于组件的 methods,封装业务逻辑,修改 state数据
*/
// 注意 不能使用箭头函数定义 actions 因为箭头函数绑定外部this
actions:{

}
})
// 2.使用容器中的 state
// 3.修改 state
// 4.容器中的 action 的使用

使用

模板中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<templte>
<p>{{ mainStore.count }}</p>
<p>{{ mainStore.foo }}</p>

</hr>
<p>{{ count }}</p>
<p>{{ foo }}</p>

<p><button @click="handleClick"></button></p>
</templte>
<script>
import {useMainStore} from '../store'
const mianStore = useMainStore()

// 这是有问题的,因为这样拿到的数据不是响应式的,是一次性的
// Pinia 其实就是把 state 数据都做了 reactive 处理了
// const { count, foo } = mainStore
// 解决方法 toRefs 或者是 storeToRefs
cosnt {count, foo} = storeToRefs(mainStore)
cosnt handleClick = () => {
// 方式一:对每个数据进行单个修改
mainStore.count++
mainStore.foo++

// 方式二:如果需要修改多个数据,建议使用 $patch 批量更新
mainStore.$patch({
count: mainStore.count+1,
foo: 'hello'
arr: [...mainStore.arr,4]
})

// 方式三:更好的批量更新方式:$patch 一个函数,批量更新
mainStore.$patch(state => {
state.count++,
state.foo = 'hello',
state.arr.push(4)
})

// 方式四:逻辑比较多的时候可以封装都 actions 做处理
mainStore.changeState()
/* actions:{
* changeState() {
* this.count++
* this.foo = 'foo'
* this.arr.push(4)
* this.$patch({})
* this.$patch(state=>{})
* }
*/
// patch 和普通多次修改的区别是什么,按理来说多次修改也是批量修改
// patch 是一次改变所有数据在更新一次视图 而changeState这种每次修改数据都要更新一次视图
}

</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
getters:{
// 函数接受一个可选参数:state 状态对象
// 方式一:传入state
count(state) {
console.log('count 调用了')
return state.count + 10
}
// 这样也可以但是不推荐
// count() {
// console.log('count 调用了')
// return this.count + 10
// }


// 方式二:不传入state
// 如果在 getters 中使用了 this 则必须手动返回指定返回值的类型,否则类型推导不出来
count():number {
console.log('count 调用了')
return this.count + 10
}
}

composition API

创建仓库

store/counter.ts

1
2
3
4
5
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {
return {}
})

App.vue

1
2
3
4
5
6
7
8
9
<script setup lang="ts">
import { useCounterStore } from './store/counter'
// store 中有 counter 中 return 的状态和函数
const store = useCounterStore()
</script>

<template>
App
</template>

进行状态管理

counter.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'

export const useCounterStore = defineStore('counter', () => {
// state
const count = ref(100)
// getters
const doubleCount = computed(() => count.value * 2)
// actions
const update = () => count.value++
const asyncUpdate = () => {
setTimeout(() => {
count.value++
}, 1000)
}
return { count, doubleCount, update, asyncUpdate }
})

使用数据和修改数据的方法

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
<script setup lang="ts">
import { useCounterStore } from './store/counter'

// store 中有 counter 中 return 的状态和函数
const store = useCounterStore()
</script>

<template>
APP {{ store.count }} {{ store.doubleCount }}
<button @click="store.update()">count++</button>
<button @click="store.asyncUpdate()">async update</button>
</template>

storeToRefs

使用 storeToRefs 解决解构仓库状态丢失响应式的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useCounterStore } from './store/counter'

// store 中有 counter 中 return 的状态和函数
const store = useCounterStore()

const { count, doubleCount } = storeToRefs(store)
const { update, asyncUpdate } = store
</script>

<template>
APP {{ count }} {{ doubleCount }}
<button @click="update">count++</button>
<button @click="asyncUpdate">async update</button>
</template>

Vuex 和 Pinia 的主要区别

  • 体积大小:Vuex 核心有 state、mutations、actions、getters和modules。而 Pinia 则更简单,去除了modules 和 mutations,更加轻量化、更适用中小型项目

  • 调试:vuex支持时间快照,在调试工具中可以看到mutations修改state数据的记录,并且可以回到对应的历史状态

  • 状态组织:在 Vuex 中,所有的状态都被组织在一个大的单一对象中。而在 Pinia 中,每个 store 都是一个独立的状态容器,这使得状态更容易组织和管理

  • 类型安全:Pinia 有更好的 TypeScript 支持,因此如果你使用 TypeScript,Pinia 可能会更适合你

  • 开发工具支持:虽然 Vuex 和 Pinia 都支持 Vue.js 开发工具,但 Pinia 提供了一些更多的内置功能

  • 服务器端渲染支持:Pinia 对服务器端渲染(SSR)提供了内置支持

  • 兼容性:Vuex 和 Pinia 都支持 Vue 2 和 Vue 3