js list添加元素_JS中的观察者们 —— 四种 Observers

JS中的观察者们 —— 四种 Observers

今天跟大家一块学习一下JS中的几个观察者(Observer) API,他们是 ——

  • Intersection Observer
  • Mutation Observer
  • Resize Observer
  • Performance Observer

Intersection Observer

当你想监听某个元素,当它在视口中可见的时候希望可以得到通知,这个API就是最佳的选择了。以往我们的做法是绑定容器的scroll事件,或者设定时器不停地调用getBoundingClientRect() 获取元素位置, 这样做的性能会很差,因为每次获取元素的位置都会引起整个布局的重新计算。还有一个场景是,如果你的元素被放在iframe里,如一些广告,想要知道他们何时出现几乎是不可能的。

现在,我们完全可以把这些工作交给IntersectionObserver了。

326b16873dfb9b023f24b5ff6c64e9f6.gif

怎么用?

实例化一个观察器:

var observer = new IntersectionObserver(callback[, options]);
  • callback 是一个回调函数,里面返回监听目标元素的实时数据组成的数组
    • time 时间戳
    • rootBounds 根元素的位置信息
    • boundingClientRect 目标元素的位置信息
    • intersectionRect 交叉部分的位置信息
    • intersectionRatio 目标元素的可见比例,看下图示
    • target等
  • options 是一些配置
    • root 目标元素的祖先元素,即该元素必须是目标元素的直接或间接父级
    • rootMargin 一个在计算交叉值时添加至root的边界盒中的一组偏移量,写法类似CSS的margin
    • threshold 规定了一个监听目标与边界盒交叉区域的比例值,可以是一个具体的数值或是一组0.0到1.0之间的数组
ae389cba28dca6aad5063c33dc8394b7.png


开始监听元素:

observer.observe(target)

这两步之后,就可以在callback里补全业务代码了。

此外,还有两个方法:

  • 停止对某目标的监听
observer.unobserve(target)
  • 终止对所有目标的监听
observer.disconnect()

demo ?↓

做一个视频流的简单demo,当视频滚动到全部出现在屏幕的时候播放,并暂停其他“滚出”屏幕的或者还未“滚进来”的视频。

(滚动看效果,括号里显示的是每个元素在观察视口的可见比例)

主要代码:

methods: {
    reserveCallback (entries) {
     let ratio = entries[0].intersectionRatio,
      $target = entries[0].target;
     if(1 - ratio <= 0.03) {
      this.onPlay($target);
     }
    },

    onPlay ($target) {
     //播放视频
    },

    onPause ($target) {
     //暂停视频
    },

    addObserver ($targets) {
     for(let i = 0, len = $targets.length; i             //开始监听
      this.observer.observe($targets[i]);
     }
    }
},

mounted () {
    let $referenceBox = document.querySelector('.js-content'),
     $targets = document.querySelectorAll('.js-item');
    //实例化观察器
    this.observer = new IntersectionObserver(this.reserveCallback, {
     root: $referenceBox,
     rootMargin: '0px',
     threshold: [0.7, 0.8, 0.9, 1]
    });

    this.addObserver($targets);
    }

上面的demo也有很大的不足之处:对所有的视频元素都进行了监听,并且对“消失”的视频没有关掉观察器,当视频数增多时,势必会引起性能上的问题。如果真的应用在业务中还需要进一步的优化。

其他应用

除了上面提到的广告展示,还可以做懒加载:在列表的底部设置一个用来监听的元素,当它出现时,加载更多的内容,同时改变该监听元素的位置到底部继续监听。

Mutation Observer

当我们想知道某个元素在某个时候发生了具体哪些变化时,MutationObserver便是最佳选择了。

怎么用

实例化一个观察器:

var observer = new MutationObserver(callback);

开始监听:

observer.observe(target, config);
  • config 填写需要监听属性
    • attributes 布尔类型 属性的变动
    • childList 布尔类型 子节点的变动(指新增,删除或者更改)
    • characterData 布尔类型 节点内容或节点文本的变动。
    • subtree 布尔类型 是否将该观察器应用于该节点的所有后代节点
    • attributeOldValue 布尔类型 观察attributes变动时,是否需要记录变动前的属性值
    • characterDataOldValue 布尔类型 观察characterData变动时,是否需要记录变动前的值
    • attributeFilter 数组 需要观察的特定属性(比如['class','src'])

下面是一个简单的demo,任何对节点的操作都会收到MutationObserver API的通知。

demo ?↓

主要代码:

methods: {
    observerCallBack (mutations) {
     //do log
    },

    onAddAttr () {
     // toggle attribute 'd'
    }
},

mounted () {
    this.$list = document.querySelector('.js-list');

    let config = {
     attributes: true,
     childList: true,
     characterData: true,
     subtree: true
    };
    let observer = new MutationObserver(this.observerCallBack);
    observer.observe(this.$list, config);
}

Resize Observer

从名字就可以知道该API是干嘛的了:监听元素的尺寸变化。

之前为了监听元素的尺寸变化,都将侦听器附加到window中的resize事件。对于不受窗口变化影响的元素就没那么简单了。现在我们可以使用该API轻松的实现。

怎么用

同样只需要两步:

var observer = new ResizeObserver(callback);
observer.observe(target);

但是它的触发也是有条件的,下面是触发和不触发的条件:

  • 触发

    • 1.元素被插入或移除时触发
    • 2.元素display从显示变成 none 或相反过程时触发
  • 不触发

    • 1.对于不可替换内联元素不触发
    • 2.CSS transform 操作不触发

demo ?↓

拖动右下角变换元素尺寸或点击随机按钮设置随机尺寸,都会收到通知(里面的小星星转动速度变化)。而通过transform变化视觉上的尺寸时是没有通知的。

遗憾的是该API仍处于实验阶段,好多浏览器没有实现。

不过,由于MutationObserver已经被大部分浏览器支持,且有polyfill的支持, 我们可以轻松的利用他来代替ResizeObserver。

推荐阅读

  • JavaScript中的ResizeObserver
  • Resize Observer 1
  • A Look at the Resize Observer JavaScript API
  • THE RESIZE OBSERVER EXPLAINED

Performance Observer

PerformanceObserver 是个相对比较复杂的API,用来监控各种性能相关的指标。该API由一系列API组成:

  • Performance Timeline Level 2
  • Paint Timing 1
  • Navigation Timing Level 2
  • User Timing Level 3
  • Resource Timing Level 2
  • Long Tasks API 1

若真细研究起来东西还是很多的,这里只简单地介绍一下(因为我也没搞太清?)。

怎么用

var observer = new PerformanceObserver(callback);
observer.observe({ entryTypes: [entryTypes] });

entryTypes: 需要监控的指标名,这些指标都可以通过 performance.getEntries() 获取到,此外还可以通过 performance.getEntriesByName() 、performance.getEntriesByType() 分别针对 name 和 entryType 来过滤。

  • mark 获取所有通过 performance.mark(markName) 做的所有标记
  • measure 获取通过 performance.measure(measureName, markName_start, markName_end) 得到的所有测量值
  • longtask 监听长任务(超过50ms 的任务)(不足:只能监控到长任务的存在,貌似不能定位到具体任务)
  • paint 获取绘制相关的性能指标,分为两种:“first-paint”、“first-contentful-paint”
  • navigation 各种与页面有关的时间,可通过 performance.timing 获取
  • resource 各种与资源加载相关的信息

相较之前的各种操作,现在我们代码仅需要像这样就可以了——

const observer = new PerformanceObserver((list) => {
   let output;
   for (const item of list.getEntries()) {
       //业务代码
   }
});

observer.observe({
    //按需要填写
    entryTypes: ['mark', 'measure', 'longtask', 'paint', 'navigation', 'resource']
});

上述内容如有错误,欢迎不吝赐教?。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值