resize-observer-polyfill 源码解读之 ResizeObserverSPI

ResizeObserverSPI 类定义

resize-observer-polyfill/src/ResizeObserverSPI.js

ResizeObserverSPI, 这个是真正提供观察某个元素功能的地方,SPI 应该是 service provider interface的简称。它是 ResizeObserver 那三个接口真正提供功能的地方。它提供了如下的方法

  • observe(htmlelement), 开始观察某个 html 元素,该元素以 ResizeObservation 的形态保存于内部的一个Map里面。
  • unobserve(htmlelement), 停止观察某个 html 元素
  • disconnect() 停止观察所有的 html 元素。
  • gatherActive() 轮询内部的 ResizeObservation Map,调用它的 isActive() 方法,发现更新了元素,存放于内部的 activeObservations 数组中
  • boradcastActive() 轮询内部的 ResizeObservation Map,调用它的 broadcastRect(),更新最新值。同时调用 callback 通知观察者更新。
  • hasActive() 返回当前是否存在更新的元素。
  • clearActive() 清除更新的元素数组。activeObservations
 class ResizeObserverSPI {
  activeObservations_ = []; //存在更新的被观察目标元素
  callback_;
  callbackCtx_;//传给回调函数用的this对象
  controller_;
  observations_ = new Map(); //注册ResizeObservation instances的集合

  constructor(callback, controller, callbackCtx) {
    this.callback_ = callback;
    this.controller_ = controller;
    this.callbackCtx_ = callbackCtx;
  }

  /**
   * Starts observing provided element.
   */
  observe(target) {
    // 校验出错...

    const observations = this.observations_;

    // Do nothing if element is already being observed.

    if (observations.has(target)) {
      return;
    }
    // observations_ 设置存储ResizeObservation创建的实例
    observations.set(target, new ResizeObservation(target));

    // 触发调度器的addObserver
    this.controller_.addObserver(this);

    // Force the update of observations.
    this.controller_.refresh();
  }

  /**
   * Stops observing provided element.

   */
  unobserve(target) {
    const observations = this.observations_;

    // Do nothing if element is not being observed.
    if (!observations.has(target)) {
      return;
    }

    // 移除监测的目标元素
    observations.delete(target);

    // 当没有监测的元素时,执行调度器controller_的removeObserver函数
    if (!observations.size) {
      this.controller_.removeObserver(this);
    }
  }

  /**
   * Stops observing all elements. | 停止所有元素的观察
   */
  disconnect() {
    // 清空activeObservations_
    this.clearActive();
    // 移除observations_中的所有元素  map.clear()
    this.observations_.clear();
    // 执行调度器controller_的removeObserver函数
    this.controller_.removeObserver(this);
  }

  /**
   * Collects observation instances the associated element of which has changed
   * it's content rectangle.
   *
   * @returns {void}
   */
  gatherActive() {
    // 清空activeObservations_
    this.clearActive();

    this.observations_.forEach((observation) => {
      // 判断被观察的target元素是否存在变化
      if (observation.isActive()) {
        this.activeObservations_.push(observation);
      }
    });
  }

  /**
   * Invokes initial callback function with a list of ResizeObserverEntry
   * instances collected from active resize observations.
   *
   * @returns {void}
   */
  broadcastActive() {
    // Do nothing if observer doesn't have active observations.
    if (!this.hasActive()) {
      return;
    }

    const ctx = this.callbackCtx_;

    // Create ResizeObserverEntry instance for every active observation.
    const entries = this.activeObservations_.map((observation) => {
      // 返回被觀察element最新的大小
      return new ResizeObserverEntry(
        observation.target,
        // 執行observation.broadcastRect函數獲取最新的大小
        observation.broadcastRect()
      );
    });

    // 改变回调函数的this指向ctx
    this.callback_.call(ctx, entries, ctx);

    // 调用clearActive方法,清空activeObservations_
    this.clearActive();
  }

  clearActive() {
    //清空his.activeObservations_, his.activeObservations_ = []
    this.activeObservations_.splice(0);
  }

  // 判断当前是否存在变更的被观察目标元素
  hasActive() {
    return this.activeObservations_.length > 0;
  }
}

ResizeObserverEntry函數

获取真正在观察的 ElementSVGElement 最新的大小。

class ResizeObserverEntry {
    /**
     * Element size of which has changed.
     * Spec: https://wicg.github.io/ResizeObserver/#dom-resizeobserverentry-target
     *
     * @readonly
     * @type {Element}
     */
    target;

    /**
     * Element's content rectangle.
     * Spec: https://wicg.github.io/ResizeObserver/#dom-resizeobserverentry-contentrect
     *
     * @readonly
     * @type {DOMRectReadOnly}
     */
    contentRect;

    /**
     * Creates an instance of ResizeObserverEntry.
     *
     * @param {Element} target - Element that is being observed.
     * @param {DOMRectInit} rectInit - Data of the element's content rectangle.
     */
    constructor(target, rectInit) {
        const contentRect = createReadOnlyRect(rectInit);

        // According to the specification following properties are not writable
        // and are also not enumerable in the native implementation.
        //
        // Property accessors are not being used as they'd require to define a
        // private WeakMap storage which may cause memory leaks in browsers that
        // don't support this type of collections.
        defineConfigurable(this, {target, contentRect});
    }
}

createReadOnlyRect函数

DOMRectReadOnly 接口通过详细列出DOMRect所使用的标准属性来定义一个属性不可变的矩形。

Object.prototype是一个对象,用于表示Object的原型对象。几乎所有的JavaScript对象都是Object的实例,其原型链上最后一个就是指向Object.prototype。一个典型的对象继承了Object.prototype的属性和方法。

创建没有原型对象的对象,比如通过Object.create(null)创建,或通过Object.setPrototypeOf(obj, null)方法来改变指定对象的原型对象。

改变Object.prototype的属性和方法,或给Object.prototype添加属性和方法,都会影响到原型链上的所有对象,除非这些对象本身有定义相同的属性和方法进一步覆盖。

Object.prototype === obj.__proto__
/**
 * Creates rectangle with an interface of the DOMRectReadOnly.
 * Spec: https://drafts.fxtf.org/geometry/#domrectreadonly
 *
 * @param {DOMRectInit} rectInit - Object with rectangle's x/y coordinates and dimensions.
 * @returns {DOMRectReadOnly}
 */
function createReadOnlyRect({x, y, width, height}) {
    // 当浏览器支持DOMRectReadOnly便使用DOMRectReadOnly,否则用Object 构造函数
    const Constr = typeof DOMRectReadOnly !== 'undefined' ? DOMRectReadOnly : Object;
    // rect對象原型鏈上有Constr.prototype上的方法
    const rect = Object.create(Constr.prototype);

    // Rectangle's properties are not writable and non-enumerable.
    defineConfigurable(rect, {
        x, y, width, height,
        top: y,
        right: x + width,
        bottom: height + y,
        left: x
    });

    return rect;
}

defineConfigurable函数

定义一个不可写 不可枚举的target对象

/**
 * Defines non-writable/enumerable properties of the provided target object.
 *
 * @param {Object} target - Object for which to define properties.
 * @param {Object} props - Properties to be defined.
 * @returns {Object} Target object.
 */
export default (target, props) => {
    for (const key of Object.keys(props)) {
        Object.defineProperty(target, key, {
            value: props[key],
            enumerable: false,
            writable: false,
            configurable: true
        });
    }

    return target;
};

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值