Vue 3 & Pinia 状态管理(3) - Pinia State 的相关使用
嗨,我是温新,一名 PHPer
本篇目标:了解什么是 Pinia
Pinia 官方文档:https://pinia.web3doc.top/introduction.html
state
是 Pinia 中的核心部分。state
是什么?把它当做属性来看,当做属性的来操作。
添加 state
添加 state 数据,只需要在 defineStore
第二个参数中添加相关数据即可。
// src/stores/user.ts
import { defineStore } from 'pinia'
import {ref} from 'vue'
// 组合式 API
export default defineStore('user', () => {
// 添加公用数据
const name = ref('自如初')
const url = ref('https:/www.ziruchu.com')
const age = ref(4)
return {name, url, age}
})
使用 State 数据
第一种使用方式
<template>
<!-- src/App.vue -->
<div>
<h2>名称:{{ name }}</h2>
<h2>网址:{{ url }}</h2>
<h2>年龄:{{ age }}</h2>
<hr>
<p>可以直接取值进行使用</p>
<h2>名称:{{ userStore.name }}</h2>
<h2>网址:{{ userStore.url }}</h2>
<h2>年龄:{{ userStore.age }}</h2>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import useUserStore from './stores/user'
const userStore = useUserStore()
// 注意类型的声明
const name = ref(userStore.name)
const url = ref<string>(userStore.url)
const age = ref<number>(userStore.age)
</script>
<style scoped></style>
第二种使用方式:解构赋值
<template>
<div>
<h2>名称:{{ name }}</h2>
<h2>网址:{{ url }}</h2>
<h2>年龄:{{ age }}</h2>
</div>
</template>
<script setup lang="ts">
import { toRefs } from 'vue'
import useUserStore from './stores/user'
const userStore = useUserStore()
// 解构赋值
const {name, url, age} = toRefs(userStore)
</script>
<style scoped></style>
注意:使用解构赋值时,若不使用
toRefs
,则会失去响应式
多组件使用 state
使用 pinia 的一个重要原因就是组件间的数据共享,现在通过多组件来使用 state 数据。
步骤一:信件 child 子组件
<template>
<!-- src/home/child.vue -->
<div>
<h1>我是 Child 子组件</h1>
<h2>名称:{{ name }}</h2>
<h2>网址:{{ url }}</h2>
<h2>年龄:{{ age }}</h2>
<input type="text" v-model="name">
</div>
</template>
<script setup lang="ts">
import useUserStore from '../stores/user'
import { toRefs } from 'vue';
const userStore = useUserStore()
const {name, url, age} = toRefs(userStore)
</script>
步骤二:父组件使用使用子组件
<template>
<div>
<h2>名称:{{ name }}</h2>
<h2>网址:{{ url }}</h2>
<h2>年龄:{{ age }}</h2>
<hr>
<!-- 子组件 -->
<child></child>
</div>
</template>
<script setup lang="ts">
import { toRefs } from 'vue'
import useUserStore from './stores/user'
import child from './home/child.vue'
const userStore = useUserStore()
const {name, url, age} = toRefs(userStore)
</script>
<style scoped></style>
通过浏览查看效果,会发现子组件和父组件使用了相同的数据。
在 child
组件中,使用了一个双向绑定的 input
,通过child
组件中的 input
进行修改名称,会发现 App.vue
和 child.vue
中的数据都发生了变化。
因此可以得出结论,使用共享数据时,若使用了响应式,则使用该数据的所有组件都会受到影响。
修改 state 数据
修改数据,直接对属性进行赋值即可。
方式一:pinia 中的 storeToRefs
<template>
<!-- App.vue -->
<div>
<h2>名称:{{ name }}</h2>
<h2>网址:{{ url }}</h2>
<h2>年龄:{{ age }}</h2>
<button @click="changeName">修改名称</button>
<hr>
<child></child>
</div>
</template>
<script setup lang="ts">
// 引入 storeToRefs
import { storeToRefs } from 'pinia';
import useUserStore from './stores/user'
import child from './home/child.vue'
const userStore = useUserStore()
// storeToRefs 响应式
const {name, url, age} = storeToRefs(userStore)
const changeName = () => {
userStore.name = '计算机导论'
}
</script>
<style scoped></style>
这个案例在父组件 App.vue
中添加了一个修改名称的按钮,当点击修改时,子组件和父组件的数据都会发生变化,原因是使用了 storeToRefs
,若不使用 storeToRefs
,则不会发生响应式改变。
除了使用了 pinia
中的 storeToRefs
外,还可以使用 vue
中的 toRefs
。
方式二:vue 中的 toRefs
<script setup lang="ts">
// App.vue
import { toRefs } from 'vue'
import useUserStore from './stores/user'
import child from './home/child.vue'
const userStore = useUserStore()
const {name, url, age} = toRefs(userStore)
const changeName = () => {
userStore.name = '计算机导论'
}
</script>
批量修改 state 数据
批量修改数据,使用 store
中的 $patch
方法。
为了保持整洁,重复的内容省略了。
方式一: $patch 批量修改
<!-- App.vue -->
<button @click="changePatchStore">批量修改数据</button>
<script setup lang="ts">
const changePatchStore = () => {
userStore.$patch({
name: "操作系统原理",
age: 19,
})
}
</script>
使用这种方法,代价有点高,任何集合修改(例如,从数组中推送、删除、拼接元素)都需要创建一个新集合。
方式二:$patch 参数进行批量修改
<script setup lang="ts">
const changePatchStore = () => {
userStore.$patch( state => {
state.name = '鸟哥的私房菜',
state.age = 8
})
}
</script>
方式三:替换
pinna
中提供了 $state
方法替换整个 state 对象
<script>
const changePatchStore = () => {
// 方式一:使用 $state 替换整个 state
userStore.$state = {name:"算法导论", age:19, url:"www.ziruchu.com"}
// 方式二: 使用 $patch 可以替换一个或多个
//userStore.$patch({name:"计算机科学导论"})
}
</script>
重置 state
修改了 state 数据后,想要还原到旧数据时,可以使用 store
的 $reset()
方法。
注意:按照文档说明案例,使用选项式 API 时不会出现问题,但是在使用组合式 API 时会存在问题。下面使用组合式 API 进行学习,并解决组合式 API 中 $reset 报错问题。
步骤一:添加重置功能
<!-- App.vue -->
<button @click="handleReset">重置</button>
<script>
const handleReset = () => {
userStore.$reset()
}
</script>
当点击重置按钮时,组合式 API 中 $reset 报错信息如下:
runtime-core.esm-bundler.js:221 Uncaught Error:: Store "user" is built using the setup syntax and does not implement $reset().
步骤二:解决组合式 API 报错
// main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createPinia } from 'pinia'
const app = createApp(App)
// 以插件的形式为组合式 API 添加 $reset 方法
const pinia = createPinia()
pinia.use(({store}) => {
// 获取原始值
const initialState = JSON.parse(JSON.stringify(store.$state))
store.$reset = () => {
// 重置原始使用
store.$state = JSON.parse(JSON.stringify(initialState))
}
})
app.use(pinia)
app.mount('#app')
订阅状态
通过 store 的 $subscribe()
方法查看状态及其变化。与常规的 watch()
相比,使用 $subscribe()
的优点是 subscriptions 只会在 patches 之后触发一次。
<script>
// App.vue
import { toRefs, watch } from 'vue'
userStore.$subscribe((mutation, state) => {
console.log(mutation, state)
})
watch(userStore.$state, (newValue, oldValue) => {
console.log(newValue, oldValue)
})
</script>
1、点击 修改名称
按钮,控制台中观察$subscribe
和 watch
的变化;
2、再次点击 修改名称
按钮,观察两者的变化。
关于 state
的相关操作就结束了。
最后附上完整代码
<template>
<!-- App.vue -->
<div>
<h2>名称:{{ name }}</h2>
<h2>网址:{{ url }}</h2>
<h2>年龄:{{ age }}</h2>
<button @click="changeName">修改名称</button>
<button @click="changePatchStore">批量修改数据</button>
<button @click="handleReset">重置</button>
<hr>
<child></child>
</div>
</template>
<script setup lang="ts">
import { toRefs, watch } from 'vue'
import useUserStore from './stores/user'
import child from './home/child.vue'
const userStore = useUserStore()
const {name, url, age} = toRefs(userStore)
const changeName = () => {
userStore.name = '计算机导论'
}
// const changePatchStore = () => {
// userStore.$patch({
// name: "操作系统原理",
// age: 19,
// })
// }
// const changePatchStore = () => {
// userStore.$patch( state => {
// state.name = '鸟哥的私房菜',
// state.age = 8
// })
// }
const changePatchStore = () => {
// userStore.$state = {name:"算法导论", age:19, url:"www.ziruchu.com"}
userStore.$patch({name:"计算机科学导论"})
}
const handleReset = () => {
userStore.$reset()
}
userStore.$subscribe((mutation, state) => {
console.log(mutation, state)
})
watch(userStore.$state, (newValue, oldValue) => {
console.log(newValue, oldValue)
})
</script>
<style scoped></style>