关于Vue中的ref和$refs的二三事

  Vue中的ref可以用来给元素或子组件注册引用信息,而$refs可以用来获取ref注册过的元素或组件,关于这一点概念,其实很容易理解,使用一下就可以上手了,详细的概念参见 [vue-ref]   [vue-$refs]

  但是在实际项目中,会发现,有时候通过 this.$refs.ref名称 来获取对应的DOM或组件时,并不能成功,而要在后面加一个 [0] 才能成功获取到。多留心一下,会发现好像是涉及到循环时,就会出现这种情况,那不如来多做几组测试来验证一下,顺便试试各种特殊情况下的特别之处。

  太长了,我要直接看结论

  

  part 1 常规用法

  当ref用在组件上时,通过$refs获取的是该组件的实例,可进而调用组件内定义的方法、属性等;

  如下图所示,当ref定义在组件Home上时,通过this.$refs.test获取的是Home组件对应的实例对象,通常我们可以通过这种方式来调用某个组件内的methods、data中定义的数据、或是进而获取其子组件等。

<template>
  <div id="app">
    <Home ref="test" />
  </div>
</template>

<script>
import Home from './views/Home'
export default {
  components: { Home },
  mounted () {
    console.log(this.$refs.test)
  }
}
</script>

  当ref用在DOM节点上时,通过$refs获取的是该DOM元素节点。

  如下图所示,当ref定义在DOM元素上时,通过this.$refs.test获取的是该DOM元素,且包含子元素,进而可以进行下一步的DOM操作。

<template>
  <div id="app">
    <p ref="test">
      <span>123</span>
    </p>
  </div>
</template>

<script>
export default {
  mounted () {
    console.log(this.$refs.test)
  }
}
</script>

 


 

  Part 2 多处ref引用的值相同时(非v-for内使用时)

   当html中ref值相同的节点是兄弟节点时,通过$refs获取到的是文档中顺序靠下的节点(使用在DOM上和组件上原则一致,以DOM为例)

<template>
  <div id="app">
    <p ref="test">
      <span>123</span>
    </p>
    <span ref="test">456</span>
  </div>
</template>

<script>
export default {
  mounted () {
    console.log(this.$refs.test)
  }
}
</script>

 

   当html中ref值相同的节点是嵌套关系时,比如父子关系,则通过$refs获取到的是外层节点(使用在DOM上和组件上原则一致,以DOM为例)

<template>
  <div id="app">
    <p ref="test">
      <span ref="test">123</span>
    </p>
  </div>
</template>

<script>
export default {
  mounted () {
    console.log(this.$refs.test)
  }
}
</script>

 

   当html中ref值相同的节点不在同一层级,也不是嵌套关系时,仍然遵循文档中顺序靠下的节点被获取的原则(使用在DOM上和组件上原则一致,以DOM为例)

<template>
  <div id="app">
    <p ref="test">456</p>
    <p>
      <span ref="test">123</span>
    </p>
  </div>
</template>

<script>
export default {
  mounted () {
    console.log(this.$refs.test)
  }
}
</script>


 

   Part 3 当ref值是个变量时(非v-for内使用时)

   ref值使用变量时,通过this.$refs[变量名称]指向的依旧是该DOM节点或组件实例,不需要加[0],且当出现ref值重复时,仍然满足Part2实践中的原则

<template>
  <div id="app">
    <p :ref="name">
      <span>123</span>
    </p>
  </div>
</template>

<script>
export default {
  data () {
    return {
      name: 'test'
    }
  },
  mounted () {
    console.log(this.$refs[this.name])
  }
}
</script>

 


 

   Part 4 ref用在v-for上时

  当ref值相同时,this.$refs[ref名称]获取到的是该DOM节点或组件的数组,此处以组件为例

<template>
  <div id="app">
    <Home v-for="num in loop" :key="num" ref="test"></Home>
  </div>
</template>

<script>
import Home from './views/Home'
export default {
  components: { Home },
  data () {
    return {
      loop: 3
    }
  },
  mounted () {
    console.log(this.$refs.test)
  }
}
</script>

 

   当ref值不同时,this.$refs[ref名称]无法获取对应的DOM节点或是组件,需要在其后追加[0]才可访问,此处以组件为例

<template>
  <div id="app">
    <Home v-for="num in loop" :key="num" :ref="'test' + num"></Home>
  </div>
</template>

<script>
import Home from './views/Home'
export default {
  components: { Home },
  data () {
    return {
      loop: 3
    }
  },
  mounted () {
    console.log(this.$refs.test1)
  }
}
</script>

 


 

   总结:

     1. ref用在DOM上时,通过this.$refs[ref名称]访问的是该DOM元素节点;ref用在组件上时,通过this.$refs[ref名称]访问的是该组件的实例对象,可以进而调用组件的方法或者获取组件的属性等;

    2. 当同一组件内部,有多处ref引用值相同的情况时,this.$refs[ref名称]指向文档流中靠下的组件或DOM节点,若是父子关系,则指向父级组件或DOM节点;

    3. 若ref的值是个变量,只要不是用在v-for里,通过this.$refs[变量名称]指向的依旧是该DOM节点或组件实例,且当出现ref值重复时,仍然满足取靠下的节点或是父级节点的规则;

    4. 若ref用在v-for里,当循环内的ref值相同时,this.$refs[ref名称]获取到的是该DOM节点或组件的数组,当ref值不同时,需提供this.$refs[ref名称][0]来获取该DOM节点或组件实例。

 

  以上各项实践可能考虑的情况不一定全,若是有遇到其他情况出现不一样的用法和结果,再进行补充。

 

 

 

 

 

 

 

 

 

 

posted @ 2019-12-12 18:10  吃火鸡的馒头  阅读(6579)  评论(0编辑  收藏  举报