ChatGPT解决这个技术问题 Extra ChatGPT

在计算属性中使用 $refs

如何在计算中访问 $refs?第一次运行计算属性时它总是未定义。

是的,它仅在第一个渲染循环完成时定义。对于它的价值,明确建议不要在计算属性中使用 $refs,因为它不是响应式的。 vuejs.org/v2/guide/components.html#Child-Component-Refs 您可能需要找到更好的模式...
在已安装的内部使用 Watch 功能enter link description here

k
kissu

打算在这里回答我自己的问题,我在其他任何地方都找不到满意的答案。有时您只需要访问 dom 元素即可进行一些计算。希望这对其他人有帮助。

安装组件后,我不得不欺骗 Vue 更新计算属性。

Vue.component('my-component', {
  data(){
    return {
      isMounted: false
    }
  },
  computed:{
    property(){
      if(!this.isMounted)
        return;
      // this.$refs is available
    }
  },
  mounted(){
    this.isMounted = true;
  }
})

有完全相同的要求,这是我找到的唯一可行的解决方案。将类属性绑定到计算属性,如果嵌套组件(ref)设置了一些属性,则必须应用一些类。
@Bondsmith对不起哈哈,我是提问者和回答者,我想我忘了接受我自己的回答
我没有意识到。好笑ツ
非常有帮助,感谢@EricGuan 花时间回答您自己的问题以帮助他人
虽然这最初有效,但它不是反应性的,当 refs 更改时,它不会更新,例如,clientWidth 是静态的,就好像它只在安装时获取初始大小,如果对象发生更改,它不会更新。
t
tony19

我认为引用 Vue js guide 很重要:

$refs 仅在组件被渲染后才被填充,并且它们不是响应式的。它仅作为直接子操作的逃生舱口 - 您应该避免从模板或计算属性中访问 $refs。

因此,这不是您应该做的事情,尽管您总是可以绕过它。


k
kissu

如果您在 v-if 之后需要 $refs,则可以使用 updated() 挂钩。

<div v-if="myProp"></div>
updated() {
    if (!this.myProp) return;
    /// this.$refs is available
},

巧妙的把戏!将 v-if 条件合并到 computed 的逻辑中,以便将它们注册为依赖项。正是对具有 ref 并由 v-if 切换的元素的好奇心的答案
或者将访问 $refs 的代码放在 nextTick 中: this.$nextTick(() => this.$refs...)
我希望我能多次支持这个答案。你救了我!
t
tony19

我刚遇到同样的问题,并意识到这是计算属性不起作用的情况。

根据当前文档 (https://v2.vuejs.org/v2/guide/computed.html):

“[...]我们可以将相同的函数定义为方法,而不是计算属性。对于最终结果,这两种方法确实完全相同。但是,不同之处在于计算属性是基于它们的响应式缓存的依赖关系。计算属性只会在其某些反应性依赖关系发生变化时重新评估“

因此,在这些情况下(可能)发生的事情是完成组件的已安装生命周期并设置 refs 并不算作对计算属性的依赖项的反应性更改。

例如,在我的情况下,当我的参考表中没有选定的行时,我有一个需要禁用的按钮。因此,此代码将不起作用:

<button :disabled="!anySelected">Test</button>

computed: {
    anySelected () {
      if (!this.$refs.table) return false

      return this.$refs.table.selected.length > 0
    }
}

您可以做的是将计算属性替换为方法,并且应该可以正常工作:

<button :disabled="!anySelected()">Test</button>

methods: {
    anySelected () {
      if (!this.$refs.table) return false

      return this.$refs.table.selected.length > 0
    }
}

我认为这种方法是迄今为止最好的方法。它不会将指令扩展到 data()updated() 和其他部分。干净简洁。
方法不是响应式的,如果 ref 属性更改,也不会更新。
k
kissu

对于像我这样只需要将一些数据传递给 prop 的其他用户,我使用 data 而不是 computed

Vue.component('my-component', {
    data(){
        return {
            myProp: null
        }
    },    
    mounted(){
        this.myProp= 'hello'    
        //$refs is available              
        // this.myProp is reactive, bind will work to property
    }
})

如果您不需要它是被动的,这比接受的答案要好。
k
kissu

如果需要,请使用属性绑定。 :disabled 道具在这种情况下是被动的

<button :disabled="$refs.email ? $refs.email.$v.$invalid : true">Login</button>

但是要检查两个字段,我发现没有其他方法可以作为虚拟方法:

<button :disabled="$refs.password ? checkIsValid($refs.email.$v.$invalid, $refs.password.$v.$invalid) : true">
  {{data.submitButton.value}}
</button>
methods: {
   checkIsValid(email, password) {
      return email || password;
   }
}

M
Marco Santana

我遇到了类似的情况,我用以下方法修复了它:

  data: () => {
   return { 
    foo: null,
   }, // data

然后你观察变量:

    watch: {
     foo: function() {
       if(this.$refs)
        this.myVideo = this.$refs.webcam.$el;
       return null;
      },
    } // watch

请注意评估 this.$refs 是否存在的 if,当它发生变化时,您将获得数据。


J
Justo

我所做的是将引用存储到数据属性中。然后,我在挂载事件中填充此数据属性。

    data() {
        return {
            childComps: [] // reference to child comps
        }
    },
    methods: {
        // method to populate the data array
        getChildComponent() {
            var listComps = [];
            if (this.$refs && this.$refs.childComps) {
                this.$refs.childComps.forEach(comp => {
                    listComps.push(comp);
                });
            }
            return this.childComps = listComps;
        }
    },
    mounted() {
        // Populates only when it is mounted
        this.getChildComponent();
    },
    computed: {
        propBasedOnComps() {
            var total = 0;
            // reference not to $refs but to data childComps array
            this.childComps.forEach(comp => {
                total += comp.compPropOrMethod;
            });
            return total;
        }
    }

R
Raine Revere

另一种方法是完全避免 $refs 并且只订阅来自子组件的事件。

它需要子组件中的显式设置器,但它是反应式的,不依赖于挂载时间。

父组件:

<script>
{
  data() {
    return {
      childFoo: null,
    }
  }
}
</script>

<template>
  <div>
    <Child @foo="childFoo = $event" />

    <!-- reacts to the child foo property -->
    {{ childFoo }}

  </div>
</template>

子组件:

{
  data() {
    const data = {
      foo: null,
    }
    this.$emit('foo', data)
    return data
  },
  emits: ['foo'],
  methods: {
    setFoo(foo) {
      this.foo = foo
      this.$emit('foo', foo)
    }
  }
}

<!-- template that calls setFoo e.g. on click -->

这是正确的方法,brb