Intersection Observer 笔记

最近用 Intersection Observer API 解决了一个小需求,记录一下用法。

有时候我们需要根据一个元素的位置来修改它的属性,比如图片的 lazyload,比如视频离开视窗之后停止播放。

以前的做法通常是:

  1. 侦听 window.scroll 事件
  2. scroll 触发,遍历每个要检查的 DOM Element,执行 .getBoundingClientRect() 取出它的 widthheighttopleft,然后根据 viewport 和宽高 scrollTop scrollLeft 计算对象是否应该出现
  3. 然后做处理

这样做会产生一些问题:

  1. 如果漏掉移除侦听器,可能造成内存泄漏
  2. 不同组件之间,很难共享侦听器
  3. 每一次都计算所有 Element,成本很高

于是现在我们有了 Intersection Observer,专门用来观察一个 Element 是否出现在 viewport 或者父容器里。它可以很好的解决这些问题:

  1. API 更清晰
  2. 逻辑原生,速度更快,消耗更少
  3. 可以自定义阈值,更加可控

关于 Intersection Observer 的详细知识,建议大家认真阅读 MDN – Intersection Observer,我就不抄文档了。

使用 Intersection Observer 大体上分为三步:

  1. 声明一个 Intersection Observer 实例,这一步最关键的是要确定显隐依据哪个元素,也就是 root
  2. 将需要检查的元素加入侦听队列
  3. 在回调函数里处理元素状态

写成代码大概是这样的:

// 声明一个实例
// 因为我的视口即当前 viewport,所以这里不需要 `options`
const observer = new IntersectionObserver(entries => {
  // 遍历所有实例,如果它显示出来,即 intersectionRatio 显示比例大于 0
  // 那么就让它 `dispatch('visible')`
  entries.forEach(({target, intersectionRatio}) => {
    const event = new CustomEvent('visible', {
      detail: {
        isVisible: intersectionRatio > 0,
      },
    });
    target.dispatchEvent(event);
  });
});

// 然后可以在 Vue 里侦听这个事件
export default {
  template: '<div @visible="onVisible"></div>';
}

Intersection Observer API 公布一段时间之后,又进行了升级,现在支持更丰富的参数,比如我们可以定义一个函数去判断更复杂情况下的显示状态。不过大部分场景下,我们并不需要很精准的判断,所以我觉得这个只是保障选择的机会。


参考阅读:

Proposed Updates for Intersection Observer

Chat idea:记一次 Firefox 下 Vue 带来的性能危机的解决

前两天遇到一个问题:我厂的一个产品在 Firefox 下,可能发生因为 CPU 占用过高而卡死的情况。这个问题在测试环境不复现,在 Chrome下基本上也不会复现。

因为我厂老板是这个产品的主要用户,这个 bug 让我倍感压力,但一直没有解决它的好办法。终于有一天,在某台生产机上调试另外一个 bug 的时候,我终于发现了稳定复现这个 bug 的方式。

接下来就是几个小时的 debug,然后发现问题所在,然后解决问题,然后发现解决方案不理想,于是寻求新的解决方案,然后找到新的 API,最终彻底的解决这个问题。

接下来我就分享这个过程,读完整篇文章当中你将学会:

  1. 使用开发者工具查找性能问题
  2. 不断切分,缩小问题范围
  3. 理解 Vue 响应式原理分析问题根源
  4. 修复问题并验证
  5. 新的解决方案 Intersection Observer
  6. 解决问题并上线

目标读者:

  1. 中级开发者
  2. 熟悉原生 JS

大家觉得这个 idea 如何?请留言告诉我。不出意外的话这将是我下个月的 gitchat 内容。