首页 文章

突变未使用Vuex中的计算属性进行注册

提问于
浏览
0

我有一个系统,它使用许多表单组件来根据输入的信息实时更新视图 . 当我第一次构建应用程序时,我主要使用v-model,本地组件数据和vue save状态来将数据保存在本地存储中,以便在重新加载页面时保持不变 .

然而,当我在程序扩展时转移到vuex时,这并不是那么简单,所以我决定使用带有getter和setter的计算属性的v-model,所以我不必为50多个不同的输入写入更改函数 . 我还意识到你可以为一个对象创建一个计算变量,并且仍然使用v-model来访问和更新该对象的属性,如下所示:

<div v-for="prof in info.profs">
  <textarea v-model="prof.name" class="code-input uk-input" rows="1" cols="20"></textarea>
  <textarea v-model="prof.email" class="code-input uk-input" rows="1" cols="25"></textarea>
  <textarea v-model="prof.office" class="code-input uk-input" rows="1" cols="50"></textarea> <br>
  </div>

info: {
  get () {
    return this.$store.getters.getInfo
  },
  set (payload) {
    this.$store.commit('updateInfo', payload)
  }
},

这很好用,商店更新每个属性的数据,而不必创建自己独立的计算变量,但由于某种原因,它不会在vue chrome dev工具上显示为已提交的变体“updateInfo”,当我使用时vuex的本地存储插件,如vuex-persistedstate或vuex-persist,它不会更改本地存储数据,直到我提交另一个正常结构的突变 . 现在我的解决方法是在组件中创建属性的本地副本,然后观察该属性的更改并提交到商店,这让我可以再次使用组件级别localstorage mixin,但我觉得必须有更好的执行此操作的方法不涉及为info中的每个属性编写更改函数或计算变量,因为在此应用程序中这将非常详细 .

data () {
return {
  info: this.$store.getters.getInfo
 }
},
watch: {
info: function(payload){
  this.$store.commit('updateInfo', payload)
 }
},

1 回答

  • 1

    实际上,两种方式都是错误的 . 将 strict: true 添加到您的商店,您会看到两种情况都会抛出错误 .

    在两种选择中, profnameemailoffice 属性都被直接修改(这违反了Vuex原则,规定每次变化都应该通过突变发生) .

    类似地,不是那个计算的setter(第一种情况),也不是那个观察者(第二种情况)被触发,因为你没有修改 item ,而是它的深层嵌套属性(例如 name ) .


    最简单的解决方案,允许你仍然 use a mixin ,是抛弃 v-model 并使用 :value@input 绑定 . 例:

    <textarea :value="prof.name" @input="updateProf(prof, 'name', $event)" >
    

    请注意,它使用 updateProf 方法,该方法提交变异(见下文)并进入mixin .

    这样所有修改都在突变中完成 . 最后要注意的是,如果你使用 :value@input verbose,你可以创建一个custom directive来处理它 .

    JSFiddle link或下面的演示(相同代码) .

    const store = new Vuex.Store({
      strict: true,
      state: {
      	info: {
        	profs: [
          	{name: "Alice", email: "alice@example.com", office: "NY"},
            {name: "Bob", email: "bob@example.com", office: "CA"}
          ]
        }
      },
      mutations: {
      	updateProf(state, {prof, prop, value}) {
        	prof[prop] = value;
        }
      },
      getters: {
        getInfo: state => {
          return state.info
        }
      }
    });
    const mixin = {
      computed: {
        info() {
          return this.$store.getters.getInfo
        }
      },
      methods: {
        updateProf(prof, prop, e) {
        	this.$store.commit('updateProf', {prof, prop, value: e.target.value})
        }
    	}
    }
    new Vue({
      store,
      mixins: [mixin],
      el: '#app'
    })
    
    <script src="https://unpkg.com/vue"></script>
    <script src="https://unpkg.com/vuex"></script>
    
    <div id="app">
      {{ info }}
      <div v-for="prof in info.profs">
        <hr>
        name: <textarea :value="prof.name" @input="updateProf(prof, 'name', $event)" class="code-input uk-input" rows="1" cols="20"></textarea> <br>
        email: <textarea :value="prof.email" @input="updateProf(prof, 'email', $event)" class="code-input uk-input" rows="1" cols="25"></textarea> <br>
        office: <textarea :value="prof.office" @input="updateProf(prof, 'office', $event)" class="code-input uk-input" rows="1" cols="50"></textarea>
      </div>
    </div>
    

    Keeping v-model

    只是这样,没有人告诉我没有你能够继续使用 v-model 的方法 . 这个替代方案的关键点是深度克隆和深度克服的功能 . 我提供了两个简单/天真的实现,YMMV:

    JSFiddle link . 演示(与小提琴相同的代码)如下:

    const store = new Vuex.Store({
      strict: true,
      state: {
      	info: {
        	profs: [
          	{name: "Alice", email: "alice@example.com", office: "NY"},
            {name: "Bob", email: "bob@example.com", office: "CA"}
          ]
        }
      },
      mutations: {
      	updateInfo(state, data) {
        	state.info = data
        }
      },
      getters: {
        getInfo: state => {
          return state.info
        }
      }
    });
    
    // these two functions are key here
    // consider using other implementations if you have more complicated property types, like Dates
    function deepClone(o) { return JSON.parse(JSON.stringify(o)); }
    function deepEquals(o1, o2) { return JSON.stringify(o1) === JSON.stringify(o2) }
    
    const mixin = {
    	data() {
        return {
          info: deepClone(this.$store.getters.getInfo),
        }
      },
      computed: {
        getInfo() {
          return this.$store.getters.getInfo;
        }
      },
      watch: {
        getInfo: {
        	deep: true,
        	handler(newInfo) {
            if (!deepEquals(newInfo, this.info)) { // condition to prevent infinite loops
        			this.info = deepClone(newInfo);
            }
        	}
        },
      	info: {
        	deep: true,
        	handler(newInfo) {
        		this.$store.commit('updateInfo', deepClone(newInfo))
        	}
        }
    	}
    }
    new Vue({
      store,
      mixins: [mixin],
      el: '#app'
    })
    
    <script src="https://unpkg.com/vue"></script>
    <script src="https://unpkg.com/vuex"></script>
    
    <div id="app">
      {{ info }}
      <div v-for="prof in info.profs">
        <hr>
        name: <textarea v-model="prof.name"  class="code-input uk-input" rows="1" cols="20"></textarea> <br>
        email: <textarea v-model="prof.email"  class="code-input uk-input" rows="1" cols="25"></textarea> <br>
        office: <textarea v-model="prof.office"  class="code-input uk-input" rows="1" cols="50"></textarea>
      </div>
    </div>
    

相关问题