前言

echarts-for-react在对echarts进行轻量级封装的基础上,额外提供图表尺寸自适应容器尺寸的这小而实用的功能,而这功能的背后就是本文想介绍的size-sensor了。

源码介绍

size-sensor源码十分精简,主要是对原生API​​ResizeObserver​​​方案和​​object​​元素方案进行检测和API统一化而已。

代码首先会检测当前运行时是否支持原生API​​ResizeObserver​​​,若不支持则使用​​object​​元素方案。下面我们将对两种方案进行探讨。

基于浏览器原生API - ​​ResizeObserver​​实现

用于监听Element内容盒子或边框盒子或者SVGElement边界尺寸的大小,并调用回调函数。
MDN: ​​​https://developer.mozilla.org/zh-CN/docs/Web/API/ResizeObserver​

/**
* @param {ResizeObserverEntry} entries - 用于获取每个元素改变后的新尺寸
* @param {ResizeObserver} observer
* @see https://developer.mozilla.org/zh-CN/docs/Web/API/ResizeObserverEntry
*/
function handleResize(entries, observer) {
for (let entry of entries) {
//......
}
}
const target = document.getElementById('main')

const observer = new ResizeObserver(handleResize)

// 开始对指定DOM元素的监听
observer.observe(target)

// 结束对指定DOM元素的监听
observer.unobserve(target)

// 结束对所有DOM元素的监听
observer.disconnect()

注意:在​​handleResize​​​中修改​​target​​​的尺寸并不会导致递归调用​​handleResize​​函数。

基于​​object​​元素的兼容方案实现

​object​​​元素用于内嵌图像、音频、视频、Java applets、ActiveX、PDF和Flash等外部资源,因此其也会像​​iframe​​​元素那样生成独立的browser context。
而browser context中​​​Window​​​实例的尺寸会保持和​​object​​​元素的一致,因此可以通过订阅browser context中​​Window​​​实例的​​resize​​事件实现对容器的尺寸的监听。

function bind(target, handle) {
if (getComputedStyle(target).position === 'static') {
target.style.position = 'relative'
}

let object = document.createElement('object')
object.onload = () => {
object.contentDocument.defaultView.addEventListener('resize', handle)
// 初始化时先触发一次
handle()
}
object.style.display = 'block'
object.style.position = 'absolute'
object.style.top = 0
object.style.let = 0
object.style.width = '100%'
object.style.height = '100%'
object.style.pointerEvents = 'none'
object.style.zIndex = -1
object.style.opacity = 0
object.type = 'text/html'

target.appendChild(object)
object.data = 'about:data'

return () => {
if (object.contentDocument) {
object.contentDocument.defaultView.removeEventListener('resize', handle)
}
if (object.parentNode) {
object.parentNode.removeChild(object)
}
}
}

这里将object元素替换为iframe元素也是可以的,只需将​​object.data​​​换成​​iframe.src​​​即可。
注意:在​​​handle​​​中修改​​target​​​的尺寸并会导致递归调用​​handle​​函数。

​ResizeObserver​​​的polyfill兼容方案 - ​​MutationObserver​

Repos: ​​https://github.com/que-etc/resize-observer-polyfill​​​

Repos: ​​https://github.com/juggle/resize-observer​

作者:肥仔John

欢迎添加我的公众号一起深入探讨技术手艺人的那些事!

React魔法堂:size-sensor源码略读_初始化