MutationObserver 接口提供了监听 DOM 树变化的能力,被设计出来用来替代旧的 Mutaion Events 目前已经是 DOM3 事件标准的一部分。
构造函数 MutationObserver()
创建并返回实例,当 DOM 发生变化时去运行一个回调。
|
|
方法(observe, disconnect, takeRecords)
方法名 | 说明 |
disconnect() | 停止监听,实例不会再接受到任何通知直到再次调用 observe() |
observe() | 重启监听 |
takeRecords() | 移除 MutationObserver 的通知队列中所有挂起的通知, 并返回一个 MutationRecord 数组 |
测试:
可以被监听的属性:
|
|
创建实例: observer = new MutationObserver(callback)
observer.observe(target<Node>, options)
options 支持的各选项,可以用来指定监听哪些行为变化:
名字 | 说明 |
---|---|
childList | 监听目标节点的 children 变化 |
attributes | 监听目标节点的属性变化 |
characterData | 监听目标节点的 data |
subtree | 监听目标自身以及它的子孙节点 |
attributeOldValue | 记录 attributes=true 时变化之前的旧值 |
characterDataOldValue | 记录 characterData=true 时变化之前的旧值 |
attributeFilter | Array<string> 忽略监听哪些属性的变化 |
实现伪码:
|
|
TIP
Transient registered observers are used to track mutations within a given node’s descendants after node has been removed so they do not get lost when subtree is set to true on node’s parent.
临时注册的 observers 用来在目标节点被移除之后,跟踪目标节点的子孙节点的变化,以
防 在目标节点的父级节点上设置了 subtree:true
时丢失这些子孙节点的信息。
observer.disconnect()
|
|
observer.takeRecords()
|
|
队列管理(Record Queue)
当一个 mutation 入列时发生以下步骤(伪码形式展示):
实现主要分三个部分:
遍历所有祖先节点,找到已注册了的 observer, 如果是 attributes 和 characterData 则需要记录下变化之前的旧值
遍历保存的所有旧值和对应的 observer,为其创建新的
MutationRecord
进行入列操作, 将来执行 callback 时传入的就是这些 record。
|
|
例如:入列一个 childList
mutation
queue('childList', target, null, null, null, addedNodes, removedNodes,
previousSibling, nextSibling)
此时的 attributeName
, attributeNamespace
, oldValue
都为 null
callback(…)
callback 会在指定的 DOM 树发生变化时被调用,调用时:
callback(mutationRecord: MutationRecord, mutationObserver: MutationObserver)
mutationRecord 是一个
MutationRecord
类型对象,包含了触发的 mutation 信息(比 如:类型)等。mutationObserver: 是
new MutationObserver(callback)
得到的那个实例对象。
如:
MutationRecord
第一个参数 mutationsList<MutationRecord>
中 MutationRecord
的接口实现:
|
|
type: 有三个值
"attribute"
: DOM 元素属性的变化"characterData"
: CharacterData 节点的变化"childList"
: DOM 树或节点的变化target: 根据 mutation 类型不同指向不同的目标,如果
type
是:"attribute"
: 指向属性发生变化的那个元素本身"characterData"
: CharacterData 节点"childList"
: 谁的子节点变化了就指向谁addedNodes, removedNodes: 当
type = childList
时,被添加或删除的节点列表previousSibling, nextSibling: 针对被添加或移除的节点而言的 preivous 和 next 兄弟节点。
attributeName: 发生变化的属性名
attributeNamespace: 发生变化的属性名的命名空间
oldValue: 取决于
type
:"attribute"
: 变化之前的属性值"characterData"
: CharacterData 节点变化之前的 data"childList"
: null~~
总结
学习这个对象原因,是因为 vue3 3.2.0-beta.1 中有个 bug #3894, 说是当使用
transition + v-if + cssVar(v-bind(var))
的时候,会导致 cssVar 不能正常使用,
vue-next 解决这个问题的时候就用到了 MutationObserver
对象。
添加的代码:
|
|
等于是说,监听了当前组件的父级 DOM 节点,当它的 children 发生变化时候去执行 setVars,设置 css 变量,从而解决这个问题。
MutationObserver
使用步骤:创建实例:
var ob = new MutationObserver(callback)
开启监听:
ob.observe(target, options)
, options 指定监听类型(attributes
,characterData
,childList
)停止监听:
ob.disconnect()
提取清空队列:
ob.takeRecords()
MutationRecord
包含内容type: attributs - 属性变化, characterData - data 变化, childList - 子孙节点 变化
target: 变化的目标节点
addedNodes, removedNodes: 当
type = childList
时被移除或添加的节点列表previousSibling, nextSibling: 目标元素的兄弟节点
attributeName: 变化的属性名
oldValue: 变化之前的值
callback(mutationsList, mutationObserver)
mutationsList:
Array<MutationRecord>
mutationObserver: MutationObserver, 即通过
new MutationObserver(callback)
创 建的那个实例对象。