VueJSソース学習——MutationObserver実現nextTick
3920 ワード
Vueは開発者がDOMをできるだけ直接操作しないことを提唱しているが、時には様々なニーズで開発者にそうさせなければならないことがある.そこでnextTickの実現は、開発者がデータを修正した後、データをDOMに更新してから対応する関数を実行し、最新のDONデータを取得することである.
原文アドレス項目アドレス
ではnextTickをどのように実現するかというと、まずsettimeoutの非同期コールバックを利用して実現することが考えられますが、各ブラウザによってsettimeoutの遅延が高いため、nextTickでは最後のスペアタイヤとしてのみ使用され、第一選択案はMutationObserver(後述ではMOがMutationObserverを表す)
nextTickのソースコード実装
MutationObserverの機能と役割
MOは開発者にある範囲のDOM数が変化したときに適切に反応できる能力であるMDNを提供する
開発者は、DOM要素を傍受し、DOMツリーが変化したときに提供されるコールバック関数を実行するオブジェクトを作成することができます.
具体的にはこのDEMOを参考に
特にインスタンス化する場合は、コールバック関数を先に入力する必要があります.
次に、観察ノードと観察のプロパティを含む観察オプションを設定します.
古いバージョンのグーグルと火狐には、接頭辞付きMOが必要です.
MutationObserverとmicrotask
では、なぜMutationObserverを使うのが好ましいのでしょうか.
最初はMOがDOMの変化を傍受するためのものだと思っていたが、textnodeを用いてDOMの変化をシミュレートしてMOを再利用してトリガを傍受してnextTickを実現するのは適切ではない.
ここではJSの実行メカニズムを理解する必要があります(私の3つの観点を再リフレッシュしました)、JSのイベント実行メカニズムが実行されるとtaskとmicrotaskが区別され、エンジンはtaskごとに実行が完了し、キューからtaskを取って実行する前に、すべてのmicrotaskキューのmicrotaskを実行します
(taskとmicrotaskの抜粋https://jakearchibald.com/)
settimeoutコールバックは新しいtaskに割り当てられて実行を待つが、Promiseのresolver、MOのコールバックはmicrotaskのキューに割り当てられるのでsettimoutより先に実行される
settimoutよりも速いほか、レンダリング性能の問題もあります.HTML Standardによると、taskごとに実行が完了するとUIが再レンダリングされ、microtaskでデータ更新が完了し、現在taskが終了すると最新のUIが得られます.逆にtaskを新規作成してデータ更新を行うと、レンダリングは2回行われます.
だから性価がこんなに高いMOは自然に第一選択になりました
Microtaskについては、Jakeが書いたTasks,microtasks,queues and schedulesを具体的に読むことができます.
nexttickのバージョン反復
上記nextTickのソースコード実装はvueの最初のバージョンv 1に属する.0.9,mutationObserverを深く掘り下げたところnexttickはvueのバージョン反復においても進化し続け,同僚も劣化したことがあり,非常に興味深い:
まず劣化したイベントについて述べると、尤大(vueの著者)はMOの代わりに
進化については、後続のバージョンでes 6の新しい文法のため、nexttickはPromiseを使用し始めた.thenとMOは第一選択と第二選択を行い、前の議論で述べたように、Promise.thenもmicrotaskに属している.
しげん MutationObserver MDN Tasks, microtasks, queues and schedules
原文アドレス項目アドレス
ではnextTickをどのように実現するかというと、まずsettimeoutの非同期コールバックを利用して実現することが考えられますが、各ブラウザによってsettimeoutの遅延が高いため、nextTickでは最後のスペアタイヤとしてのみ使用され、第一選択案はMutationObserver(後述ではMOがMutationObserverを表す)
nextTickのソースコード実装
export const nextTick = (function () {
var callbacks = []
var pending = false
var timerFunc
function nextTickHandler () {
pending = false
var copies = callbacks.slice(0)
callbacks = []
for (var i = 0; i < copies.length; i++) {
copies[i]()
}
}
/* istanbul ignore if */
if (typeof MutationObserver !== 'undefined') { // MutationObserver
var counter = 1
var observer = new MutationObserver(nextTickHandler) // MO
var textNode = document.createTextNode(counter)
observer.observe(textNode, { // textNode
characterData: true // nextTickHandler
})
timerFunc = function () {
counter = (counter + 1) % 2 // timeFunc 1 0
textNode.data = counter
}
} else {
timerFunc = setTimeout // MutationObserver, setTimeout
}
return function (cb, ctx) {
var func = ctx
? function () { cb.call(ctx) }
: cb
callbacks.push(func)
if (pending) return
pending = true
timerFunc(nextTickHandler, 0)
}
})()
MutationObserverの機能と役割
MOは開発者にある範囲のDOM数が変化したときに適切に反応できる能力であるMDNを提供する
開発者は、DOM要素を傍受し、DOMツリーが変化したときに提供されるコールバック関数を実行するオブジェクトを作成することができます.
具体的にはこのDEMOを参考に
特にインスタンス化する場合は、コールバック関数を先に入力する必要があります.
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation.type);
})
})
次に、観察ノードと観察のプロパティを含む観察オプションを設定します.
//
var target = document.querySelector('#some-id');
// :
var config = { attributes: true, childList: true, characterData: true }
//
observer.observe(target, config);
// ,
observer.disconnect();
古いバージョンのグーグルと火狐には、接頭辞付きMOが必要です.
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
MutationObserverとmicrotask
では、なぜMutationObserverを使うのが好ましいのでしょうか.
最初はMOがDOMの変化を傍受するためのものだと思っていたが、textnodeを用いてDOMの変化をシミュレートしてMOを再利用してトリガを傍受してnextTickを実現するのは適切ではない.
ここではJSの実行メカニズムを理解する必要があります(私の3つの観点を再リフレッシュしました)、JSのイベント実行メカニズムが実行されるとtaskとmicrotaskが区別され、エンジンはtaskごとに実行が完了し、キューからtaskを取って実行する前に、すべてのmicrotaskキューのmicrotaskを実行します
(taskとmicrotaskの抜粋https://jakearchibald.com/)
settimeoutコールバックは新しいtaskに割り当てられて実行を待つが、Promiseのresolver、MOのコールバックはmicrotaskのキューに割り当てられるのでsettimoutより先に実行される
settimoutよりも速いほか、レンダリング性能の問題もあります.HTML Standardによると、taskごとに実行が完了するとUIが再レンダリングされ、microtaskでデータ更新が完了し、現在taskが終了すると最新のUIが得られます.逆にtaskを新規作成してデータ更新を行うと、レンダリングは2回行われます.
だから性価がこんなに高いMOは自然に第一選択になりました
Microtaskについては、Jakeが書いたTasks,microtasks,queues and schedulesを具体的に読むことができます.
nexttickのバージョン反復
上記nextTickのソースコード実装はvueの最初のバージョンv 1に属する.0.9,mutationObserverを深く掘り下げたところnexttickはvueのバージョン反復においても進化し続け,同僚も劣化したことがあり,非常に興味深い:
まず劣化したイベントについて述べると、尤大(vueの著者)はMOの代わりに
window.postMessage
を使用してnextTickを実現したが、開発者が使用した後に問題を発見した.この2つのJSF:jsfiddle 1とjsfiddle 2を見ることができ、2つの例は異なるバージョンで要素の絶対定位を実現し、最初に2.0.0-rc 6を使用し、このバージョンはMOを採用している.その後、IOS 9.3のWebViewにMOにバグがあったため、特にウンチをwindowに変えた.PostMessageは、第2のインスタンスバージョンが2.0.0-rc 7であるが、postMessageはmacrotask、すなわちtaskにコールバックを入れるため、複数のUIを実行する可能性のあるtaskはwindowを実行しない.postMessageのtaskも、DOM操作の更新が遅れる.尤大は後続バージョンで今回の修正を撤回し、具体的な議論はissueを見ることができる.進化については、後続のバージョンでes 6の新しい文法のため、nexttickはPromiseを使用し始めた.thenとMOは第一選択と第二選択を行い、前の議論で述べたように、Promise.thenもmicrotaskに属している.
しげん