当前位置: 首页 > article >正文

Vue 项目中使用$refs来访问组件实例或 DOM 元素,有哪些注意事项?

大白话Vue 项目中使用$refs来访问组件实例或 DOM 元素,有哪些注意事项?

在 Vue 项目里,$refs 是个超实用的工具,它能让你直接访问组件实例或者 DOM 元素。不过使用的时候,有一些地方可得注意,下面咱就详细唠唠。

1. $refs 只有在组件渲染完成后才可用

在 Vue 里,组件从创建到渲染完成是有个过程的。只有当组件完全渲染好了,$refs 才能正常使用。要是在组件还没渲染好的时候就想用 $refs 去访问东西,那肯定会出问题。所以,通常会把使用 $refs 的代码放到 mounted 钩子函数里,因为这个钩子函数是在组件渲染完成后才执行的。

export default {
  // 组件挂载完成后执行的钩子函数
  mounted() {
    // 这里可以安全地使用 $refs 访问组件实例或 DOM 元素
    this.$refs.myComponent.someMethod(); // 调用组件实例的方法
    this.$refs.myElement.focus(); // 让 DOM 元素获取焦点
  }
};

2. 不要在模板里直接使用 $refs

虽然 $refs 能让你访问组件实例或者 DOM 元素,但千万别在模板里直接用它。因为模板里的代码会在每次数据更新的时候重新计算,如果在模板里用 $refs,可能会导致意外的结果,而且还会影响性能。

<template>
  <!-- 不要这样做 -->
  <!-- <div>{{ $refs.myElement.textContent }}</div> -->
  <div ref="myElement">这是一个 DOM 元素</div>
</template>

<script>
export default {
  mounted() {
    // 在 mounted 钩子函数里使用 $refs
    const text = this.$refs.myElement.textContent;
    console.log(text); // 输出: 这是一个 DOM 元素
  }
};
</script>

3. $refs 不是响应式的

$refs 不像 Vue 的响应式数据那样,数据一变页面就跟着更新。$refs 只是一个普通的对象,它的属性值在组件渲染完成后就固定了。所以,如果你想在数据变化的时候更新 $refs 相关的操作,就得手动去处理。

<template>
  <div>
    <button @click="updateElement">更新元素</button>
    <div ref="myElement">{{ message }}</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: '初始消息'
    };
  },
  methods: {
    updateElement() {
      this.message = '更新后的消息';
      // 手动更新 $refs 相关的操作
      this.$refs.myElement.textContent = this.message;
    }
  }
};
</script>

4. 动态绑定 ref 时要注意

要是你需要动态绑定 ref,也就是根据不同的条件给不同的元素或者组件绑定 ref,那得小心了。因为动态绑定 ref 可能会导致 $refs 的值发生变化,所以在使用的时候要确保 $refs 里确实有你想要的元素或者组件。

<template>
  <div>
    <!-- 动态绑定 ref -->
    <component :is="currentComponent" :ref="currentRef"></component>
    <button @click="switchComponent">切换组件</button>
  </div>
</template>

<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

export default {
  data() {
    return {
      currentComponent: ComponentA,
      currentRef: 'componentRef',
      componentRef: null
    };
  },
  methods: {
    switchComponent() {
      this.currentComponent = this.currentComponent === ComponentA ? ComponentB : ComponentA;
      // 切换组件后,确保 $refs 里有正确的组件实例
      if (this.$refs.componentRef) {
        this.$refs.componentRef.someMethod();
      }
    }
  }
};
</script>

5. 在子组件销毁时清理 $refs

当子组件被销毁的时候,$refs 里对应的引用不会自动清除。所以,要是你在子组件销毁后还去访问 $refs 里的这个引用,就会报错。为了避免这种情况,你可以在子组件销毁的时候手动清理 $refs 里的引用。

<template>
  <div>
    <child-component ref="childRef"></child-component>
    <button @click="destroyChild">销毁子组件</button>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  methods: {
    destroyChild() {
      // 销毁子组件
      this.$refs.childRef.$destroy();
      // 手动清理 $refs 里的引用
      this.$refs.childRef = null;
    }
  }
};
</script>

总之,$refs 是个很强大的工具,但使用的时候得注意上面这些点,这样才能避免出现各种奇怪的问题,让你的 Vue 项目更加稳定。

除了$refs,Vue还有哪些可以访问组件实例或DOM元素的方式?

在 Vue 里,除了使用 $refs 访问组件实例或 DOM 元素外,还有以下几种方式:

1. 通过事件机制传递信息

在 Vue 中,你可以利用自定义事件和事件总线在组件间传递数据,从而间接访问组件实例。

自定义事件

子组件能够通过 $emit 触发自定义事件,把数据传递给父组件,父组件在接收到事件后就可以访问子组件实例的属性或方法。

<template>
  <!-- 父组件 -->
  <div>
    <child-component @custom-event="handleCustomEvent"></child-component>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  methods: {
    handleCustomEvent(childInstance) {
      // 访问子组件实例
      console.log(childInstance.someMethod());
    }
  }
};
</script>
<template>
  <!-- 子组件 -->
  <div>
    <button @click="sendInstance">发送实例</button>
  </div>
</template>

<script>
export default {
  methods: {
    sendInstance() {
      // 触发自定义事件,传递当前组件实例
      this.$emit('custom-event', this);
    },
    someMethod() {
      return '这是子组件的方法';
    }
  }
};
</script>
事件总线

事件总线是一个全局的事件中心,组件能够在上面触发和监听事件,以此实现组件间的通信。

// eventBus.js
import Vue from 'vue';
export const eventBus = new Vue();
<template>
  <!-- 发送组件 -->
  <div>
    <button @click="sendMessage">发送消息</button>
  </div>
</template>

<script>
import { eventBus } from './eventBus.js';

export default {
  methods: {
    sendMessage() {
      // 触发事件总线的事件
      eventBus.$emit('message-sent', this);
    }
  }
};
</script>
<template>
  <!-- 接收组件 -->
  <div></div>
</template>

<script>
import { eventBus } from './eventBus.js';

export default {
  mounted() {
    // 监听事件总线的事件
    eventBus.$on('message-sent', (senderInstance) => {
      console.log(senderInstance.someMethod());
    });
  }
};
</script>

2. 使用 provideinject

provideinject 主要用于实现跨级组件间的通信,父组件能够通过 provide 提供数据,子组件可以使用 inject 注入这些数据。

<template>
  <!-- 父组件 -->
  <div>
    <child-component></child-component>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  provide() {
    return {
      parentInstance: this
    };
  }
};
</script>
<template>
  <!-- 子组件 -->
  <div></div>
</template>

<script>
export default {
  inject: ['parentInstance'],
  mounted() {
    // 访问父组件实例
    console.log(this.parentInstance.someMethod());
  }
};
</script>

3. 使用 Vue.observable(Vue 2)或 reactive(Vue 3)

在 Vue 2 里可以使用 Vue.observable 创建一个响应式对象,在 Vue 3 中则使用 reactive。通过这个响应式对象存储组件实例,从而实现对组件实例的访问。

Vue 2
// store.js
import Vue from 'vue';

export const store = Vue.observable({
  componentInstance: null
});
<template>
  <!-- 组件 A -->
  <div>
    <button @click="setInstance">设置实例</button>
  </div>
</template>

<script>
import { store } from './store.js';

export default {
  methods: {
    setInstance() {
      // 将当前组件实例存储到响应式对象中
      store.componentInstance = this;
    }
  }
};
</script>
<template>
  <!-- 组件 B -->
  <div></div>
</template>

<script>
import { store } from './store.js';

export default {
  mounted() {
    // 访问存储的组件实例
    if (store.componentInstance) {
      console.log(store.componentInstance.someMethod());
    }
  }
};
</script>
Vue 3
// store.js
import { reactive } from 'vue';

export const store = reactive({
  componentInstance: null
});
<template>
  <!-- 组件 A -->
  <div>
    <button @click="setInstance">设置实例</button>
  </div>
</template>

<script setup>
import { store } from './store.js';

const setInstance = () => {
  // 将当前组件实例存储到响应式对象中
  store.componentInstance = getCurrentInstance();
};
</script>
<template>
  <!-- 组件 B -->
  <div></div>
</template>

<script setup>
import { store } from './store.js';

if (store.componentInstance) {
  console.log(store.componentInstance.someMethod());
}
</script>

这些方法都有各自的适用场景,你可以根据具体需求来选择合适的方式。

在Vue中,如何避免$refs访问带来的性能问题?

在 Vue 里使用 $refs 访问组件实例或 DOM 元素时,若使用不当可能会引发性能问题。以下是一些避免 $refs 访问带来性能问题的方法:

1. 避免在渲染循环中频繁访问 $refs

$refs 本身并非响应式的,但在渲染循环里频繁访问 $refs 会导致不必要的计算和 DOM 操作,从而降低性能。应该把 $refs 的访问逻辑移到事件处理函数或者生命周期钩子函数中。

<template>
  <div>
    <div ref="myElement">示例元素</div>
    <button @click="handleClick">点击</button>
  </div>
</template>

<script>
export default {
  methods: {
    handleClick() {
      // 在事件处理函数中访问 $refs
      const element = this.$refs.myElement;
      if (element) {
        // 对元素进行操作
        element.style.color = 'red';
      }
    }
  }
};
</script>

2. 仅在必要时使用 $refs

$refs 主要用于直接访问组件实例或 DOM 元素,不过很多时候可以借助 Vue 的响应式系统来实现相同的功能,从而避免使用 $refs

示例:动态改变样式

不使用 $refs 的情况:

<template>
  <div>
    <div :style="{ color: textColor }">示例元素</div>
    <button @click="changeColor">改变颜色</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      textColor: 'black'
    };
  },
  methods: {
    changeColor() {
      this.textColor = 'red';
    }
  }
};
</script>

3. 及时清理不再使用的 $refs

当组件被销毁时,$refs 里对应的引用不会自动清除。若不清理,可能会造成内存泄漏。所以在组件销毁时要手动清理 $refs

<template>
  <div>
    <child-component ref="childRef"></child-component>
    <button @click="destroyChild">销毁子组件</button>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  methods: {
    destroyChild() {
      // 销毁子组件
      this.$refs.childRef.$destroy();
      // 手动清理 $refs 里的引用
      this.$refs.childRef = null;
    }
  }
};
</script>

4. 避免在 watch 中频繁访问 $refs

watch 用于监听数据变化,若在 watch 里频繁访问 $refs,会导致不必要的性能开销。可以在 watch 中设置 immediate: false,避免初始化时就执行访问操作。

<template>
  <div>
    <div ref="myElement">示例元素</div>
    <input v-model="inputValue" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      inputValue: ''
    };
  },
  watch: {
    inputValue: {
      handler(newValue) {
        if (this.$refs.myElement) {
          // 对元素进行操作
          this.$refs.myElement.textContent = newValue;
        }
      },
      immediate: false // 避免初始化时执行
    }
  }
};
</script>

5. 利用缓存机制

要是需要多次访问 $refs,可以把访问结果缓存起来,避免重复访问。

<template>
  <div>
    <div ref="myElement">示例元素</div>
    <button @click="doSomething">执行操作</button>
    <button @click="doAnotherThing">执行另一个操作</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      cachedElement: null
    };
  },
  methods: {
    getElement() {
      if (!this.cachedElement) {
        this.cachedElement = this.$refs.myElement;
      }
      return this.cachedElement;
    },
    doSomething() {
      const element = this.getElement();
      if (element) {
        // 对元素进行操作
        element.style.fontSize = '20px';
      }
    },
    doAnotherThing() {
      const element = this.getElement();
      if (element) {
        // 对元素进行另一个操作
        element.style.backgroundColor = 'yellow';
      }
    }
  }
};
</script>

通过以上这些方法,可以有效避免 $refs 访问带来的性能问题,提升 Vue 应用的性能和稳定性。


http://www.kler.cn/a/613189.html

相关文章:

  • 最接近的三数之和
  • CORDIC算法:三角函数的硬件加速革命——从数学原理到FPGA实现的超高效计算方案
  • 2.1-WAF\CDN\OSS\反向代理\负载均衡
  • 自动驾驶中基于潜在世界模型学习多概率决策(LatentDriver)
  • 《2核2G阿里云神操作!Ubuntu+Ollama低成本部署Deepseek模型实战》
  • 51c嵌入式~三极管~合集1
  • 剑指Offer49 -- DP_贪心
  • Meatachat:演示版(AI组件库、Chat、智能对话、移动适配)(附源码)
  • mybatis里in关键字拼接id问题
  • Jmeter-负载测试
  • 算法基础——模拟
  • 如何选择适合的实验室铸铁地板和铸铁试验平板?北重专业帮助指南
  • OpenHarmony子系统开发 - 显示管理
  • 软件工程面试题(七)
  • 爬虫的第三天——爬动态网页
  • ruoyi-vue部署 linux 系统项目安装部署 oa 项目部署 (合集)
  • docker torcherve打包mar包并部署模型
  • Java 代理(一) 静态代理
  • 第二届计算机网络和云计算国际会议(CNCC 2025)
  • Reactor/Epoll为什么可以高性能?