深入理解 Vue 中的 $refs:用法、原理与避坑指南

在 Vue 框架的日常开发中,$refs 是一个高频出现但又容易被误用的 API。它为开发者提供了直接操作 DOM 元素或组件实例的便捷方式,但如果使用不当,不仅会违背 Vue 的设计理念,还可能引发各种难以排查的问题。本文将从核心作用、使用场景、注意事项三个维度,带你全面掌握$refs的正确打开方式。

一、$refs 的核心作用

$refs 是 Vue 实例上的一个对象属性,其核心作用是提供对 DOM 元素或子组件实例的直接访问,打破 Vue “数据驱动视图” 的封装性,满足特定场景下的手动操作需求。

1. 访问 DOM 元素

在 Vue 的模板语法中,我们通常通过v-bindv-model等指令实现数据与视图的双向绑定,无需直接操作 DOM。但在某些场景下(如获取元素尺寸、手动聚焦输入框、调用 DOM 原生方法),直接操作 DOM 是最高效的方式,此时$refs就是最优选择。
示例代码:
vue
<template>
  <div>
    <!-- 给DOM元素添加ref属性 -->
    <input ref="inputBox" type="text" placeholder="请输入内容" />
    <button @click="focusInput">聚焦输入框</button>
  </div>
</template>

<script>
export default {
  methods: {
    focusInput() {
      // 通过this.$refs.xxx访问DOM元素,调用原生focus方法
      this.$refs.inputBox.focus();
    }
  }
}
</script>

2. 访问子组件实例

除了 DOM 元素,$refs还能直接获取子组件的实例,从而调用子组件的方法、访问子组件的 data 或 props(不推荐直接修改 props,违背单向数据流原则)。
示例代码:
vue
<!-- 子组件 Child.vue -->
<template>
  <div>{{ count }}</div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++;
    }
  }
}
</script>

<!-- 父组件 Parent.vue -->
<template>
  <div>
    <Child ref="childComp" />
    <button @click="callChildMethod">调用子组件方法</button>
  </div>
</template>

<script>
import Child from './Child.vue';
export default {
  components: { Child },
  methods: {
    callChildMethod() {
      // 访问子组件实例,调用其方法
      this.$refs.childComp.increment();
    }
  }
}
</script>

二、使用 $refs 必须注意的 8 个关键点

$refs虽然便捷,但使用时需严格遵守 Vue 的运行机制,否则极易出现 “refs 为空”“操作无效” 等问题。

1. $refs 不是响应式的

这是$refs最核心的特性,也是最容易踩坑的点。Vue 不会对$refs做响应式处理,当依赖的数据源变化导致 DOM 重新渲染时,$refs的更新不会触发组件的重新渲染,也无法通过 watch 监听$refs的变化。
错误示例(试图监听 refs 变化):
javascript
运行
// 无效代码!$refs非响应式,watch不会触发
watch: {
  '$refs.inputBox'(newVal) {
    console.log('输入框DOM变化', newVal);
  }
}

2. 初始化时机:DOM 渲染完成后才可用

$refs的赋值发生在 DOM 渲染完成之后,因此在created钩子中无法访问$refs(此时 DOM 尚未生成),只能在mounted(挂载完成)或后续生命周期中使用。
正确 / 错误对比:
javascript
运行
export default {
  created() {
    // 错误:此时DOM未渲染,$refs.inputBox为undefined
    console.log(this.$refs.inputBox); // undefined
  },
  mounted() {
    // 正确:mounted阶段DOM已挂载完成
    console.log(this.$refs.inputBox); // <input> DOM元素
  }
}

3. 动态渲染场景:nextTick 的使用

当通过条件渲染(v-if)、数据更新等方式触发 DOM 重新渲染时,$refs的更新会延迟到下一个 DOM 更新周期。此时需要使用this.$nextTick()等待 DOM 更新完成后再访问$refs
示例(v-if 切换后访问 refs):
vue
<template>
  <div>
    <input v-if="showInput" ref="dynamicInput" />
    <button @click="toggleInput">显示/隐藏输入框</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      showInput: false
    }
  },
  methods: {
    toggleInput() {
      this.showInput = !this.showInput;
      // 直接访问会获取到更新前的refs(可能为undefined)
      console.log(this.$refs.dynamicInput); // 第一次点击时为undefined
      
      // 正确做法:通过nextTick等待DOM更新
      this.$nextTick(() => {
        if (this.showInput) {
          this.$refs.dynamicInput.focus(); // 成功聚焦
        }
      });
    }
  }
}
</script>

4. 循环渲染中的 refs:返回数组而非单个元素

ref属性用在v-for循环中时,$refs.xxx会返回一个数组,数组元素为对应位置的 DOM 元素 / 组件实例,顺序与循环数据一致。
示例(v-for 中的 refs):
vue
<template>
  <div>
    <div v-for="(item, index) in list" :key="index" ref="itemBox">
      {{ item }}
    </div>
    <button @click="getRefsInLoop">获取循环refs</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: ['A', 'B', 'C']
    }
  },
  methods: {
    getRefsInLoop() {
      console.log(this.$refs.itemBox); // [div, div, div] 数组
      // 访问第二个元素
      console.log(this.$refs.itemBox[1].innerText); // B
    }
  }
}
</script>

5. 避免过度使用:优先遵循数据驱动

Vue 的核心设计理念是 “数据驱动视图”,$refs仅应作为 “兜底方案” 使用。以下场景应优先避免使用$refs
  • 替代v-model直接修改输入框 value:应通过绑定 data 实现;
  • 替代v-show/v-if控制元素显示隐藏:应通过修改数据驱动;
  • 替代 props/emit 实现父子组件通信:违背单向数据流原则。

6. 组件 refs:仅能访问非递归的子组件

如果子组件是递归组件(自身调用自身),$refs只能获取到最外层的组件实例;如果需要访问深层嵌套的子组件,建议通过provide/inject或事件总线实现,而非链式访问$refs(如this.$refs.parent.$refs.child,耦合性过高)。

7. 服务器端渲染 (SSR):慎用 $refs

在 SSR 场景下,服务端没有 DOM 环境,$refs会返回空值。如果代码中强依赖$refs,会导致服务端渲染失败,此时需通过process.client判断环境,仅在客户端执行 refs 相关操作。

8. 命名规范:避免与 Vue 内置属性冲突

$refs的命名不要使用 Vue 实例的内置属性名(如$el$data$options等),否则会覆盖内置属性,引发不可预知的问题。
错误示例:
vue
<!-- 错误:ref命名为$el,覆盖Vue内置的$el属性 -->
<div ref="$el">测试</div>

三、$refs 的适用场景总结

虽然$refs有诸多限制,但在以下场景中使用是合理且高效的:
  1. 操作 DOM 原生方法:如聚焦输入框、获取元素尺寸 / 位置、触发滚动等;
  2. 调用子组件的方法:如子组件封装了复杂的业务逻辑,需要父组件主动触发;
  3. 第三方库集成:如某些 jQuery 插件、图表库需要直接绑定 DOM 元素。

总结

  1. $refs的核心作用是直接访问 DOM 元素 / 子组件实例,是 Vue “数据驱动” 理念的补充,而非替代;
  2. 使用$refs的核心注意点:非响应式、DOM 渲染后才可用、动态场景需配合nextTick、循环中返回数组;
  3. 遵循 “最小使用原则”,优先通过数据驱动实现功能,仅在必要场景下使用$refs,避免代码耦合性过高。
掌握$refs的正确用法,既能发挥其便捷性,又能保持 Vue 代码的优雅与可维护性。希望本文能帮助你避开$refs的常见陷阱,写出更符合 Vue 设计理念的代码。

购买须知/免责声明
1.本文部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责。
2.若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。
3.如果本站有侵犯、不妥之处的资源,请在网站右边客服联系我们。将会第一时间解决!
4.本站所有内容均由互联网收集整理、网友上传,仅供大家参考、学习,不存在任何商业目的与商业用途。
5.本站提供的所有资源仅供参考学习使用,版权归原著所有,禁止下载本站资源参与商业和非法行为,请在24小时之内自行删除!
6.不保证任何源码框架的完整性。
7.侵权联系邮箱:aliyun6168@gail.com / aliyun666888@gail.com
8.若您最终确认购买,则视为您100%认同并接受以上所述全部内容。

会员源码网 建站教程 深入理解 Vue 中的 $refs:用法、原理与避坑指南 https://svipm.com/21173.html

相关文章

猜你喜欢