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 这样的原始类型。
更多推荐
所有评论(0)