VueJS $watch $refs

问题描述 投票:0回答:8

可以

$watch
Vue
$refs
吗?

我想针对嵌套在当前 Vue 实例内但在

ready
回调内的子组件设置逻辑,
$refs.childcomponent
在处理时最初为
undefined

里面

ready()

this.$watch('$refs', function() {
    console.log("not firing");
}, { deep: true });

结果:错误:超出最大调用堆栈

watch
实例的属性

watch: {
  '$refs': {
     handler: function() { console.log("hit"); },
     deep: true
  }
}

结果:什么都没有。

javascript vue.js
8个回答
56
投票

您可以

$watch
$refs.<name>.<data>
但不能
$refs.<name>
本身,更不用说
$refs

https://jsfiddle.net/kenberkeley/9pn1uqam/

const Counter = {
  data: () => ({
    i: 0
  }),
  template: `<fieldset>
    <p>Counter</p>
    <code>i = {{ i }}</code>
    <button @click="i += 1"> Add One </button>
  </fieldset>`
}

const App = {
  components: { Counter },
  mounted () {
    this.$watch(
        () => {
            return this.$refs.counter.i
        },
      (val) => {
        alert('App $watch $refs.counter.i: ' + val)
      }
    )
  },
  template: `<fieldset>
    <p>App</p>
    <counter ref="counter" />
  </fieldset>`
}

new Vue({
    el: '#app',
    render: h => h(App)
})

36
投票

不,

$refs
没有反应,手表无法工作。


4
投票

在安装使用代码如下:

this.$watch(
        () => {
            return this.$refs.<name>.<data>
        },
      (val) => {
        alert('$watch $refs.<name>.<data>: ' + val)
      }
    )

1
投票

有一个解决办法。考虑到 JavaScript 在将数组分配给变量时不会创建数组的副本,它只是创建对原始数组的引用。知道 Vue 的 $refs 是数组,我们可以执行以下操作:

<template>
    <div>
            <ul v-if="showAvailable">
                 <li v-for="pet in allPets.available" :key="pet.id" ref="pets">
                      {{ pet.name }}
                 </li>
            </ul>
            <ul v-else>
                 <li v-for="pet in allPets.unavailable" :key="pet.id" ref="pets">
                      {{ pet.name }}
                 </li>
            </ul>
    </div>
</template>

<script>

export default {
    props: ['allPets'],

    data() {
         showAvailable: true // Normally would change from a button or something
         shownPets: null // set to null for now
    },

    mounted() {
         this.$set(this.$data, 'shownPets', this.$refs.pets);    
    },

    watch: {
         shownPets: {
                         handler(newVal, oldVal){
                              // Do something when the DOM changes!
                         },
                         deep: true
                    }
    }
}
</script>

瞧。组件安装后,我们将数据

shownPets
设置为我们的宠物
$ref
。该引用将根据
showAvailable
true
false
来保存不同的元素,现在我们可以观察 $ref 或 DOM 更改。


1
投票

如果您在created()期间在$refs中定义属性,或者如果您不需要数据中的后备存储,则在beforeCreate()期间定义属性是可能的。例如:

const vm = new Vue({
    el: "#app",
  data() {
    return {
        mode: 0,
        refTest: undefined
    };
  },
  created() {
    Object.defineProperty(this.$refs, 'test', {
      get: function () {
        return this.refTest;
      }.bind(this),
      set: function (newValue) {
        console.log(`set - Setting test refs to an array? ${Array.isArray(newValue)}`);
        this.refTest = newValue;
      }.bind(this),
      enumerable: true
    });
  },
  watch: {
    refTest: {
      handler(newValue, oldValue) {
        console.log(`watch - array? ${newValue ? Array.isArray(newValue) : undefined}`);
      },
      deep: false,
      immediate: true
    }
  },
  methods: {
    toggle() {
        this.mode = (this.mode === 1) ? 2 : 1
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <button @click="toggle">Toggle</button>
  <ul v-if="mode === 1">
    <li ref="test">Single Ref</li>
  </ul>
  <ul v-else-if="mode === 2">
    <li ref="test" v-for="s in ['Multiple', 'Refs']" :key="s">{{ s }}</li>
  </ul>
  <div v-else>No Refs</div>
</div>

这是我尝试过的唯一解决方案,当引用未与组件本身一起安装时,该解决方案有效。

这里使用defineProperty的原因是因为如果ref发生变化,观察者将失去对它的跟踪。

如果你想在defineProperty中处理你的动作,我想那没问题,然后你就可以摆脱你的观察者了。但您始终需要有一个后备存储 - 尽管它不需要位于数据中。


0
投票

以下解决方案使用MutationObserver

在已安装的挂钩中添加:

const config = {
      attributes: true,
      childList: true,
      subtree: true
    };
 // this will be triggered for any change in your element
    this.observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation) {
          this.infoHeight = this.$refs.info.clientHeight + 'px'
          console.log(" changed ", this.$refs.info.clientHeight)
        }
      });
    });
//observe the referenced element
    this.observer.observe(this.$refs.info, config);

完整示例

Vue.config.devtools = false;
Vue.config.productionTip = false;

new Vue({
  el: '#app',
  data: () => ({
    infoHeight: 0,
    observer: null,

    img: "https://images.ctfassets.net/hrltx12pl8hq/6TOyJZTDnuutGpSMYcFlfZ/4dfab047c1d94bbefb0f9325c54e08a2/01-nature_668593321.jpg?fit=fill&w=480&h=270"

  }),
  mounted() {
    const config = {
      attributes: true,
      childList: true,
      subtree: true
    };
    this.observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation) {
          this.infoHeight = this.$refs.info.clientHeight + 'px'
          console.log(" changed ", this.$refs.info.clientHeight)
        }
      });
    });
    this.observer.observe(this.$refs.info, config);


  },

 beforeDestroy(){
   this.observer.disconnect()
  },
  methods: {
    changeImg() {
      this.img = "https://i.pinimg.com/originals/a7/3d/6e/a73d6e4ac85c6a822841e449b24c78e1.jpg"
    },

  }
})
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>


<div id="app" class="container">
  <p>{{infoHeight}}</p>

  <button class="btn btn-primary" @click="changeImg">Change image</button>
  <div ref="info">
    <img :src="img" alt="image" />
  </div>
</div>


0
投票

在Vue 3中测试:你至少可以在

$watch
中的
$refs
上使用浅
mounted()

mounted () {
  this.$watch(
    '$refs',
    this.$refs.childComponent?.whatever(),
    { immediate: true }
  )
}

使用可选的链接运算符 (

?.
) 很重要,因为
$refs.childComponent
最初可能是未定义的。但是当它添加到
$refs
时,观察者将再次被触发。


0
投票

万一这里有人像我一样遇到这个问题,除了模板参考之外还需要观看构图参考,这里是如何做到这一点

import { ref, watch } from 'vue'

const watchedRef = ref('hello')

watch(() => watchedRef.value, () => { 
  console.log('watching', watchedRef.value
  // do more stuff here
})
© www.soinside.com 2019 - 2024. All rights reserved.