news 2026/4/14 19:37:52

35、Vue 中如何判断元素进入可视区?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
35、Vue 中如何判断元素进入可视区?

目录

一、先给一个面试里的标准回答

二、什么叫“进入可视区”?

三、方式一:getBoundingClientRect()

1. 原理

2. 最简单判断

3. Vue 3 示例

4. Vue 2 示例

四、这种方式的优缺点

优点

缺点

五、方式二:IntersectionObserver

1. 原理

2. Vue 3 示例

3. 参数解释

root

threshold

rootMargin

六、IntersectionObserver 的优缺点

优点

缺点

七、实际项目里怎么选?

1. 简单页面 / 兼容要求高

2. 图片懒加载 / 曝光埋点 / 长列表

3. 在 Vue 项目里封装成指令或 Hook

八、Vue 中如何封装成可复用能力?

方案1:封装成自定义 Hook(Vue 3)

方案2:封装成自定义指令

九、如果面试官问:怎么做图片懒加载?

十、如果面试官问:滚动监听方案怎么优化?

可以从这几个角度回答

1. 节流

2. requestAnimationFrame

3. 减少 DOM 查询

4. 批量处理

5. 优先使用 IntersectionObserver

十一、面试中怎么回答才算精彩?

高分回答模板

十二、一个更适合背诵的精简版

十三、顺手给你一个最常见判断公式

只判断纵向进入可视区

判断完整进入可视区

十四、一句话总结


这是一个很常见的前端 / Vue 面试题。
如果你只回答:

getBoundingClientRect()

不能算错,但不够完整
因为面试官通常想听的是:

  • 有哪些判断方式
  • 在 Vue 里怎么拿到元素
  • 如何处理滚动监听
  • 有没有更现代的方案
  • 哪种方案性能更好
  • 实际项目里怎么选

一、先给一个面试里的标准回答

在 Vue 中判断元素是否进入可视区,常见有两种方式:
一种是通过ref获取 DOM 元素,然后用getBoundingClientRect()判断元素的topbottom是否和视口区域有交集;
另一种是使用IntersectionObserver,让浏览器帮我们监听元素和可视区的相交情况。

如果是简单场景或者需要兼容老环境,我会用getBoundingClientRect() + scroll/resize
如果是图片懒加载、曝光埋点、长列表可见性判断这类场景,我更推荐IntersectionObserver,因为性能更好,也不需要手动频繁监听滚动事件。

在 Vue 里实现时,通常会通过ref拿元素,在mounted/onMounted中初始化,在组件卸载时移除监听或断开 observer。

这段已经算是比较完整的面试答案了。


二、什么叫“进入可视区”?

先把概念说清楚,这样回答会更严谨。

通常“进入可视区”指的是:

元素和当前视口区域发生了相交,用户有机会看到它。

这里又分几种业务定义:

  • 只要露出一点点就算进入
  • 必须整个元素都进入
  • 进入超过 50% 才算
  • 进入后停留一段时间才算曝光

所以面试时最好补一句:

是否进入可视区,本质上取决于业务定义。

这样会显得你不是在死背 API。


三、方式一:getBoundingClientRect()

这是最基础、最常见的方案。


1. 原理

getBoundingClientRect()可以拿到元素相对于当前视口的位置:

  • top
  • bottom
  • left
  • right
  • width
  • height

只要元素和视口范围有交集,就可以认为它进入可视区。


2. 最简单判断

const rect = el.getBoundingClientRect() const inView = rect.top < window.innerHeight && rect.bottom > 0

这个判断的意思是:

  • 元素顶部在视口底部上方
  • 元素底部在视口顶部下方

说明元素和视口纵向有交集。

如果还要判断横向:

const inView = rect.top < window.innerHeight && rect.bottom > 0 && rect.left < window.innerWidth && rect.right > 0

3. Vue 3 示例

<template> <div ref="boxRef" class="box">观察我是否进入可视区</div> </template> <script setup> import { ref, onMounted, onBeforeUnmount } from 'vue' const boxRef = ref(null) const checkInView = () => { const el = boxRef.value if (!el) return const rect = el.getBoundingClientRect() const inView = rect.top < window.innerHeight && rect.bottom > 0 console.log('是否进入可视区:', inView) } onMounted(() => { checkInView() window.addEventListener('scroll', checkInView) window.addEventListener('resize', checkInView) }) onBeforeUnmount(() => { window.removeEventListener('scroll', checkInView) window.removeEventListener('resize', checkInView) }) </script>

4. Vue 2 示例

<template> <div ref="box" class="box">观察我</div> </template> <script> export default { methods: { checkInView() { const rect = this.$refs.box.getBoundingClientRect() const inView = rect.top < window.innerHeight && rect.bottom > 0 console.log(inView) } }, mounted() { this.checkInView() window.addEventListener('scroll', this.checkInView) window.addEventListener('resize', this.checkInView) }, beforeDestroy() { window.removeEventListener('scroll', this.checkInView) window.removeEventListener('resize', this.checkInView) } } </script>

四、这种方式的优缺点


优点

  • 兼容性好
  • 实现简单
  • 理解成本低
  • 灵活,能自定义各种可见规则

缺点

  • 需要手动监听scroll/resize
  • 高频触发时可能影响性能
  • 页面元素很多时,逐个计算成本较高
  • 代码相对繁琐

五、方式二:IntersectionObserver

这是现代浏览器里更推荐的方案。


1. 原理

IntersectionObserver会监听:

目标元素和根容器(默认是视口)之间的相交情况

当元素进入或离开可视区时,浏览器会回调通知你。

它不需要你自己在scroll事件里不停计算,性能更好。


2. Vue 3 示例

<template> <div ref="targetRef" class="box">观察我是否进入可视区</div> </template> <script setup> import { ref, onMounted, onBeforeUnmount } from 'vue' const targetRef = ref(null) let observer = null onMounted(() => { observer = new IntersectionObserver((entries) => { entries.forEach(entry => { console.log('是否进入可视区:', entry.isIntersecting) console.log('可见比例:', entry.intersectionRatio) }) }, { root: null, // 默认视口 threshold: 0.1 // 可见 10% 就触发 }) if (targetRef.value) { observer.observe(targetRef.value) } }) onBeforeUnmount(() => { if (observer) { observer.disconnect() } }) </script>

3. 参数解释

root

观察的根容器:

  • null:表示浏览器视口
  • 某个滚动容器元素:表示相对于该容器判断可视性

threshold

阈值,表示可见比例达到多少时触发:

  • 0:露出一点就触发
  • 0.5:可见一半触发
  • 1:完全进入才触发

rootMargin

根容器的外扩或内缩边距:

rootMargin: '100px 0px'

表示可以提前 100px 触发,常用于图片预加载。


六、IntersectionObserver的优缺点


优点

  • 性能更好
  • 不需要手动频繁监听滚动
  • 语义清晰
  • 特别适合懒加载、曝光埋点、无限滚动

缺点

  • 老旧浏览器兼容性不如传统方案
  • 某些复杂业务场景需要额外处理
  • 需要理解阈值和根容器概念

七、实际项目里怎么选?

这个问题回答出来会很加分。


1. 简单页面 / 兼容要求高

用:

getBoundingClientRect() + scroll

例如:

  • 简单吸顶
  • 判断某个模块是否露出
  • 兼容老项目

2. 图片懒加载 / 曝光埋点 / 长列表

用:

IntersectionObserver

例如:

  • 图片进入视口才加载
  • 广告曝光统计
  • feed 流卡片曝光
  • 无限滚动加载更多

3. 在 Vue 项目里封装成指令或 Hook

这是更工程化的做法。


八、Vue 中如何封装成可复用能力?

这部分特别适合面试加分。


方案1:封装成自定义 Hook(Vue 3)

import { ref, onMounted, onBeforeUnmount } from 'vue' export function useInView(targetRef) { const isInView = ref(false) let observer = null onMounted(() => { observer = new IntersectionObserver(([entry]) => { isInView.value = entry.isIntersecting }) if (targetRef.value) { observer.observe(targetRef.value) } }) onBeforeUnmount(() => { observer && observer.disconnect() }) return { isInView } }

使用:

<template> <div ref="boxRef">内容</div> <p>{{ isInView ? '可见' : '不可见' }}</p> </template> <script setup> import { ref } from 'vue' import { useInView } from './useInView' const boxRef = ref(null) const { isInView } = useInView(boxRef) </script>

方案2:封装成自定义指令

export default { mounted(el, binding) { const observer = new IntersectionObserver(([entry]) => { if (binding.value) { binding.value(entry.isIntersecting, entry) } }) observer.observe(el) el._observer = observer }, unmounted(el) { el._observer && el._observer.disconnect() } }

使用:

<template> <div v-in-view="handleView">内容</div> </template> <script setup> const handleView = (visible, entry) => { console.log('是否可见:', visible) } </script>

九、如果面试官问:怎么做图片懒加载?

你可以顺带回答:

在 Vue 里做图片懒加载,我通常会用IntersectionObserver
先给图片一个占位图,监听图片元素是否进入可视区,当entry.isIntersectingtrue时,再把真实图片地址赋值给src,加载完成后取消监听。
这种方式比滚动事件 +getBoundingClientRect()更高效。


十、如果面试官问:滚动监听方案怎么优化?

这是追问高频点。


可以从这几个角度回答

1. 节流

滚动事件触发很频繁,可以节流:

function throttle(fn, delay) { let timer = null return function () { if (timer) return timer = setTimeout(() => { fn.apply(this, arguments) timer = null }, delay) } }

2.requestAnimationFrame

把高频滚动计算合并到一帧里执行。

3. 减少 DOM 查询

不要每次滚动都重新querySelector

4. 批量处理

多元素场景不要反复读写 DOM,尽量统一计算

5. 优先使用IntersectionObserver

更省性能


十一、面试中怎么回答才算精彩?

精彩的关键不是“说 API 多”,而是:

  • 先说主流方案
  • 再说适用场景
  • 再说性能和工程化

高分回答模板

在 Vue 中判断元素是否进入可视区,常见有两种做法。
第一种是通过ref获取 DOM 元素,在mounted/onMounted里使用getBoundingClientRect()获取元素相对于视口的位置,再结合window.innerHeightwindow.innerWidth判断元素是否和视口有交集。这种方式兼容性好,但通常需要配合scrollresize事件监听,高频场景下要注意节流。

第二种是使用IntersectionObserver,它可以让浏览器直接监听元素和可视区的相交状态,不需要手动在滚动事件里频繁计算,性能更好,也更适合图片懒加载、曝光埋点和长列表场景。

在实际 Vue 项目中,我通常会通过ref拿元素,在组件挂载时初始化监听,在组件卸载时移除监听或disconnectobserver。如果这个能力要复用,我会进一步封装成自定义指令或者 Composition API Hook。

所以如果是简单场景我会用getBoundingClientRect(),如果是生产项目里大量元素的可视区监听,我更倾向于IntersectionObserver

这段答出来,已经很不错了。


十二、一个更适合背诵的精简版

Vue 中判断元素进入可视区,通常有两种方式:
一种是ref + getBoundingClientRect(),通过判断元素的top/bottom是否和视口范围有交集;
另一种是IntersectionObserver,直接监听元素是否进入视口。

前者实现简单、兼容性好,但需要自己监听滚动事件;
后者性能更好,更适合懒加载和曝光埋点。
在 Vue 中我一般会在mounted/onMounted中初始化,在组件卸载时清理监听。


十三、顺手给你一个最常见判断公式


只判断纵向进入可视区

const rect = el.getBoundingClientRect() const inView = rect.top < window.innerHeight && rect.bottom > 0

判断完整进入可视区

const rect = el.getBoundingClientRect() const fullyInView = rect.top >= 0 && rect.left >= 0 && rect.bottom <= window.innerHeight && rect.right <= window.innerWidth

十四、一句话总结

Vue 中判断元素进入可视区,基础方案是ref + getBoundingClientRect(),更推荐的现代方案是IntersectionObserver

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/14 19:33:57

WeChatMsg:微信聊天记录的终极本地化保存与分析完整方案

WeChatMsg&#xff1a;微信聊天记录的终极本地化保存与分析完整方案 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeC…

作者头像 李华
网站建设 2026/4/14 19:31:48

过程决策程序图中的风险预案与应对策略

过程决策程序图&#xff08;PDPC&#xff09;是一种系统化的管理工具&#xff0c;用于识别潜在风险并制定应对策略&#xff0c;确保项目或流程的顺利推进。在复杂多变的商业环境中&#xff0c;风险无处不在&#xff0c;而PDPC通过结构化分析&#xff0c;帮助团队提前预见问题并…

作者头像 李华
网站建设 2026/4/14 19:31:42

如何5秒内将B站缓存视频转换为MP4格式:m4s-converter完整使用指南

如何5秒内将B站缓存视频转换为MP4格式&#xff1a;m4s-converter完整使用指南 【免费下载链接】m4s-converter 一个跨平台小工具&#xff0c;将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾经收藏了B…

作者头像 李华