如何将 Vuex 与异步计算的 setter 属性一起使用

Posted

技术标签:

【中文标题】如何将 Vuex 与异步计算的 setter 属性一起使用【英文标题】:How to use Vuex with asynchronous computed setter property 【发布时间】:2019-02-10 11:41:11 【问题描述】:

我有以下组件,其中有一个 theme 计算属性。计算属性的set 函数执行一个名为setTheme 的Vuex 操作,该操作返回一个promise 并更新Vuex 状态下的theme 属性。

<template>
  <div>
    <input id="light-theme-radio" v-model="theme" type="radio" value="light">
    <label for="light-theme-radio">Light</label>

    <input id="dark-theme-radio" v-model="theme" type="radio" value="dark">
    <label for="dark-theme-radio">Dark</label>
  </div>
</template>

<script>
import Vue from "vue";
import  createNamespacedHelpers  from "vuex";

const  mapActions  = createNamespacedHelpers("theme");

export default 
  computed: 
    theme: 
      get() 
        return this.$store.state.theme.theme;
      ,
      set(value) 
        this.setTheme(value);
      
    
  ,
  methods: 
    ...mapActions(["setTheme"])
  
;
</script>

问题是theme.get 计算属性在setTheme 完成以使用新选择的项目更新单选按钮后没有被调用。使用异步设置器时,解决此问题的最佳方法是什么。这就是我的 Vuex 的样子:

export const state = 
  theme: "light"
;

export const mutations = 
  theme: (s, p) => (s.theme = p)
;

export const actions: ActionTree = 
  async setTheme(context, theme) 
    context.commit("theme/theme", theme);
    // ...omitted
    await Timer.delay(750);
    // ...omitted
  
;

const mainModule = 
  actions,
  getters,
  mutations,
  namespaced: true,
  state
;
export default mainModule;

const modules = 
  other: otherModule,
  theme: themeModule
;

const store = new Store(
  modules,
);
export default store;

【问题讨论】:

在等待promise解决期间,你要渲染什么? 【参考方案1】:

如果我理解正确,您遇到的问题是两个单选按钮都有选中效果,这是由于 Vue 没有及时渲染造成的。

所以解决方案是让 Vue 先渲染,然后等待 promise。完成后,再次渲染。

以下是两种方法

    使用vm.$forceUpdate

    提交一个像loading...这样的假值,Vue会先渲染(Vue是数据驱动的),当真正的值出现后,Vue会再次自动渲染。

下面是一个简单的演示:

Vue.config.productionTip = false
const store = new Vuex.Store(
  state: 
    theme: "light"
  ,
  mutations: 
    theme: (s, p) => (s.theme = p)
  ,
  actions: 
    setTheme: async function (context, theme) 
      return new Promise((resolve, reject) => 
        setTimeout(()=> 
          context.commit("theme", theme)
          resolve('done')
        , 1500)
      )
    
  
)

new Vue(
  el: '#app',
  store,
  data() 
    return 
      updatedCount: 1
    
  ,
  computed: 
    theme: 
      get() 
        return this.$store.state.theme
      ,
      set(value) 
        //or use this.$forceUpdate() instead
        this.$store.commit("theme", 'loading...') //or other values
        this.setTheme(value)
      
    
  ,
  updated()
    console.log('updated', this.updatedCount++)
  ,
  methods: 
    ...Vuex.mapActions(["setTheme"])
  
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<script src="https://unpkg.com/vuex@3.0.1/dist/vuex.js"></script>
<div id="app">
  <div>
    <h3>theme</h3>
    <input id="light-theme-radio" v-model="theme" type="radio" value="light">
    <label for="light-theme-radio">Light</label>

    <input id="dark-theme-radio" v-model="theme" type="radio" value="dark">
    <label for="dark-theme-radio">Dark</label>
  </div>
</div>

【讨论】:

以上是关于如何将 Vuex 与异步计算的 setter 属性一起使用的主要内容,如果未能解决你的问题,请参考以下文章

Vuex 语法错误与 localcomputed 函数结合 getter 和 setter

vue的计算属性 是怎么关联某个数据

Typescript Vuex - 如何使用 setter 和 getter 定义状态?

如何在 Vuetify 组件中使用 Vue v-model 绑定以及计算属性和 Vuex?

如何将计算属性中的 vuex 数据传递给 data prop

Vuex 在异步操作完成之前访问状态