Skip to content

关于 ref 与 $refs 对 dom 元素的操作 #5

@jwchan1996

Description

@jwchan1996

关于 ref 与 $refs 对 dom 元素的操作

如何获取 v-for 渲染的多个 ref 的 dom

🍍 在编写 vue 项目过程中,遇到了获取不到正确的 dom 元素节点的问题。

功能界面如图所示:

界面

需要实现的是,点击每个播放器的右上角的关闭按钮,则关闭播放器。
代码如下:

<template>
  <div class="device-status">
    <!-- 树组件 -->
    <Tree></Tree>
    <!-- 业务内容 -->
    <div class="content-container" ref="container">
      <h1>{{ msg }}</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>

至此代码功能正常,点击右上角关闭按钮,则移除播放器元素。

然后问题在于,播放器可能有多个存在,这时候,如何实现点击每个关闭按钮,关闭对应的播放器呢?

尝试修改代码如下:

<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>
methods: {
  //关闭播放器
  close(videoStr){
    //应该用数据驱动dom,这里直接操作了dom,不符合vue的理念,暂时
    let video = this.$refs[videoStr]
    video.parentNode.removeChild(video)
  }
}

然后点击关闭按钮时,可以看到控制台报错:

报错

提示不能获取未定义的属性,则表明该 dom 元素节点获取不对。

发现问题所在

后面经过一番折腾,发现以上代码 this.$refs[videoStr] 获取的是一个 ref 等于 videoStr (此处为变量)的 dom 节点数组,不是单个 dom 节点元素!

数组节点

至此,踩坑这个之后,就明白了为什么获取不到对应的 dom 元素了。

代码修改如下即可:

methods: {
  //关闭播放器
  close(videoStr){
    //应该用数据驱动dom,这里直接操作了dom,不符合vue的理念,暂时
    let video = this.$refs[videoStr][0]
    video.parentNode.removeChild(video)
  }
}

功能实现后,再来拓展其他方法。

比如当 ref 的值一样,都为 videoBox 时:

<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>
methods: {
  //关闭播放器
  close(index){
    // 利用数组下标操作
    let video = this.$refs.videoBox[index]
    video.parentNode.removeChild(video)
  }
}

又或者不使用 ref 属性,改而为每个播放器元素赋值 id,也可:

<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>
methods: {
  //关闭播放器
  close(videoId){
    // 利用数组下标操作
    let video = document.querySelector(`#${videoId}`)
    video.parentNode.removeChild(video)
  }
}

官网概念:

官网概念

总结:

ref 相当于给元素或组件赋予一个 ID 引用,用来注册引用信息的,方便获取 dom 元素或获取组件实例。

使用场景:

1. ref 加在普通元素上,this.$refs.name 获取的是 dom 元素
2. ref 加在子组件上,this.$refs.name 获取到的是组件实例,方便父组件使用子组件的所有方法
3. 当 v-for 用于元素或组件,ref 获取的将是一组数组或 dom 节点

文章所说遇到的问题即是上述第三种情况。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions