为啥引入 `ResizeObserver` 来监听调整大小的变化,而不是更简单的 Element.prototype.addEventListener('resize', callback)

Posted

技术标签:

【中文标题】为啥引入 `ResizeObserver` 来监听调整大小的变化,而不是更简单的 Element.prototype.addEventListener(\'resize\', callback)【英文标题】:why was `ResizeObserver` introduced to listen to resize changes and not a simpler Element.prototype.addEventListener('resize', callback)为什么引入 `ResizeObserver` 来监听调整大小的变化,而不是更简单的 Element.prototype.addEventListener('resize', callback) 【发布时间】:2018-06-13 18:03:49 【问题描述】:

我有点惊讶,为了监听元素尺寸(不是窗口对象)的变化,我们必须使用一个名为ResizeObserver 的新接口。虽然它似乎做得很好;这似乎与仅添加侦听器即可使用的其他元素相关事件有所不同。

例如添加一个事件监听器来监听鼠标悬停事件

document.querySelector('#ele').addEventListener('mouseover', callback);

为什么不简单地为元素的 resize 事件添加一个新的侦听器?

document.querySelector('#ele').addEventListener('resize', callback);

是为了避免与窗口resize事件发生冲突吗?如果是这样,为什么不换个名字来称呼它

document.querySelector('#ele').addEventListener('elementResize', callback);

我知道创建一个帮助方法来简化ResizeObserver 的使用很容易。这样的东西可以像原来的 addEventListener 方法一样简单使用

export const getResizeObserver = ( ele, onResize ) => 
  let obs;

  const observerInterface = 
    stop: () =>  obs.unobserve( ele ); obs.disconnect() ,
  ;

  obs = new ResizeObserver( entries => 
    for ( const entry of entries ) 
      onResize && onResize( entry.contentRect );
    
   );

  obs.observe( ele );

  return observerInterface;
;

// usage to add the listener
const obs = getResizeObserver(document.querySelector('#ele'), callback);
// later to remove the listener
obs.stop();

在任何情况下,除了 api 偏好和多个元素可以共享观察者实例这一事实之外,还有什么理由使 ResizeObserver 方法比 addEventListener 方法更好?

【问题讨论】:

【参考方案1】:

有一个讨论 in this PR,其中 W3C 技术架构小组试图定义何时使用观察者模式而不是 EventTarget。此讨论的输出记录在here,我引用了第一条声明:

一般来说,使用 EventTarget 和通知事件,而不是 观察者模式,除非 EventTarget 不能很好地为你工作 功能。

使用观察者模式相对于EventTarget 的优势如下:

    可以在观察时或创建时自定义实例。 Observer 的构造函数或其observe() 方法可以采用允许作者自定义每个回调观察到的内容的选项。 addEventListener() 无法做到这一点。

    使用 Observer 对象上的 disconnect()unobserve() 方法很容易停止监听多个回调。

    您可以选择提供类似takeRecords() 的方法,该方法会立即获取相关数据,而不是等待事件触发。

    因为观察者是单一用途的,所以您不需要指定事件类型。

具体谈谈ResizeObserver在我看来

第 2 点和第 4 点不太相关。它没有添加或占用任何关键的东西,我认为resize 事件在这里会更好。

它仍然没有实现takeRecords()方法,所以3d点无关

它使我们能够在创建和观察时对其进行自定义,并允许稍后更改这些自定义 API,而无需更改 Event 模型。

另外,可能存在性能问题或an unavoidable recursion,但我没有找到任何关于它们的测量或解释。

【讨论】:

【参考方案2】:

这是proposed(问题仍然存在),但 Chrome 决定使用观察者模型来发布它,似乎主要是因为它比基于事件的实现快 10 倍 https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/z6ienONUb5A

现在为什么那是真的,我不知道。

【讨论】:

以上是关于为啥引入 `ResizeObserver` 来监听调整大小的变化,而不是更简单的 Element.prototype.addEventListener('resize', callback)的主要内容,如果未能解决你的问题,请参考以下文章

ResizeObserver API

如何使用反应测试库模拟 ResizeObserver 以在单元测试中工作

为啥bootstrap.css无法引入

ResizeObserver API 测试笑话

ResizeObserver:如何获取大小调整后的元素的ID?

用玩笑进行单元测试时,如何以角度模拟 ResizeObserver polyfill?