文章目录
引入 Pinia
首先在 main.ts
中引入 pinia
,通过 createPinia
创建 pinia
实例,然后挂载到 Vue 根实例上。
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
const app = createApp(App)
/** 创建 pinia 实例 */
const pinia = createPinia()
/** 挂载到 Vue 根实例上 */
app.use(pinia)
app.mount('#app')
创建 Store
首先在 src
下创建 store/index.ts
文件,然后通过 defineStore
对仓库进行配置。
import { defineStore } from 'pinia'
/** main 相当于为容器起一个名字 */
export const useStore = defineStore('main', {
state: () => {
return {
msg: 'Hello World'
}
},
getters: {},
actions: {}
})
现在每个页面和组件都可以通过 Pinia 方法读取到全局状态 msg
。
state
: 用来存储全局状态数据。
getters
:相当于组件中的计算属性,有缓存功能。
actions
:用来修改全局状态数据。
在组件中读取 Store 数据
在 CostPaymentList 组件中,通过 store/index.ts
文件暴露出来的 useStore
得到 store
实例,就可以在组件里使用 Store 里面的全局状态了。
<template>
<h1>CostPaymentList Page</h1>
<!-- 在 template 中,有两种方式可以获取 Store 中的全局状态 -->
<p>{{ store.msg }}</p>
<p>{{ store.$state.msg }}</p>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { useStore } from '../../store/index'
export default defineComponent({
name: 'CostPaymentList',
setup() {
const store = useStore()
/** 在 setup 中,有两种方式可以获取 Store 中的全局状态 */
console.log(store.msg)
console.log(store.$state.msg)
return {
store
}
},
})
</script>
打印一下 store
:
在组件中修改 Store 数据
- 首先在 Store 仓库中添加一个
count
全局状态。
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
state: () => {
return {
count: 0
}
}
})
- 创建一个新的组件,并为页面中的按钮添加
click
事件,在事件里面修改全局状态count
。
<template>
<h1>HelloWorld Page</h1>
<p>{{ store.count }}</p>
<button @click="handleAddCount">增加</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { useStore } from '../store/index'
export default defineComponent({
name: 'HelloWorld',
setup() {
const store = useStore()
const handleAddCount = () => {
store.count++
}
return { store, handleAddCount }
}
})
</script>
- 把新创建的组件引入到 App 组件中。
<template>
<CostPaymentList />
<hr />
<HelloWorld />
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import CostPaymentList from 'views/finance/CostPaymentList.vue'
import HelloWorld from './components/HelloWorld.vue'
export default defineComponent({
name: 'App',
components: {
CostPaymentList,
HelloWorld
}
})
</script>
当点击按钮时,两个组件的数据都会同时变化。
对 Store 进行解构
在 CostPaymentList 组件对 Store 进行解构,并在 template
中直接使用解构后的数据。
<template>
<h1>CostPaymentList Page</h1>
<p>{{ count }}</p>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { useStore } from '../../store/index'
export default defineComponent({
name: 'CostPaymentList',
setup() {
const { count } = useStore()
return {
count
}
},
})
</script>
然后点击 Hello World 组件中的按钮,发现 CostPaymentList 组件中的数据并未同步更新。
打印一下解构后的 count
:
console.log({count}, typeof count)
可以发现 count
是 number
类型,并非是响应式结构,所以才会丢失响应。
如果需要解构后还保持响应式,需要使用到 pinia
暴露出来的 storeToRefs
。
<template>
<h1>CostPaymentList Page</h1>
<p>{{ count }}</p>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../../store/index'
export default defineComponent({
name: 'CostPaymentList',
setup() {
const store = useStore()
const { count } = storeToRefs(store)
return {
count
}
},
})
</script>
通过 storeToRefs
可以使解构出来的数据拥有响应能力。
打印一下 count
:
可以看到,此时的 count
是作了 ref
响应式处理。
修改 Store 状态数据的多种方式
修改 Store 中状态方式除了上面所说的方式,还有三种方式。
- 通过
$patch
同时修改多个状态。
<template>
<h1>HelloWorld Page</h1>
<p>{{ count }}</p>
<p>{{ name }}</p>
<button @click="handleUpdateStore">更新</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../store/index'
export default defineComponent({
name: 'HelloWorld',
setup() {
const store = useStore()
const handleUpdateStore = () => {
store.$patch({
count: 100,
name: 'new_sjw'
})
}
return { ...storeToRefs(store), handleUpdateStore }
}
})
</script>
如果想同时修改多个状态,一定要使用 $patch
吗?
并不是,通过多条语句也可以达到这个目的:
const handleUpdateStore = () => {
store.count = 100
store.name = 'new _sjw'
}
之所以使用 $patch
,是因为 Pinia 的官方网站,已经明确表示 $patch
的方式是经过优化的,会加快修改速度,对程序的性能有很大的好处。所以如果是多个状态同时更新据,推荐使用 $patch
方式更新。
- 通过 $patch 加函数的形式修改状态
第一种方式中,$patch
接收一个对象,适合基本数据类型的修改,不适合复杂数据的修改(如数组、对象)。
针对这个问题,$patch
可以通过接收一个函数来处理复杂数据:
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
state: () => {
return {
count: 0,
name: 'sjw',
list: [
{ des: '1 号篮球员' },
{ des: '2 号篮球员' },
{ des: '3 号篮球员' }
]
}
}
})
<template>
<h1>HelloWorld Page</h1>
<p>{{ count }}</p>
<p>{{ name }}</p>
<ul>
<li v-for="(item, index) in list" :key="index">{{ item.des }}</li>
</ul>
<button @click="handleUpdateStore">更新</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../store/index'
export default defineComponent({
name: 'HelloWorld',
setup() {
const store = useStore()
const handleUpdateStore = () => {
store.$patch((state) => {
/** 在这里不要对 state 进行解构,否则会丢失响应式 */
state.count++
state.name = 'new_sjw'
state.list[0].des = '200 号足球员'
})
}
return { ...storeToRefs(store), handleUpdateStore }
}
})
</script>
- 通过调用 actions 来修改状态
对于复杂的修改操作,可以在 actions
中封装一个方法,然后在组件调用该方法即可。
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
state: () => {
return {
count: 0,
name: 'sjw',
list: [
{ des: '1 号篮球员' },
{ des: '2 号篮球员' },
{ des: '3 号篮球员' }
]
}
},
actions: {
changeState() {
this.count++
this.name = 'new_sjw'
this.list[0].des = '200 号足球员'
}
}
})
<script lang="ts">
import { defineComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../store/index'
export default defineComponent({
name: 'HelloWorld',
setup() {
const store = useStore()
const handleUpdateStore = () => {
store.changeState()
}
return { ...storeToRefs(store), handleUpdateStore }
}
})
</script>
在使用
actions
的时候,不能时候箭头函数,否则this
为undefined
。
Getters 的使用
Pinia 中的 Getter 和组件中的计算属性(Computer)作用几乎一样,可以对 State 里面的状态进一步处理。
- 首先在 Store 中声明一个 Getter 属性,它对
msg
进行了处理。
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
state: () => {
return {
msg: 'Hello'
}
},
getters: {
logMsg(state) {
return state.msg + ' Wold!'
}
}
})
- 在组件中的使用方式和
state
里面的状态一样,直接调用即可。
<template>
<h1>CostPaymentList Page</h1>
<p>{{ logMsg }}</p>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../../store/index'
export default defineComponent({
name: 'CostPaymentList',
setup() {
const store = useStore()
const { logMsg } = storeToRefs(store)
return {
logMsg
}
},
})
</script>
验证 Getter 的缓存特性:
1.首先在 logMsg
中添加 console.log()
语句,保证 logMsg
每次被调用时都能知道。
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
state: () => {
return {
msg: 'Hello'
}
},
getters: {
logMsg(state) {
console.log('调用了 logMsg')
return state.msg + ' Wold!'
}
}
})
- 然后在组件中编写一个方法,使其能够多次调用
logMsg
。
<template>
<h1>CostPaymentList Page</h1>
<button @click="handleLogMsg">调用</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../../store/index'
export default defineComponent({
name: 'CostPaymentList',
setup() {
const store = useStore()
const { logMsg } = storeToRefs(store)
const handleLogMsg = () => {
console.log(logMsg.value)
}
return {
logMsg,
handleLogMsg
}
},
})
</script>
可以发现,多次调用 logMsg
时,只打印了一次“调用了 logMsg
”,说明 logMsg
真正被调用次数的只有一次,后续的调用都是从缓存中返回数据。
- 在组件中编写一个可以更改
msg
状态的方法。
<template>
<h1>CostPaymentList Page</h1>
<button @click="handleLogMsg">调用</button>
<button @click="handleChangeMsg">更改</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../../store/index'
export default defineComponent({
name: 'CostPaymentList',
setup() {
const store = useStore()
const { msg, logMsg } = storeToRefs(store)
const handleLogMsg = () => {
console.log(logMsg.value)
}
const handleChangeMsg = () => {
msg.value += 'sjw-'
console.log(logMsg.value)
}
return {
logMsg,
handleLogMsg,
handleChangeMsg
}
},
})
</script>
可以发现,当 msg
发生变化时,logMsg
都会随着执行一次,清除之前的缓存。
也可以在 Getters 中使用 this。
Store 的互相调用
当项目存在多个 Store 时,这些 Store 之间可以互相调用。
- 首先,新增一个新的
userStore
,并声明一个storeName
状态。
/* user.ts */
import { defineStore } from 'pinia'
export const userStore = defineStore('user', {
state: () => {
return {
storeName: 'userStore'
}
}
})
- 在其它 Store 中引入
userStore
并调用storeName
属性。
/* index.ts */
import { defineStore } from 'pinia'
import { userStore } from './user'
export const useStore = defineStore('main', {
state: () => ({
msg: 'Hello'
}),
actions: {
getUserStore() {
console.log(userStore().storeName)
}
}
})