Vue 中未调用计算属性集

Posted

技术标签:

【中文标题】Vue 中未调用计算属性集【英文标题】:computed property set not called in Vue 【发布时间】:2020-11-06 14:57:01 【问题描述】:

根据文档,只要我定义 get/set 方法,我应该能够在 Vue 中使用计算属性作为 v-model,但在我的情况下它不起作用:

export default

    template: `
      <form class="add-upload" @submit.prevent="return false">
        <label><input type="checkbox" v-model="options.test" /> test </label>
      </form>
    `,

    computed: 

      options: 

        get()
          console.log('get');
          return test: false;
        ,

        set(value)
          console.log('set');
        ,

      ,

    


当我选中/取消选中输入时,显然不会调用 set。 但是在显示组件的时候会调用get...

【问题讨论】:

这里 option 是被动的,而不是 options.test 这是否意味着我需要使用 get/set 为每个键定义计算属性?那将迫使我编写大量重复的代码。我这里只有test,但我计划添加更多输入字段.. 使用表单提交。因为复选框将单独更新表单值 我不明白? 那么为什么要在 v-model 中使用计算属性呢?定义数据有什么问题?这里的问题是v-model 尝试在options.test 上设置值,但您的计算属性正在options 上设置新值。不确定这是否会自动工作。 【参考方案1】:

代替computed getter/setter,使用本地数据道具,初始化为目标localStorage项目;和一个deep watcher(检测任何子属性的更改)在更改时设置localStorage。这使您仍然可以将v-model 与本地数据属性一起使用,同时观察对象子属性的变化。

步骤:

    声明一个本地数据属性(名为options),初始化为localStorage的当前值:
export default 
  data() 
    return 
      options: 
    
  ,
  mounted() 
    const myData = localStorage.getItem('my-data')
    this.options = myData ? JSON.parse(myData) : 
  ,

    在 data 属性 (options) 上声明一个监视,将 deep=truehandler 设置为将 localStorage 设置为新值的函数:
export default 
  watch: 
    options: 
      deep: true,
      handler(options) 
        localStorage.setItem('my-data', JSON.stringify(options))
      
    
  ,

demo

【讨论】:

【参考方案2】:

编辑看了下你依赖localstorage的cmets,我只能建议你采取Vuex的方式,用一个持久化库来处理localstorage。 (https://www.npmjs.com/package/vuex-persist) 这样,您的本地存储将始终链接到您的应用程序,您不必每次都弄乱 getItem/setItem。

看看你的方法,我认为你有理由使用计算属性而不是数据属性。

出现问题是因为您的计算属性返回了一个在 get 处理程序中没有定义的对象。 无论您尝试什么,都无法在 set 处理程序中操作该对象。

getset 必须链接到一个公共引用。正如许多人所建议的那样,您的应用程序中的数据属性或事实来源(Vuex 实例就是一个很好的例子)。

这样,您的 v-model 将与您的计算属性的 set 处理程序完美配合。

这是一个演示解释的工作小提琴:

使用 Vuex

const store = new Vuex.Store(
  state: 
    // your options object is predefined in the store so Vue knows about its structure already
    options: 
      isChecked: false
    
  ,
  mutations: 
    // the mutation handler assigning the new value
    setIsCheck(state, payload) 
      state.options.isChecked = payload;
    
  
);

new Vue(
  store: store,
  el: "#app",
  computed: 
    options: 
      get() 
        // Here we return the options object as depicted in your snippet
        return this.$store.state.options;
      ,
      set(checked) 
        // Here we use the checked property returned by the input and we commit a Vuex mutation which will mutate the state
        this.$store.commit("setIsCheck", checked);
      
    
  
)
body 
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;


#app 
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;


h2 
  font-weight: bold;
  margin-bottom: 15px;
<div id="app">
  <h2>isChecked:  options.isChecked </h2>
  <input type="checkbox" v-model="options.isChecked" />
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex@2.0.0"></script>

带有数据属性

new Vue(
  el: "#app",
  data: 
    options: 
      isChecked: false
    
  ,
  computed: 
    computedOptions: 
      get() 
        return this.options;
      ,
      set(checked) 
        this.options.isChecked = checked;
      
    
  
)
body 
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;


#app 
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;


h2 
  font-weight: bold;
  margin-bottom: 15px;
<div id="app">
  <h2>isChecked:  computedOptions.isChecked </h2>
  <input type="checkbox" v-model="computedOptions.isChecked" />
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

恕我直言,您的方法有点特别,但同样,您必须有自己的理由这样做。

【讨论】:

【参考方案3】:

看来问题在于options 的存在和getter 的返回值。

你可以试试这个:

let options;

try 
  options = JSON.parse(localStorage.getItem("options"));

catch(e) 
  // default values
  options =  test: true ;


function saveOptions(updates) 
  localStorage.setItem("options", JSON.stringify( ...options, ...updates ));


export default
  template: `
    <form class="add-upload" @submit.prevent="return false">
      <label><input type="checkbox" v-model="test" /> test </label>
    </form>`,
  computed: 
    test: 
      get() 
        console.log('get');
        return options.test;
      ,
      set(value) 
        console.log('set', value);
        saveOptions( test: value );
      ,
    ,
  

希望这会有所帮助。

【讨论】:

【参考方案4】:

这里的代码非常简单的解释。计算属性取决于其他数据/反应变量。如果只有当反应性属性改变了它们的值,并且如果相同的属性用于计算其他一些计算属性,那么计算属性就会变成反应性的。

这样我们必须设置值并在 setter 和 getter 方法中获取。

new Vue(
  el: '#app',
  data: 
    message: 'Use computed property on input',
    foo:0,
    isChecked:true
  ,
  computed:
   bar:
    get: function()
        return this.foo;
    ,
   set: function(val)
     this.foo = val;
    
   ,
   
    check:
    get: function()
        return this.isChecked;
    ,
   set: function(val)
     this.isChecked = val;
    
   
  
)
<script src="https://unpkg.com/vue"></script>

<div id="app">
  <p> message  Text</p>
  <input type="text" v-model="bar" /> 
  bar

<br/>
 <p> message  Checkbox</p>
    <input type="checkbox" v-model="check" /> 
    
    check
</div>

【讨论】:

【参考方案5】:

我不熟悉是否有可以在这里工作的计算集方法,但还有一些其他方法可以解决这个问题。

如果您想要一个单一的 getter 来改变数据,您可以使用基于事件的方法来设置数据。这个方法是我最喜欢的:

export default 
  template: `
      <form class="add-upload" @submit.prevent="">
        <label for="test"> test </label>
        options.test
        <input id="test" type="checkbox" v-model="options.test" @input="setOptions(test: !options.test)"/>
      </form>
    `,
  data() 
    return 
      optionsData: 
        test: false
      
    
  ,
  computed: 
    options: 
      get() 
        return this.optionsData;
      ,
    ,
  ,
  methods: 
    setOptions(options) 
      this.$set(this, "optionsData",  ...this.optionsData, ...options )
    
  

如果您在 get/set 中没有真正做任何事情,您可以使用 data 选项

export default 
  template: `
      <form class="add-upload" @submit.prevent="">
        <label for="test"> test </label>
        options.test
        <input id="test" type="checkbox" v-model="options.test" />
      </form>
    `,
  data() 
    return 
      options: 
        test: false
      
    
  

然后还有每个属性的get/set选项

export default 
  template: `
      <form class="add-upload" @submit.prevent="">
        <label for="test"> test </label>
        test
        <input id="test" type="checkbox" v-model="test" />
      </form>
    `,
  data() 
    return 
      optionsData: 
        test: false
      
    
  ,
  computed: 
    test: 
      get() 
        return this.optionsData.test;
      ,
      set(value) 
        this.optionsData.test = value
      
    ,
  ,

【讨论】:

【参考方案6】:

Vue 计算属性的返回值不会自动变为响应式。因为您要返回一个普通对象,并且因为您要分配给计算属性的属性,所以 setter 不会触发。

您有两个问题需要解决,一个问题的解决方案是存储您计算的属性值的反应版本(请参阅Vue.observable())。下一个问题有点微妙,我需要知道为什么你想加入二传手。如果没有更多信息,我最好的猜测是您实际上正在寻找执行副作用。在这种情况下,您应该注意更改的值(请参阅vm.$watch())。

这是我根据上述假设编写该组件的方式。

export default 
  template: `
      <form class="add-upload" @submit.prevent="return false">
        <label><input type="checkbox" v-model="options.test" /> test </label>
      </form>
    `,
  computed: 
    options(vm) 
      return (
        vm._internalOptions ||
        (vm._internalOptions = Vue.observable( test: false ))
      )
    ,
  ,
  watch: 
    "options.test"(value, previousValue) 
      console.log("set")
    ,
  ,

如果您需要根据options 上的任何变化触发副作用,您可以深入观看。但最大的警告是对象必须是响应式的(由Vue.observable() 解决或在data 选项中定义它)。

export default 
  watch: 
    options: 
      handler(value, previousValue) 
        console.log("set")
      ,
      deep: true,
    ,
  ,

【讨论】:

以上是关于Vue 中未调用计算属性集的主要内容,如果未能解决你的问题,请参考以下文章

Vue.js this 在计算属性中未定义

:class 没有调用 vue 中的计算属性(计算属性没有被调用)

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

Vue的计算属性

vue计算属性进入页面就调用怎么设置

Vue 计算属性函数永远不会被调用