假设在根组件用 dispatch 触发一个异步的 Action
async created() {
await this.$store.dispatch(`base/info/get`)
},
在路由组件里 watch 数据
computed: {
...mapGetters({
$$info: 'base/info/get'
})
},
watch: {
$$info(val) {
console.log(val)
}
}
刷新页面, 这时候会出现有时候能触发 watch, 有时候又不触发, 这到底是什么原因呢?
看 vue 的源代码:
https://github.com/vuejs/vue/blob/dev/src/core/instance/state.js#L48-L62
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
我们可以看到, computed 是早于 watch 的, 那么我们就大概能理解上面是什么原因造成的了
当执行到 computed 时, 已经取到 ajax 返回的数据, 那么将 watch 不到 $info 的变化, $info 的数据已经是 ajax 的返回值 (其他页面已经加载, 再切换到该路由也是相同情况
)
当执行到 computed 时, 取到的是 vuex 的默认值, 那么将 watch 得到 $info 的变化, $info 的值是 vuex 的默认值变化成 ajax 加载完成后返回的值
这一切就取决于 ajax 的加载速度, 但是 ajax 的加载速度是不可控的, 要解决这个问题, 就只能在 mounted 里再加一段代码
computed: {
...mapGetters({
$info: 'base/info/get'
})
},
watch: {
$info(val) {
// 如果组件加载完成时, 还没有取到$info的值, 那么从这里执行相关方法
this.someMethods(val)
}
},
mounted() {
// 如果组件加载完成时, 已经取到$info的值, 那么从这里执行相关方法
if (this.$Info) {
this.someMethods(this.$Info)
}
},
methods: {
someMethods(info) {
console.log(info)
}
}
======== 2019.11.13 更新下写法 ========
data() {
return {
tmpWatch: null
}
}
computed: {
...mapGetters({
$info: 'base/info/get'
})
},
mounted() {
const tmpFn = () => {
// 相关逻辑
this.$info.children.forEach(item => {})
// 如果只需要监听一次, 执行完相关逻辑后可以直接注销
if (this.tmpWatch) this.tmpWatch()
}
// 如果组件加载完成时, 已经取到$info的值, 那么从这里执行相关方法
if (this.$info) {
tmpFn()
// 否则通过 $watch 来监听
} else {
this.tmpWatch = this.$watch('$info', tmpFn)
}
}