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-show
(v-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前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。