vuex,computed中 getters只执行了一次,state更新页面数据没有更新

这是看到另外一个问题记录,恰好我也遇到这个问题了,就记录一下。

我使用的是TS+组合式API

功能描述

首先我定义了一个名叫user的modules,并使用了命名空间
功能很简单,就是点击按钮,修改count的值,并同步的在页面上展示count

const state = {
 count: 0
}
const mutations = {
  increment (state, param) {
    if (param == null) {
      state.count += 1 
    } else {
      state.count = param
    }
    console.log(state.count)
  
  }
}
const actions = {
  setCountAction({commit}:any,param:number){
    commit('increment',param)
  }
}
const getters = {
  pageCount() {
    return state.count
  }
} 

export default{
  namespaced: true,
  mutations,
  actions,
  state,
  getters

}

页面上有两个按钮,

    <button @click="doCommit">doCommit</button>
    <button @click="doAction">doAction</button>

一个去提交mutation一个去执行action

const doCommit = () => {
  store.commit('user/increment')
  console.log('doCommit执行了,值为:' + store.getters['user/pageCount'])
  refCount.value = store.getters['user/pageCount']
}
const doAction = () => {
  store.dispatch('user/setCountAction',300)
  console.log('doAction执行了,值为:' + store.getters['user/pageCount'])
}

一个p标签,我想用它来展示我的count值

    <p>{{ store.getters['user/pageCount'] }}</p>

问题描述

现在问题来了,这个p标签内的值,只会更新一次,在我触发了doCommit,doAction事件后,不会实时的更新,但通过查看state状态,可以看到count值已经改变了。
点击doCommit事件,打印如下

1
doCommit执行了,值为:1

我第一反应是使用computed计算属性来监听它的变化,因此我改成如下

const count = computed(()=> {
  console.log('computed执行了,值为:' + store.getters['user/pageCount'])
  return store.getters['user/pageCount']
}) 
<p>{{ count }}</p>

点击doCommit事件,打印如下

computed执行了,值为:0
user.ts:11 1
doCommit执行了,值为:1
user.ts:11 2
doCommit执行了,值为:2
user.ts:11 3
doCommit执行了,值为:3
user.ts:11 4
doCommit执行了,值为:4

可以发现无论点击几次事件,computed只拿到了最初的原始值。

改用watch试试

watch(store.getters['user/pageCount'], (newValue, oldValue) => {
      console.log('count 变化了 newValue', newValue);
      console.log('count 变化了 oldValue', oldValue);
}, { immediate: true, deep: true });

点击doCommit事件,打印如下

count 变化了 newValue undefined
count 变化了 oldValue undefined
user.ts:11 1
doCommit执行了,值为:1
user.ts:11 2
doCommit执行了,值为:2
user.ts:11 3
doCommit执行了,值为:3

watch更离谱,连初始值都没拿到

修改watch

immediate: false

这次则不会执行,没有打印输出。

原因排查

首先我怀疑是生命周期问题,查看computed watch的执行时机,找到如下:

created --> watch(immediatet:true) --> computed --> mounted–> watch

然后重点来了

watch中设置immediate:true时,在组件加载时立刻执行
去官网查看生命周期
请添加图片描述
可以发现,数据和视图的绑定应该是在mounted之后,这就解释了为什么watch(immediatet:true) 拿到的值为undefined

至于watch(immediatet:false)为什么只执行了一次,是因为v-model双向绑定的数据值都已更新,才会执行watch方法

这也是为什么computed也只执行了一次的原因,因为我们的双向绑定用错了

v-bind主要用于HTML元素属性,是数据到页面的单项绑定,如
:style :class :disabled v-bind:id

v-model主要用于表单元素、下拉框,如input、textarea、select是一个用于实现“双向绑定”的指令。

而我们使用的
{{ count }}
是文本插值,它使用的是“Mustache”语法 (即双大括号),同时每次 count 属性更改时它也会同步更新。
但现在它没有更新,这是因为count并不具备响应式的状态,它就是一个普普通通的值,在页面不刷新时,展示值不会更改。

问题解决

方法一

使用 ref() 函数来声明响应式状态,实现页面数据的更新

/// 声明并取得初始值
const refCount = ref(store.getters['user/pageCount'])
/// 在事件中对refCount赋值
const doCommit = () => {
  store.commit('user/increment')
  refCount.value = store.getters['user/pageCount']
}
  <p>{{ refCount }}</p>

为什么要使用 ref? ​

你可能会好奇:为什么我们需要使用带有 .value 的 ref,而不是普通的变量?为了解释这一点,我们需要简单地讨论一下 Vue 的响应式系统是如何工作的。

当你在模板中使用了一个 ref,然后改变了这个 ref 的值时,Vue 会自动检测到这个变化,并且相应地更新 DOM。这是通过一个基于依赖追踪的响应式系统实现的。当一个组件首次渲染时,Vue 会追踪在渲染过程中使用的每一个 ref。然后,当一个 ref 被修改时,它会触发追踪它的组件的一次重新渲染。

方法二

你也可以声明一个对象

const result = reactive({count:store.getters['user/pageCount']})
const doCommit = () => {
  store.commit('user/increment')
  result.count = store.getters['user/pageCount']
}
<p>{{ result.count }}</p>

这样也是可以的。

注意:reactive() API 有一些局限性:
有限的值类型:它只能用于对象类型 (对象、数组和如 Map、Set 这样的集合类型)。它不能持有如 string、number 或 boolean 这样的原始类型。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

张子都

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

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

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

打赏作者

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

抵扣说明:

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

余额充值