7.使用pinia作为状态管理。
export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, name: 'Eduardo' }), getters: { doubleCount: (state) => state.count * 2, }, actions: { increment () { this.count++ }, }, })使用的时候,调用useCounterStore即可。
import { useCounterStore } from '@/stores/counter' import { computed } from 'vue' // 堆代码 duidaima.com const store = useCounterStore() setTimeout(() => { store.increment() }, 1000) const doubleValue = computed(() => store.doubleCount)看上去还不错,但是我模版中全部用的是组合式写法,肯定要用组合式API,试着写了个demo,ref就是选项式写法中的state,computed就是选项式中的getters,function就是actions。
// useTime.ts import { defineStore } from 'pinia' import { computed, ref } from 'vue' import vueConfig from '../../../common/config/vueConfig' import * as dayjs from 'dayjs' export default defineStore('time', () => { const $this = vueConfig() const time = ref<number>() const timeFormat = computed(() => dayjs(time.value).format('YYYY-MM-DD HH:mm:ss')) const getSystemTime = async () => { const res = await $this?.$request.post('/system/time') time.value = Number(res.timestamp) } return { timeFormat, getSystemTime } })调用时解构赋值,就可以直接用了。
// index.vue <script setup lang="ts"> import { onMounted } from 'vue' import useTime from './use/useTime' const { timeFormat, getSystemTime } = useTime() onMounted(async () => { // 请求 await getSystemTime() console.log('当前时间:', timeFormat) }) </script>优雅了很多,之前用vuex时还有个问题,storeA中的state、actions等,会在storeB中使用,这一点pinia文档也有说明,直接在storeB调用就好了,比如我想在另一个组件中调用上文中提到的timeFormat。
defineStore('count', () => { const count = ref<number>(0) const { timeFormat } = useTime() return { count, timeFormat, } })怎么看着这么眼熟呢,这不就是组合式函数吗?为什么我要用defineStore再包一层呢?试一试不用pinia,看能不能完成状态管理。
// useCount.ts import { computed, ref } from 'vue' const useCount = () => { const count = ref<number>(0) const doubleCount = computed(() => { return count.value * 2 }) const setCount = (v: number) => { count.value = v } return { count, doubleCount, setCount, } } export default useCount使用时直接解构申明,并使用。
import useCount from './use/useCount' const { count, setCount } = useCount() onMounted(async () => { console.log('count', count.value) // 0 setCount(10) console.log('count', count.value) // 10 })最大的问题来了,如何在多个地方共用count的值呢,这也是store最大的好处,了解javascript函数机制的我们知道useCount本身是一个闭包,每次调用,里面的ref就会重新生成。count就会重置。
import useCount from './use/useCount' const { count, setCount } = useCount() const { doubleCount } = useCount() onMounted(async () => { console.log('count', count.value, doubleCount.value) // 0 0 setCount(10) console.log('count', count.value, doubleCount.value) // 10 0 })这个时候doubleCount用的并不是第一个useCount中的count,而是第二个重新生成的,所以setCount并不会引起doubleCount的变化。怎么办呢?简单,我们只需要把count的声明暴露在全局环境中,这样在import时就会申明了,调用函数时不会被重置。
import { computed, ref } from 'vue' const count = ref<number>(0) const useCount = () => { const doubleCount = computed(() => { return count.value * 2 }) const setCount = (v: number) => { count.value = v } return { count, doubleCount, setCount, } } export default useCount当我们多次调用时,发现可以共享了。
import useCount from './use/useCount' const { count, setCount } = useCount() const { doubleCount } = useCount() onMounted(async () => { console.log('count', count.value, doubleCount.value) // 0 0 setCount(10) console.log('count', count.value, doubleCount.value) // 10 20 })但是这个时候count是比较危险的,store应该可以保护state不被外部所修改,很简单,我们只需要用readonly包裹一下返回的值即可。
import { computed, readonly, ref } from 'vue' const count = ref<number>(0) const useCount = () => { const doubleCount = computed(() => { return count.value * 2 }) const setCount = (v: number) => { count.value = v } return { // readonly可以确保引用对象不会被修改 count: readonly(count), doubleCount, setCount, } } export default useCount总结