Vue3 如何用响应式 API 做简单状态管理?

你好同学,我是沐爸,欢迎点赞、收藏、评论和关注。

在 Vue3 中,对于简单状态,不使用 Pinia 和 Vuex,如何做状态管理?解下来我们看下如何使用 ref 和 reactive 做状态管理吧。

如果你有一部分状态需要在多个组件实例间共享,你可以使用 reactive() 来创建一个响应式对象,并将它导入到多个组件中:

// store.js
import { reactive } from 'vue'

export const store = reactive({
  count: 0
})
<!-- ComponentA.vue -->
<script setup>
import { store } from './store.js'
</script>

<template>From A: {{ store.count }}</template>
<!-- ComponentB.vue -->
<script setup>
import { store } from './store.js'
</script>

<template>From B: {{ store.count }}</template>

现在每当 store 对象被更改时,<ComponentA><ComponentB> 都会自动更新它们的视图。现在我们有了单一的数据源。

然而,这也意味着任意一个导入了 store 的组件都可以随意修改它的状态:

<template>
  <button @click="store.count++">
    From B: {{ store.count }}
  </button>
</template>

虽然这在简单的情况下是可行的,但从长远来看,可以被任何组件任意改变的全局状态是不太容易维护的。为了确保改变状态的逻辑像状态本身一样集中,建议在 store 上定义方法,方法的名称应该要能表达出行动的意图:

// store.js
import { reactive } from 'vue'

export const store = reactive({
  count: 0,
  increment() {
    this.count++
  }
})
<template>
  <button @click="store.increment()">
    From B: {{ store.count }}
  </button>
</template>

除了我们这里用到的单个响应式对象作为一个 store 之外,你还可以使用其他响应式 API 例如 ref() 或是 computed(),或是甚至通过一个组合式函数来返回一个全局状态:

import { ref } from 'vue'

// 全局状态,创建在模块作用域下
const globalCount = ref(1)

export function useCount() {
  // 局部状态,每个组件都会创建
  const localCount = ref(1)

  return {
    globalCount,
    localCount
  }
}

示例

示例一

Parent.vue
<template>
  <Child1></Child1>
  <Child2></Child2>
</template>

<script setup>
import Child1 from './Child1.vue'
import Child2 from './Child2.vue'
</script>

Child1.vue

<script setup>
import { store } from './store'

function handleClick() {
    store.increment()
}
</script>

<template>
    <div style="border:1px solid #333;padding:20px;">
        <h2>Child1.vue</h2>
        <p>{{ store.count }} - {{ store.doubleCount() }}</p>
        <button @click="handleClick">改变 store.count</button>

        <!-- 或者 -->
        <!-- <button @click="store.increment">改变 store.count</button> -->
    </div>
</template>

Child2.vue

<script setup>
import { store } from './store'
</script>

<template>
    <div style="border:1px solid #333;padding:20px;margin-top: 20px;">
        <h2>Child2.vue</h2>
        <p>{{ store.count }} - {{ store.doubleCount() }}</p>
    </div>
</template>

store.js

import { reactive } from 'vue'

export const store = reactive({
  count: 0,
  doubleCount() {
    return this.count * 2
  },
  increment() {
    this.count++
  }
})

效果预览

示例二

Parent.vue 不变。

Child1.vue

<script setup>
import { useCount } from './store'
const { globalCount, localCount, doubleCount, increment, incrementGlobal } = useCount()
</script>

<template>
    <div style="border:1px solid #333;padding:20px;">
        <h2>Child1.vue</h2>
        <p>globalCount - {{ globalCount }}</p>
        <p>localCount - {{ localCount }}</p>
        <p>doubleCount - {{ doubleCount }}</p>
        <button @click="increment">改变 store 中的 localCount</button>
        <button @click="incrementGlobal">改变 store 中的 globalCount</button>
    </div>
</template>

Child2.vue

<script setup>
import { useCount } from './store'
const { globalCount, localCount, doubleCount, increment, incrementGlobal } = useCount()
</script>

<template>
    <div style="border:1px solid #333;padding:20px;margin-top: 20px;">
        <h2>Child2.vue</h2>
        <p>globalCount - {{ globalCount }}</p>
        <p>localCount - {{ localCount }}</p>
        <p>doubleCount - {{ doubleCount }}</p>
        <button @click="increment">改变 store 中的 localCount</button>
        <button @click="incrementGlobal">改变 store 中的 globalCount</button>
    </div>
</template>

store.js

import { ref, computed } from "vue";

// 全局状态,创建在模块作用域下
const globalCount = ref(0)

export function useCount() {
  // 组件作用域
  const localCount = ref(0)
  const doubleCount = computed(() => localCount.value * 2)

  // 修改组件作用域数据
  function increment(n) {
    localCount.value++
  }

  // 修改全局作用域数据
  function incrementGlobal() {
    globalCount.value++
  }

  return {
    globalCount,
    localCount,
    doubleCount,
    increment,
    incrementGlobal
  }
}

效果预览


好了,分享结束,谢谢点赞,下期再见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沐爸muba

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值