VueJSソース学習——MutationObserver実現nextTick


Vueは開発者がDOMをできるだけ直接操作しないことを提唱しているが、時には様々なニーズで開発者にそうさせなければならないことがある.そこでnextTickの実現は、開発者がデータを修正した後、データをDOMに更新してから対応する関数を実行し、最新のDONデータを取得することである.
原文アドレス項目アドレス
では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に属している.
しげん
  • MutationObserver MDN
  • Tasks, microtasks, queues and schedules