blog-frontend
blog-frontend copied to clipboard
ResizeObserver
1. 出处
element-ui src/utils/resize-event.js
1.1 完整代码
import ResizeObserver from 'resize-observer-polyfill';
const isServer = typeof window === 'undefined';
/* istanbul ignore next */
const resizeHandler = function(entries) {
for (let entry of entries) {
const listeners = entry.target.__resizeListeners__ || [];
if (listeners.length) {
listeners.forEach(fn => {
fn();
});
}
}
};
/* istanbul ignore next */
export const addResizeListener = function(element, fn) {
if (isServer) return;
if (!element.__resizeListeners__) {
element.__resizeListeners__ = [];
element.__ro__ = new ResizeObserver(resizeHandler);
element.__ro__.observe(element);
}
element.__resizeListeners__.push(fn);
};
/* istanbul ignore next */
export const removeResizeListener = function(element, fn) {
if (!element || !element.__resizeListeners__) return;
element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
if (!element.__resizeListeners__.length) {
element.__ro__.disconnect();
}
};
2. ResizeObserver API
2.1 个人理解
开发过程当中经常遇到的一个问题就是如何监听一个div的尺寸变化?, 常用的做法是监听window.resize事件
这样做有如下缺陷:
-
window.resize
事件触发频率高, 这很容易导致性能问题 -
window.resize
事件获得每个视窗大小的变化, 不仅仅是某一个元素变化, 这通常是浪费的 -
只有
window
才有resize
事件 -
window.resize
事件不能帮助我们观察到元素地添加或删除
而 ResizeObserver
正好完美的解决了上面的问题, 个人认为它可以完美替换window.resize
它对所观察到的元素的大小的变化做出反应, 具体行为如下
-
当观察到的元素被插入或从
DOM
中删除时, 观察将会触发 -
当观察到的元素
display
为none
时, 观察将会触发 -
观察不会由
css
的transform
触发 -
如果元素有显示,而且元素大小不是0,0,观察将会触发
-
观察不会对未替换的内联元素(non-replaced inline element)触发
2.2 API
实例化
const myObserver = new ResizeObserver(entries => {
})
用于观察多个元素
myObserver.observe(elementA)
myObserver.observe(elementB)
取消对某个元素的观察
myObserver.unobserve(elementA)
取消对所有元素的观察
myObserver.disconnect()
通过观察, 可以获得一个ResizeObserverEntry
对象数组, 该对象有如下属性
{
target: 触发resize的元素的引用
contentRect: { x, y, width, height, top, left, bottom, left }
}
与getBoundingClientRect
不同, contentRect
的行为非常怪异, 虽然一般我只需要
width
以及height
, 行为如下
width = content-box width
height = content-box height
x = left = padding-left
y = top = padding-top
right = width + padding-left
bottom = height + padding-top
3. 应用: 以Vue为例
3.1 假设我需要实时维护一个innerWidth
的值
使用window.resize
export default {
data: () => ({
innerWidth: '',
}),
methods: {
handleResize() {
this.innerWidth = window.innerWidth
},
},
mounted() {
this.innerWidth = window.innerWidth
window.addEventListener('resize', this.handleResize)
},
beforeDestory() {
window.removeEventListener('resize', this.handleResize)
},
}
使用ResizeObserver
export default {
data: () => ({
resizeObserver: null,
innerWidth: '',
}),
mounted() {
this.resizeObserver = new ResizeObserver(entries => {
this.innerWidth = entries[0].contentRect.width
})
this.resizeObserver.observe(this.$el)
},
beforeDestory() {
this.resizeObserver.disconnect()
},
}
差别不是特别大, 因为其当观察到的元素被插入或从DOM
中删除时, 观察将会触发的特点, 可以少掉赋初始值的一步
3.2 v-resize指令
import ResizeObserver from 'resize-observer-polyfill'
function inserted(el, binding) {
if (typeof binding.value !== 'function') return
const ResizeObserver = new ResizeObserver(binding.value)
ResizeObserver.observe(el)
el._ro = ResizeObserver
}
function unbind(el) {
if (!el._ro) return
el._ro.disconnect()
delete el._ro
}
export default {
inserted,
unbind,
}