关于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节点或组件实例。
以上各项实践可能考虑的情况不一定全,若是有遇到其他情况出现不一样的用法和结果,再进行补充。