使用 pinia 时,解构 store 里的 helloWorld 和 count 时,没有发生响应式数据变化的情况。这是因为在使用 store 的过程中,如果直接进行进行解构的话,会破坏数据的响应,因此可以通过使用 storeToRefs 来解构。

一、pinia 的使用时的问题复现

1、stores:counter.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { defineStore } from "pinia";
// defineStore() 第一个参数:相当于为容器取一个名字 第二个参数为配置对象
// state:存储全局状态 getters:监视、计算状态的变化,具有缓存功能
// actions:对state里数据变化进行逻辑处理 (修改state全局状态数据)
export const mainStore = defineStore("main", {
state: () => {
return {
helloWorld: "hello world !!!",
count: 0,
};
},
getters: {},
actions: {},
});

2、组件:Test.vue

1
2
3
4
5
6
<template>
<!-- 非解构 -->
<!-- <div>{{ store.helloWorld}}{{store.count}}</div> -->
<!-- 解构 -->
<div>{{ helloWorld }}{{ count }}</div>
</template>

如果不使用storeToRefs,点击按钮不会生效。
storeToRefs的源码中,会先进行vue版本的判断,如果是Vue2版本,会直接返回toRefs(store),非Vue2环境,遍历对象的键值,会过滤掉store中的非ref/reactive对象,对于符合ref和reactive类型的值,将其复制到一个新的对象中refs中,最后返回refs

1
2
3
4
5
6
<script setup>
import {storeToRefs} from "pinia"
import {mainStore} from'../stores/counter'
const store = mainStore()
const {(helloWorld, count)} = storeToRefs(store)
</script>

3、组件:AddButton.vue

1
2
3
<template>
<div><button @click="handleClick">Add</button></div>
</template>
1
2
3
4
5
<script setup>
import {mainStore} from '../stores/counter'
const store = mainStore()
const handleClick = () => {store.count++}
</script>

4、App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<header>
<img
alt="Vue logo"
class="logo"
src="@/assets/logo.svg"
width="125"
height="125"
/>
<div class="wrapper">
<Test></Test>
<add-button></add-button>
</div>
</header>
<RouterView />
</template>
1
2
3
4
5
<script setup>
import {(RouterLink, RouterView)} from 'vue-router'
import Test from './components/Test.vue'
import AddButton from "./components/AddButton.vue"
</script>

二、pinia如何修改状态数据

1、$patch修改单个或者多个数据

1
<div><button @click="handleClickPatch">Add handleClickPatch</button></div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script setup>
import { mainStore } from '../stores/counter';
const store = mainStore()
const handleClick = () => {
store.count++;
// store.helloWorld = store.helloWorld === 'yq' ? 'helloworld' : 'yq'
}
// 1、修改状态数据 $patch 可同时修改多个数据的状态 参数为一个对象
const handleClickPatch = () => {
store.$patch({
count:store.count + 2,
helloWorld:store.helloWorld === 'yq' ? 'HelloWorld' : 'yq'
})
}
</script>

2、$patch加函数形式修改状态数据

适合修改复杂数据,例如数组、对象

1
<div><button @click="handleClickMethod">Add handleClickMethod</button></div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script setup>
import { mainStore } from '../stores/counter';
const store = mainStore()
const handleClick = () => {
store.count++;
// store.helloWorld = store.helloWorld === 'yq' ? 'helloworld' : 'yq'
}
// 1、修改状态数据 $patch 可同时修改多个数据的状态 参数为一个对象
const handleClickPatch = () => {
store.$patch({
count:store.count + 2,
helloWorld:store.helloWorld === 'yq' ? 'HelloWorld' : 'yq'
})
}

// 2、传递函数 适合复杂数据的修改,比如数组、对象的修改
const handleClickMethod = () =>{
store.$patch((state) => {
state.count++;
state.helloWorld = state.helloWorld === 'yq' ? 'helloworld' :'yq'
})
}
</script>

3、actions

在使用actions的时候,不能使用箭头函数,因为箭头函数绑定的是外部的this

stores :counter.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export const mainStore = defineStore('main', {
state:()=>{
return {
helloWorld:'hello world !!!',
count:0
}
},
getters:{

},
actions:{
changeState(){
this.count++
this.helloWorld = 'yq'
}
}
})

组件:AddButton.vue

1
<div><button @click="handleClickAction"> Add handleClickAction</button></div>
1
2
3
const handleClickAction = () =>{
store.changeState()
}

4、getters

store:counter.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
export const mainStore = defineStore('main', {
state:()=>{
return {
helloWorld:'hello world !!!',
count:0,
phone:'19542932249'
}
},
getters:{
phoneHidden(state){
console.log("go 具有缓存 只会调用一次")
// 使用this关键字
// return this.phone.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
return state.phone.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
}
},
actions:{
// 在用actions的时候,不能使用箭头函数,因为箭头函数绑定的是外部的this
changeState(){
this.count++
this.helloWorld = 'yq'
}
}
})

注意:getters里是可以使用this的

写法如下:

1
2
3
4
5
6
7
getters:{
phoneHidden():String{
console.log("go 具有缓存 只会调用一次")
// 使用this关键字
// return this.phone.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
}
},

组件:Test.vue

1
2
3
4
5
6
7
8
<template>
<div>
{{ store.helloWorld}}{{store.count}}
</div>
<div>
{{ phoneHidden }}
</div>
</template>
1
2
3
4
5
6
<script setup>
import { storeToRefs } from "pinia";
import { mainStore } from '../stores/counter';
const store = mainStore()
const { helloWorld,count,phoneHidden } = storeToRefs(store)
</script>

组件:AddButton.vue

1
<div><button @click="handleClickChangePhone">change phone</button></div>
1
2
3
4
5
6
7
8
<script setup>
import { mainStore } from '../stores/counter';
const store = mainStore()

const handleClickChangePhone = () => {
store.phone = "18775352722"
}
</script>

5、Github项目代码:

github: https://github.com/HeyJudeYQ/pinia-demo