Code前端首页关于Code前端联系我们

Vue2里用ref获取DOM到底咋操作?

terry 2天前 阅读数 15 #Vue
文章标签 Vue2;ref获取DOM

在Vue开发里,咱们大多时候靠数据驱动页面,但偶尔也得直接操作DOM,比如搞个动画、集成第三方库(像ECharts),这时候Vue给的ref就特实用,但不少刚学的同学会犯懵:ref咋用?啥时候能用?碰到undefined咋解决?今天就用问答形式把Vue2里ref操作DOM的事儿掰碎了讲明白~

Vue2里的ref是干啥的?

可以理解成给DOM元素或组件“贴个标签”,方便后续找到它们,要是给普通DOM元素加ref,之后能拿到真实的DOM节点;要是给子组件加ref,能拿到子组件的实例(里面有子组件的data、methods这些),打个比方,就像你给家里每个房间贴个门牌号,想找哪个房间,看门牌就知道在哪~

咋用ref获取单个DOM元素?

分两步走:
第一步:在模板里给元素加ref属性,取个名字,比如想抓个div的DOM,就写<div ref="myDiv">我是目标div</div>
第二步:在JS里通过this.$refs.名字去拿,但得注意时机——DOM得先渲染出来才能拿到,Vue的mounted生命周期钩子是DOM渲染完触发的,所以在mounted里或者之后的方法里用最稳,举个实际例子:

<template>
  <div>
    <div ref="myDiv">测试div</div>
    <button @click="getDivInfo">获取div信息</button>
  </div>
</template>
<script>
export default {
  methods: {
    getDivInfo() {
      const divDom = this.$refs.myDiv;
      console.log(divDom.offsetWidth); // 打印div的宽度
    }
  },
  mounted() {
    const divDom = this.$refs.myDiv;
    console.log(divDom.innerHTML); // 这里也能拿到,因为mounted时DOM已渲染
  }
};
</script>

列表循环里的多个DOM咋用ref拿?

要是用v-for循环渲染列表,给每个列表项加ref,this.$refs.名字拿到的会是数组,因为循环生成多个元素,ref会把这些元素收集成数组存起来,举个例子:

<template>
  <ul>
    <li v-for="(item, index) in list" :key="index" ref="listItems">
      {{ item.name }}
    </li>
  </ul>
</template>
<script>
export default {
  data() {
    return {
      list: [
        { name: "苹果" },
        { name: "香蕉" },
        { name: "橙子" }
      ]
    };
  },
  mounted() {
    const items = this.$refs.listItems;
    console.log(items); // 这里是个数组,长度和list一样
    items.forEach((li, idx) => {
      console.log(li.innerText); // 依次打印“苹果”“香蕉”“橙子”
    });
  }
};
</script>

ref和原生js获取DOM(比如getElementById)有啥不一样?

最核心的区别是作用域和安全性,原生方法(像document.getElementById)是全局找元素,要是多个组件里有相同id,就会冲突;但ref是“组件级”的,只在当前组件里生效,不会干扰其他组件,而且Vue是数据驱动,ref配合生命周期,能确保在DOM渲染完再操作,避免拿空值,举个反面例子:要是用document.getElementById,在created钩子(此时DOM还没生成)里调用,肯定拿不到元素;但ref在mounted里用就很稳~

为啥有时候this.$refs.xxx是undefined?

碰到这情况,大概率是这几个原因:

  • 时机不对:DOM还没渲染就去拿,比如在created钩子用ref,此时模板还没变成真实DOM,自然拿不到,解决办法:把代码移到mounted或者触发DOM更新后的方法里(比如点击事件)。
  • 元素被v-if控制:如果元素用v-if绑定,当条件为false时,元素根本没渲染到页面上,$refs里自然没有,这时候可以换成v-showv-show是隐藏元素,DOM还在),或者确保访问时v-if的条件为true
  • 拼写错误:ref属性写的是ref="myRef",但代码里写成this.$refs.myref(大小写错了),也会拿不到,得检查拼写是否一致~

给组件加ref能拿到DOM吗?

分两种情况:

  • 原生HTML标签(如div、button):ref直接拿到DOM节点,和普通元素一样。
  • 自定义子组件:默认拿到的是子组件的实例(能访问子组件的data、methods),不是DOM,要是想拿子组件的根DOM元素,得用this.$refs.组件名.$el,举个例子:

子组件MyComponent.vue

<template>
  <div class="child-div">我是子组件</div>
</template>

父组件里用ref:

<template>
  <MyComponent ref="myComp"></MyComponent>
</template>
<script>
import MyComponent from "./MyComponent.vue";
export default {
  components: { MyComponent },
  mounted() {
    const compInstance = this.$refs.myComp; // 子组件实例
    console.log(compInstance.$el); // 子组件的根DOM(即.child-div对应的div)
  }
};
</script>

实际项目里哪些场景必须用ref操作DOM?

这些场景离了ref还真不行:

  • 集成第三方库:比如ECharts需要给一个DOM容器初始化图表,得用ref拿到容器元素,再传入echarts.init(container)
  • 自定义动画/滚动:比如做一个Tab切换动画,需要获取元素的位置、高度,计算动画参数,这时候得用ref拿DOM算尺寸。
  • 表单焦点管理:页面加载后让输入框自动聚焦,this.$refs.inputRef.focus()就能实现,比原生方法更灵活。
  • Canvas绘图:Canvas需要一个DOM容器来渲染,用ref拿到容器,再初始化Canvas上下文(canvas.getContext('2d'))。

Vue2和Vue3的ref有啥不一样?

Vue3里的ref成了组合式API的核心(用const xxx = ref()声明响应式数据),但操作DOM的逻辑和Vue2有重叠也有变化:Vue3中给元素加ref,需要配合setup语法里的ref()函数来绑定(比如const myDiv = ref(null); <div ref="myDiv"></div>),但Vue2是选项式API,靠this.$refs访问,更偏向“标签式”引用,不过咱们现在聚焦Vue2,了解这些区别能帮你以后学Vue3少踩坑~

说到底,ref是Vue里操作DOM的“捷径”,但得注意用法和时机,只要搞清楚“什么时候拿、怎么拿、拿不到咋排查”,就能把ref用得溜,既满足DOM操作需求,又不破坏Vue数据驱动的优势~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门