状态管理和V-model

本文主要讨论使用状态管理工具VueX和Pinia和V-model的使用。

Vuex

在Vuex的官方文档中提到:

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。

假设我们定义了store如下:

1
2
3
4
5
export default new Vuex.Store({
state: {
str: 'test'
}
})

在组件中使用:

1
2
3
4
5
6
<template>
<div id="app">
<input type="text" v-model="this.$store.state.str">
{{ this.$store.state.str }}
</div>
</template>

如果我们修改了input的内容,则会:

可以看到数据并没有发生变化。
如果我们一定想用v-model的话,可以通过计算属性完成这个操作。在计算属性里面定义gettersetter,里面使用mutation完成state的更改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
computed: {
str: {
get() { return this.$store.state.str },
set(m) { this.$store.commit('changestr', m) }
},
}

//...

// store
mutations: {
changestr(state, m) {
state.str = m
}
}

在组件中使用:

1
2
3
4
5
6
<template>
<div id="app">
<input type="text" v-model="str">
{{ str }}
</div>
</template>

重新输入内容后,可以正常使用。

pinia

pina中store的定义分为两种,选项式和组合式。两种在使用上区别不大,以下以选项式API作为演示。

定义store如下:

1
2
3
4
5
6
7
8
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('count', {
state: () => {
return {
count: 0
}
}
})

组件中使用:

1
2
3
4
5
6
7
8
<template>
<input type="number" v-model="CounterStore.count">
{{ CounterStore.count }}
</template>
<script setup lang="ts">
import { useCounterStore } from '@/store/count'
const CounterStore = useCounterStore()
<script>

不需要其他操作,直接就可以进行双向绑定。
https://github.com/vuejs/pinia/discussions/854

值得注意的是,如果觉得CounterStore.count这种写法过于繁琐,需要通过storeToRefs转换,而不能直接进行解构

1
2
3
import { storeToRefs } from "pinia";
const CounterStore = useCounterStore()
const { count } = storeToRefs(CounterStore)

在js的代码中,使用store可以直接进行state的访问。但如果通过storeToRefs进行结构,则需要使用.value进行访问。

1
2
3
4
5
6
7
import { useSumStore } from '@/store/sum'
import { storeToRefs } from "pinia";
const sumStore = useSumStore()
const { sum } = storeToRefs(sumStore)
function add() {
sum.value += 1
}

源码:

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
export function storeToRefs<SS extends StoreGeneric>(
store: SS
): StoreToRefs<SS> {
// See https://github.com/vuejs/pinia/issues/852
// It's easier to just use toRefs() even if it includes more stuff
if (isVue2) {
// @ts-expect-error: toRefs include methods and others
return toRefs(store)
} else {
store = toRaw(store)

const refs = {} as StoreToRefs<SS>
for (const key in store) {
const value = store[key]
if (isRef(value) || isReactive(value)) {
// @ts-expect-error: the key is state or getter
refs[key] =
// ---
toRef(store, key)
}
}

return refs
}
}