现代浏览器观察者Observer

Intersection Observer 交叉观察者
Mutation Observer 变动观察者
Resize Observer 视图观察者
Performance Observer 性能观察者
 
  IntersectionObserver MutationObserver   ResizeObserver PerformanceObserver
作用 观察一个元素是否在视窗可见 观察DOM中的变化 观察视口大小的变化 坚持测性能度量事件
方法

observe()

disconnect()

takeRecords()

observe()

disconnect()

takeRecords()

unobserve()

observe()

disconnect()

unobserve()

 
 

observe()

disconnect()

takeRecords()

取代  DOM Mutation events

 getBoundingClientRect()返回元素的大小及其相对于可视窗口的位置

Scroll和Resize事件

 Resize事件 Performance接口 
用途
  1.  无限滚动
  2. 图片懒加载
  3. 兴趣埋点
  4. 控制动画、视频执行(性能优化)
  1. 更高性能的数据绑定及响应
  2. 实现视觉差滚动
  3. 图片预加载
  4. 实现富文本编辑器 
  1. 更智能的响应式布局(取代@media)
  2. 响应式组件 
  1. 更细颗粒的性能监控
  2. 分析性能对业务的影响(交互是否影响销量) 

IntersectionObserver 交叉观察者

IntersectionObserver接口,提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法,祖先元素与视窗(viewport)被称为根(root)
 
使用方式:

1.创建观察者

const options = {
  root:document.getElementById('zjy'),
  rootMargin:'0px',
  threshold:[0.3,0.5,0.8,1]          
}
const observer = new IntersectionObserver(handler,options)

  root指定一个根元素

  rootMargin使用与类似设置css边距的语法来指定根边距(根元素观察影响的范围)

  threshold阈值,可以为数组。[0.3]表示当目标元素在根元素指定的元素内可见30%时,调用处理函数。

2.定义回调函数

function handler (entries, observer) { 
    entries.forEach(entry => { 
    // 每个成员都是一个IntersectionObserverEntry对象。
    // 举例来说,如果同时有两个被观察的对象的可见性发生变化,entries数组就会有两个成员。
    // entry.boundingClientRect 目标元素的位置信息
    // entry.intersectionRatio 目标元素的可见比例
    // entry.intersectionRect 交叉部分的位置信息
    // entry.isIntersecting 
    // entry.rootBounds 根元素的位置
    // entry.target 
    // entry.time 时间戳
    }); 
}

可以在handler中做自己想做的事,例如:

 entry.target.src = entry.target.dataset.src;
 observer.unobserve(entry.target);

3.定义要观察的目标对象    

const domArr = document.querySelectorAll(“img”); 
domArr.forEach((t)=>{
  observer.observe(t);
})

MutationObserver 变动观察者

接口提供了对DOM树所做更改的能力。它被设计而为旧的MutationEvents功能的替代品,该功能是DOM3 Events 规范的一部分。

使用方式

1.定义观察者

let observer new MutationObserver(cb)

2.定义回调函数

function cb(mutations,observer){
  mutations.forEach((mutation)=>{
  //输出变更记录 console.log(mutation) }) }

每个mutation都对应一个MutationRecord对象,记录着DOM每次发生变化的变化记录。

MutationRecord对象包含的DOM相关信息如下:

属性 意义
type 观察的变动类型(attribute、characterData或者childList)
target 发生变动的DOM节点
addedNodes 新增的DOM节点
removedNodes 删除的DOM节点
previousSibling 前一个同级节点,如果没有则返回null
nextSibling 下一个同级节点,如果没有则返回null
attributeName 发生变动的属性。如果设置了attributeFilter,则只返回预先指定的属性
oldValue 变动前的值。这个属性只对attribute和characterData变动有效,如果发生childList变动,只返回null

 

3.定义要观察的目标对象

observer.observe(content, {
    attributes: true, // Boolean - 观察目标属性的改变
    characterData: true, // Boolean - 观察目标数据的改变(改变前的数据/值)
    childList: true, // Boolean - 观察目标子节点的变化,比如添加或者删除目标子节点,不包括修改子节点以及子节点后代的变化
    subtree: true, // Boolean - 目标以及目标的后代改变都会观察
    attributeOldValue: true, // Boolean - 表示需要记录改变前的目标属性值
    characterDataOldValue: true, // Boolean - 设置了characterDataOldValue可以省略characterData设置
    // attributeFilter: ['src', 'class'] // Array - 观察指定属性
});
  • 第一参数:被观察的DOM节点。
  • 第二参数:配置需要观察的变动项options

ResizeObserver 视图观察者

ResizeObserver API是一个新的JavaScript API,与IntersectionObserver API非常相似,它们都允许我们去监听某个元素的变化。

使用方式与MutationObserver大致相同 简单描述一下

//创建观察者
let observer = new ResizeObserver(callback);
//定义回调函数
function callback(entries){
 entries.forEach((entry)=>{
   console.log(entry)    
 })    
}
//定义要观察的目标对象
observer.observe(document.body)

 

介绍一下entry对象,包含两个属性contentRect和target

contentRect一些位置信息

属性 作用
bottom top + height 的值
height 元素本身的高度,不包含padding、border的值
left padding-left的值
right left + width 的值
top padding-top 的值
width 元素本身的宽度,不包含padding 、border的值
x 大小与top相同
y 大小与left相同

 

PerformanceObserver 性能观察者

这是一个浏览器和Node.js 里都存在的API,采用相同W3CPerformance Timeline规范

  • 在浏览器中,我们可以使用 window 对象取得window.performancewindow.PerformanceObserver
  • 而在 Node.js 程序中需要perf_hooks 取得性能对象
const { PerformanceObserver, performance } = require('perf_hooks');  
使用方式与MutationObserver大致相同 简单描述一下
//创建观察者
let observer = new PerformanceObserver(callback); 
//定义回调函数事件
function callback(list, observer){
   const entries = list.getEntries();
   entries.forEach((entry) => {
     console.log(“Name: “ + entry.name + “, Type: “ + entry.entryType + “, Start: “ + entry.startTime + “, Duration: “ + entry.duration + “\n”);
  });
}
//定义要观察的目标对象
observer.observe({entryTypes: ["entryTypes"]});

 每一个list都是一个完整的PerformanceObserverEntryList对象,包含三个方法getEntriesgetEntriesByTypegetEntriesByName

方法 作用
getEntries() 返回一个列表,该列表包含一些用于承载各种性能数据的对象,不做任何过滤
getEntriesByType() 返回一个列表,该列表包含一些用于承载各种性能数据的对象,按类型过滤
getEntriesByName() 返回一个列表,,该列表包含一些用于承载各种性能数据的对象,按名称过滤

 observer.observe(...)方法接受可以观察到的有效的入口类型。这些输入类型可能属于各种性能API,比如User timingNavigation Timing API。有效的entryType值:

属性 别名 类型 描述

frame

navigation

PerformanceFrameTiming

PerformanceNavigationTiming

URL 文件的地址
resource PerformanceResourceTiming URL 所请求资源的解析URL
mark PerformanceMark DOMString 通过调用创建标记时使用的名称performance.mark()
measure PerformanceMeasure DOMString 通过调用创建度量时使用的名称performance.measure()
paint PerformancePaintTiming DOMString 无论是'first-paint'或'first-contentful-paint'
longtask PerformanceLongTaskTiming DOMString 报告长任务的实例

 

静态资源监控示例代码

function filterTime(a, b) {
  return (a > 0 && b > 0 && (a - b) >= 0) ? (a - b) : undefined;
}

let resolvePerformanceTiming = (timing) => {
  let o = {
    initiatorType: timing.initiatorType,
    name: timing.name,
    duration: parseInt(timing.duration),
    redirect: filterTime(timing.redirectEnd, timing.redirectStart), // 重定向
    dns: filterTime(timing.domainLookupEnd, timing.domainLookupStart), // DNS解析
    connect: filterTime(timing.connectEnd, timing.connectStart), // TCP建连
    network: filterTime(timing.connectEnd, timing.startTime), // 网络总耗时

    send: filterTime(timing.responseStart, timing.requestStart), // 发送开始到接受第一个返回
    receive: filterTime(timing.responseEnd, timing.responseStart), // 接收总时间
    request: filterTime(timing.responseEnd, timing.requestStart), // 总时间

    ttfb: filterTime(timing.responseStart, timing.requestStart), // 首字节时间
  };

  return o;
};

let resolveEntries = (entries) => entries.map(item => resolvePerformanceTiming(item));

let resources = {
  init: (cb) => {
    let performance = window.performance || window.mozPerformance || window.msPerformance || window.webkitPerformance;
    if (!performance || !performance.getEntries) {
      return void 0;
    }

    if (window.PerformanceObserver) {
      let observer = new window.PerformanceObserver((list) => {
        try {
          let entries = list.getEntries();
          cb(resolveEntries(entries));
        } catch (e) {
          console.error(e);
        }
      });
      observer.observe({
        entryTypes: ['resource']
      })
    } else {
        window.addEventListener('load', () => {
        let entries = performance.getEntriesByType('resource');
        cb(resolveEntries(entries));
      });
    }
  },
};

 

posted @ 2020-05-26 15:25  671_MrSix  阅读(379)  评论(0编辑  收藏  举报