# 如何获取 v-for 渲染的多个不同 ref 的 dom
🍍 在编写 vue
项目过程中,遇到了获取不到正确的 dom
元素节点的问题。
功能界面如图所示:
需要实现的是,点击每个播放器的右上角的关闭按钮,则关闭播放器。
代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105<template>
<div class="device-status">
<!-- 树组件 -->
<Tree></Tree>
<!-- 业务内容 -->
<div class="content-container" ref="container">
<h1>{{ msg }} (这是待删除文字) 具体看 status.vue 看代码写法</h1>
<div>{{buildings}}</div>
<div class="video-box" ref="video1">
<Player
src="https://gss3.baidu.com/6LZ0ej3k1Qd3ote6lo7D0j9wehsv/tieba-smallvideo/12846619_51a524dffce6834f1d221be2a1037834.mp4"
poster="https://puui.qpic.cn/fans_admin/0/3_118841988_1557667793407/0"
></Player>
<i class="el-icon-circle-close" @click="close('video1')"></i>
</div>
</div>
</div>
</template>
<script>
import Tree from "component/checkbox-tree/tree";
import Player from "component/player/player";
export default {
data() {
return {
msg: "我是设备状态及控制组件",
videoList: [
{
id: 1,
index: 0,
src: "https://gss3.baidu.com/6LZ0ej3k1Qd3ote6lo7D0j9wehsv/tieba-smallvideo/12846619_51a524dffce6834f1d221be2a1037834.mp4",
poster: "https://puui.qpic.cn/fans_admin/0/3_118841988_1557667793407/0"
},
{
id: 2,
index: 1,
src: "https://cdn.theguardian.tv/webM/2015/07/20/150716YesMen_synd_768k_vp8.webm",
poster: "https://ww1.sinaimg.cn/large/007i4MEmgy1g29h63wl0yj30et08c0tc.jpg"
}
]
};
},
computed: {
buildings(){
return this.$store.state.buildingTree
}
},
components: {
Tree,
Player
},
watch: {
buildings(newVal, oldVal){
console.log("监听到树选中值变化",JSON.stringify(newVal))
//判断树数组的值,空则不作操作,否则带上树id
//进行 http 请求获取数据
}
},
methods: {
//关闭播放器
close(videoStr){
//应该用数据驱动dom,这里直接操作了dom,不符合vue的理念,暂时
let video = this.$refs[videoStr]
video.parentNode.removeChild(video)
}
}
};
</script>
<style scoped lang="stylus">
.device-status{
.el-tree{
width: 200px;
height: calc(100vh - 70px);
background-color: #f8f8f8;
float: left;
padding: 10px 20px;
}
.content-container{
padding: 20px;
margin-left: 200px;
.video-box{
position: relative;
width: 640px;
display: inline-block;
border: 5px solid #6699FF;
margin: 40px 40px 10px 10px;
.el-icon-circle-close{
position: absolute;
top: -40px;
right: -40px;
color: #4A6495;
font-size: 40px;
opacity: 0.8;
cursor: pointer;
}
}
}
}
</style>
至此代码功能正常,点击右上角关闭按钮,则移除播放器元素。
然后问题在于,播放器可能有多个存在,这时候,如何实现点击每个关闭按钮,关闭对应的播放器呢?
尝试修改代码如下:1
2
3
4
5
6
7
8
9
10<template v-for="video in videoList">
<!-- 播放器组件,带关闭按钮 -->
<div class="video-box" :ref="`video${video.id}`" >
<Player
:src="video.src"
:poster="video.poster"
></Player>
<i class="el-icon-circle-close" @click="close(`video${video.id}`)"></i>
</div>
</template>
1 | methods: { |
然后点击关闭按钮时,可以看到控制台报错:
提示不能获取未定义的属性,则表明该 dom
元素节点获取不对。
# 发现问题所在
后面经过一番折腾,发现以上代码 this.$refs[videoStr]
获取的是一个 ref
等于 videoStr
(此处为变量)的 dom
节点数组,不是单个 dom
节点元素!
至此,踩坑这个之后,就明白了为什么获取不到对应的 dom
元素了。
代码修改如下即可:1
2
3
4
5
6
7
8methods: {
//关闭播放器
close(videoStr){
//应该用数据驱动dom,这里直接操作了dom,不符合vue的理念,暂时
let video = this.$refs[videoStr][0]
video.parentNode.removeChild(video)
}
}
功能实现后,再来拓展其他方法。
比如当 ref
的值一样,都为 videoBox
时:1
2
3
4
5
6
7
8
9<template v-for="video in videoList">
<div class="video-box" ref="videoBox" >
<Player
:src="video.src"
:poster="video.poster"
></Player>
<i class="el-icon-circle-close" @click="close(video.index)"></i>
</div>
</template>
1 | methods: { |
又或者不使用 ref
属性,改而为每个播放器元素赋值 id
,也可:1
2
3
4
5
6
7
8
9<template v-for="video in videoList">
<div class="video-box" :id="`video${video.id}`">
<Player
:src="video.src"
:poster="video.poster"
></Player>
<i class="el-icon-circle-close" @click="close(`video${video.id}`)"></i>
</div>
</template>
1 | methods: { |
官网概念:
总结:
ref 相当于给元素或组件赋予一个 ID 引用,用来注册引用信息的,方便获取 dom 元素或获取组件实例。
使用场景:1
2
31. ref 加在普通元素上,this.$refs.name 获取的是 dom 元素
2. ref 加在子组件上,this.$refs.name 获取到的是组件实例,方便父组件使用子组件的所有方法
3. 当 v-for 用于元素或组件,ref 获取的将是一组数组或 dom 节点
文章所说遇到的问题即是上述第三种情况。