resize-observer-polyfill 源码解读之 ResizeObserverController

ResizeObserverController类定义

ResizeObserverController, 这个是个单例的控制器或者叫调度器。所有的 resizeObserver 归它管理,或者说 ResizeObserverSPI 归它管。它是调度器,它会检测 DOM 上面的变化,通过 MutationObserver 或者 DOMSubtreeModified 事件(如果前者不支持的话), 同时它还订阅了transactionend事件,它只关心这些 css属性的变化。

const transitionKeys = [
  "top",
  "right",
  "bottom",
  "left",
  "width",
  "height",
  "size",
  "weight",
];

当这些事件被促发的时候,它就会迭代内部的 SPI 数组,调用它们的gatherActive(),
hasActive()过滤出变化的 SPI,然后对于迭代变化的 SPI,依次调用它们的 broadcastActive() 方法。

/**
 * Singleton controller class which handles updates of ResizeObserver instances.
 */
class ResizeObserverController {
  // 指示是否已添加DOM侦听器。
  connected_ = false;

  // 告诉控制器已订阅突变事件
  mutationEventsAdded_ = false;

  // 保留对MutationObserver实例的引用
  mutationsObserver_ = null;

  // 连接的观察者列表
  observers_ = [];

  // 調度器實例
  static instance_ = null;

  /**
   * Creates a new instance of ResizeObserverController.
   *
   * @private
   */
  constructor() {
    this.onTransitionEnd_ = this.onTransitionEnd_.bind(this);
    this.refresh = throttle(this.refresh.bind(this), REFRESH_DELAY);
  }

  // 添加侦听器
  addObserver(observer) {
    // 當不存在時,將observer加入到this.observers_數組中
    if (!~this.observers_.indexOf(observer)) {
      this.observers_.push(observer);
    }

    // Add listeners if they haven't been added yet.
    if (!this.connected_) {
      this.connect_();
    }
  }

  //  从列表中移除指定侦听器
  removeObserver(observer) {
    const observers = this.observers_;
    // 找到索引, 找到大於等於0 ,未找到-1
    const index = observers.indexOf(observer);

    // 存在時 ~index 都小於0 ,轉為Boolean都為TRUE;不存在時~index 為0 ,轉為Boolean為false
    if (~index) {
      // 移除索引為index的item
      observers.splice(index, 1);
    }

    // 如果控制器没有连接的观察器,则删除侦听器
    if (!observers.length && this.connected_) {
      this.disconnect_();
    }
  }

  refresh() {
    // 当存在活跃侦听器时,返回TRUE
    const changesDetected = this.updateObservers_();

    // 如果检测到更改,则继续运行更新,因为将来可能会有由CSS转换引起的更改。
    if (changesDetected) {
      this.refresh();
    }
  }

  //  更新观察者列表中的每个观察者,并通知他们排队的条目
  updateObservers_() {
    // Collect observers that have active observations.
    const activeObservers = this.observers_.filter((observer) => {
      // 執行observer.gatherActive(),活躍的觀察者數組清空後重新存儲獲取的observer,由於後續的observer.broadcastActive()需要使用
      // 只返回observer.hasActive()為true的observe
      return observer.gatherActive(), observer.hasActive();
    });

    //  在单独的周期中传递通知,以避免任何观察者之间的冲突,例如当ResizeObserver正在跟踪同
    // 一个元素和一个元素的回调其中改变了观察到的目标的内容维度。有时这可能导致其他观察者的通知被阻止。
    activeObservers.forEach((observer) => observer.broadcastActive());

    return activeObservers.length > 0;
  }

  connect_() {
    //如果在非浏览器环境中运行,或者侦听器已存在,则不执行任何操作
    if (!isBrowser || this.connected_) {
      return;
    }

    // 订阅“Transitionend”事件用作的解决方法延迟的转换。通过这种方式,至少可以捕获元素的最终状态。
    document.addEventListener("transitionend", this.onTransitionEnd_);

    // 监听窗口resize变化
    window.addEventListener("resize", this.refresh);

    // 當瀏覽器支持MutationObserver時
    if (mutationObserverSupported) {
      //
      this.mutationsObserver_ = new MutationObserver(this.refresh);
      this.mutationsObserver_.observe(document, {
        attributes: true,
        childList: true,
        characterData: true,
        subtree: true,
      });
    } else {
      document.addEventListener("DOMSubtreeModified", this.refresh);

      // 设置已订阅DOMSubtreeModified事件
      this.mutationEventsAdded_ = true;
    }

    // 设置侦听器已添加
    this.connected_ = true;
  }

  /**
   * Removes DOM listeners.
   *
   * @private
   * @returns {void}
   */
  disconnect_() {
    //  如果在非浏览器环境中运行,或者侦听器已被删除,则不执行任何操作
    if (!isBrowser || !this.connected_) {
      return;
    }

    document.removeEventListener("transitionend", this.onTransitionEnd_);
    window.removeEventListener("resize", this.refresh);

    if (this.mutationsObserver_) {
      this.mutationsObserver_.disconnect();
    }

    if (this.mutationEventsAdded_) {
      document.removeEventListener("DOMSubtreeModified", this.refresh);
    }
    // 移除对对MutationObserver实例的引用
    this.mutationsObserver_ = null;
    //指示已取消订阅DOMSubtreeModified事件
    this.mutationEventsAdded_ = false;

    this.connected_ = false;
  }

  // 當transitionKeys有一個item存在css過度行為時返回TRUE
  onTransitionEnd_({ propertyName = "" }) {
    // 检测过渡是否会影响元素的尺寸
    const isReflowProperty = transitionKeys.some((key) => {
      return !!~propertyName.indexOf(key);
    });

    if (isReflowProperty) {
      this.refresh();
    }
  }

  /**
   * Returns instance of the ResizeObserverController.
   *
   * @returns {ResizeObserverController}
   */
  static getInstance() {
    if (!this.instance_) {
      this.instance_ = new ResizeObserverController();
    }

    return this.instance_;
  }
}

addObserver方法

按位非运算时,任何数字x的运算结果都是 -(x + 1)。例如,~-5 运算结果为 4

Array.prototype.indexOf()方法返回数组中第一次出现给定元素的下标,如果不存在则返回 -1。
存在的時候 [x].indexOf(x)返回的值是大於等於 0, ~[x].indexOf(x)返回永遠小於 0
不存在的時候[x].indexOf(z)返回-1,~[x].indexOf(z)返回 0

按位非運算例子

console.log(~1);
// Expected output: -2

console.log(~2);
// Expected output: -3

console.log(~0);
// Expected output: -1
console.log(~-1);
// Expected output: 0

逻辑非(!,逻辑连接取反)运算符将真值或假值转换为对应的相反值,经常用于布尔(逻辑)值。当与非布尔值使用时,如果其操作数可以转化为 true,则返回 false,否则返回 true。

console.log(!-2);
// Expected output: false

console.log(!0);
// Expected output: true

数值转为布尔类型示例

Boolean(0); //false

Boolean(3); //true

Boolean(-3); //true

updateObservers_函數

const activeObservers = this.observers_.filter((observer) => {
  // 只返回observer.hasActive()為true的observe
  return observer.gatherActive(), observer.hasActive();
});

connect_方法

transitionend 事件的事件处理函数,在某个 CSS transition 完成时触发。

DOMSubtreeModified:在 DOM 结构中发生任何变化时触发;

// Check if MutationObserver is available.
const mutationObserverSupported = typeof MutationObserver !== "undefined";

MutationObserver 例子

MutationObserver 接口提供了监视对 DOM 树所做更改的能力。它被设计为旧的 Mutation Events 功能的替代品,该功能是 DOM3 Events 规范的一部分。
MutationObserver()構造函數 创建并返回一个新的 MutationObserver 它会在指定的 DOM 发生变化时被调用。其中有三個方法:

  • disconnect(),阻止 MutationObserver 实例继续接收的通知,直到再次调用其 observe() 方法,该观察者对象包含的回调函数都不会再被调用。
  • observe(),配置 MutationObserver 在 DOM 更改匹配给定选项时,通过其回调函数开始接收通知。
  • takeRecords(),从 MutationObserver 的通知队列中删除所有待处理的通知,并将它们返回到 MutationRecord 对象的新 Array 中。
// 选择需要观察变动的节点
const targetNode = document.getElementById("some-id");

// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: true, subtree: true };

// 当观察到变动时执行的回调函数
const callback = function (mutationsList, observer) {
  // Use traditional 'for loops' for IE 11
  for (let mutation of mutationsList) {
    if (mutation.type === "childList") {
      console.log("A child node has been added or removed.");
    } else if (mutation.type === "attributes") {
      console.log("The " + mutation.attributeName + " attribute was modified.");
    }
  }
};

// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);

// 以上述配置开始观察目标节点
observer.observe(targetNode, config);

// 之后,可停止观察
observer.disconnect();

onTransitionEnd_函數

  /**
   * "Transitionend" event handler.
   *
   * @private
   * @param {TransitionEvent} event
   * @returns {void}
   */
  onTransitionEnd_({ propertyName = "" }) {
    // Detect whether transition may affect dimensions of an element.
    const isReflowProperty = transitionKeys.some((key) => {
      return !!~propertyName.indexOf(key);
    });

    if (isReflowProperty) {
      this.refresh();
    }
  }

生成了一份PDF文档,需要的可以点击链接下载

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值