来源于resize-observer-polyfill

1.浏览器resizeObserver兼容性存在问题

如果没有原生的ResizeObserver方法 将使用垫片提供的方法

  1. export default (() => {
  2. // Export existing implementation if available.
  3. // 启用原生api
  4. if (typeof global.ResizeObserver !== 'undefined') {
  5. return global.ResizeObserver;
  6. }
  7. // 启用垫片api
  8. return ResizeObserverPolyfill;
  9. })();

2.监听的属性变化和刷新

在树形变化后调用响应的对象 和mobx,formily类似的一套实现

  1. // 会影响size的有 onResize只在window生效
  2. window.addEventListener('resize', this.refresh);
  3. // 普通元素怎么处理 拖拽完成形变的时候触发trannsitionend事件
  4. document.addEventListener('transitionend', this.onTransitionEnd_);
  5. // transition的形变可能影响到的属性
  6. const transitionKeys = ['top', 'right', 'bottom', 'left', 'width', 'height', 'size', 'weight'];
  7. // 记录和计算前后属性变化 如果变化 触发sizeChange通知
  8. function getHTMLElementContentRect(target) {
  9. const {clientWidth, clientHeight} = target;
  10. if (!clientWidth && !clientHeight) {
  11. return emptyRect;
  12. }
  13. const styles = getWindowOf(target).getComputedStyle(target);
  14. const paddings = getPaddings(styles);
  15. const horizPad = paddings.left + paddings.right;
  16. const vertPad = paddings.top + paddings.bottom;
  17. let width = toFloat(styles.width),
  18. height = toFloat(styles.height);
  19. // model is applied (except for IE).
  20. if (styles.boxSizing === 'border-box') {
  21. if (Math.round(width + horizPad) !== clientWidth) {
  22. width -= getBordersSize(styles, 'left', 'right') + horizPad;
  23. }
  24. if (Math.round(height + vertPad) !== clientHeight) {
  25. height -= getBordersSize(styles, 'top', 'bottom') + vertPad;
  26. }
  27. }
  28. if (!isDocumentElement(target)) {
  29. const vertScrollbar = Math.round(width + horizPad) - clientWidth;
  30. const horizScrollbar = Math.round(height + vertPad) - clientHeight;
  31. if (Math.abs(vertScrollbar) !== 1) {
  32. width -= vertScrollbar;
  33. }
  34. if (Math.abs(horizScrollbar) !== 1) {
  35. height -= horizScrollbar;
  36. }
  37. }
  38. return createRectInit(paddings.left, paddings.top, width, height);
  39. }

3.react-component层面组装

  1. // 用useRef不变特性保存各阶段size数据
  2. const sizeRef = React.useRef({
  3. width: -1,
  4. height: -1,
  5. offsetWidth: -1,
  6. offsetHeight: -1,
  7. });
  8. <SingleObserver {...props} key={key}>
  9. {child}
  10. </SingleObserver>
  11. // 第一顺位获取children的ref做被监听对象的dom
  12. const elementRef = React.useRef<Element>(null);
  13. ...
  14. const mergedChildren = isRenderProps ? children(elementRef) : children;
  15. // 备选通过当前reactNode的挂载找到上层dom
  16. ReactDom.findDOMNode(node)
  17. const currentElement: HTMLElement =
  18. findDOMNode(elementRef.current) || findDOMNode(wrapperRef.current);
  19. // 通过两层dom处理的意思是 原始组件最好自带一个wapper包裹要onResize处理的部分
  20. // 否则往顶层冒泡拿一个wapper 不一定符合
  21. // 启用监听
  22. React.useEffect(() => {
  23. const currentElement: HTMLElement =
  24. findDOMNode(elementRef.current) || findDOMNode(wrapperRef.current);
  25. if (currentElement && !disabled) {
  26. // 开启监听 回调为onInternalResize
  27. observe(currentElement, onInternalResize);
  28. }
  29. return () => unobserve(currentElement, onInternalResize);
  30. }, [elementRef.current, disabled]);
  31. // onInternalResize
  32. onCollectionResize?.(sizeInfo, target, data);
  33. // defer the callback but not defer to next frame
  34. // 顺延一个frame刷新时间处理 微任务 frame开启后会查询
  35. Promise.resolve().then(() => {
  36. onResize(sizeInfo, target);
  37. });