在计算属性中使用 $refs

Posted

技术标签:

【中文标题】在计算属性中使用 $refs【英文标题】:Using $refs in a computed property 【发布时间】:2017-09-17 19:25:58 【问题描述】:

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

【问题讨论】:

是的,它仅在第一个渲染循环完成时定义。对于它的价值,明确建议不要,因为它不是响应式的。 vuejs.org/v2/guide/components.html#Child-Component-Refs你可能需要找到更好的模式... 使用 Watch 功能 内挂载:enter link description here 【参考方案1】:

要在这里回答我自己的问题,我在其他任何地方都找不到满意的答案。有时您只需要访问 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 是静态的,就好像它只在挂载时获取初始大小,如果对象更改,它不会更新。【参考方案2】:

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

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

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

【讨论】:

【参考方案3】:

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

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

【讨论】:

绝招!在computed 的逻辑中加入v-if 条件,以便将它们注册为依赖项。正是对 refv-if 切换元素的好奇心的答案 或者把访问$refs的代码放到nextTick里面:this.$nextTick(() => this.$refs...)【参考方案4】:

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

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

"[...]我们可以定义相同的函数作为方法,而不是计算属性。对于最终结果,这两种方法确实完全相同。但是不同的是,计算属性根据它们的反应性依赖项进行缓存。计算属性只会在其某些反应性依赖项发生变化时重新评估"

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

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

<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 属性更改时更新。【参考方案5】:

对于像我这样只需要将一些数据传递给 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
    
)

【讨论】:

如果您不需要它是被动的,这比接受的答案要好。【参考方案6】:

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

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

然后你观察变量:

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

注意 if 评估 this.$refs 的存在性,当它发生变化时,你会得到你的数据。

【讨论】:

【参考方案7】:

如果需要,请使用属性绑定。 :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;
   

【讨论】:

【参考方案8】:

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

    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;
        
    

【讨论】:

【参考方案9】:

另一种方法是完全避免使用 $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

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

vue composition API - ref变量内的计算属性

Vue 3 - 如何在没有 .value 的情况下使用反应式 ref 和计算?

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

初识vue3-setup语法糖,ref和reactive语法,computde计算属性,watch开启监听

Vue JS计算属性没有重新计算

[每天进步一点点~] vue uni-app 计算属性computed和监听watch