ライフサイクルメソッド
概要
コンポーネントと仮想 DOM ノードは、ライフサイクルメソッド(別名 hooks )を持つことができます。これらのメソッドは、DOM 要素のライフサイクルにおける様々なタイミングで呼び出されます。
// コンポーネント内のサンプル hook
var ComponentWithHook = {
oninit: function (vnode) {
console.log('コンポーネントを初期化します');
},
view: function () {
return 'hello';
},
};
// vnode 内のサンプル hook
function initializeVnode() {
console.log('vnode を初期化します');
}
m(ComponentWithHook, { oninit: initializeVnode });
すべてのライフサイクルメソッドは、最初の引数として vnode を受け取り、this
キーワードは vnode.state
にバインドされます。
ライフサイクルメソッドは、m.render()
呼び出しの副作用としてのみ呼び出されます。Mithril の外部で DOM が変更された場合、これらのメソッドは呼び出されません。
DOM 要素のライフサイクル
DOM 要素は通常、作成されてドキュメントに追加されます。その後、UI イベントが発生し、データが変更されると、属性または子ノードが更新されることがあります。また、要素はドキュメントから削除されることもあります。
要素が削除された後、一時的にメモリプールに保持される場合があります。プールされた要素は、後続の更新で再利用されることがあります(_DOM リサイクル_と呼ばれるプロセス)。要素をリサイクルすることで、最近存在した要素のコピーを再作成するパフォーマンスコストを回避できます。
oninit
oninit(vnode)
hook は、vnode が仮想 DOM エンジンによって処理される直前に呼び出されます。oninit
は、その DOM 要素がドキュメントにアタッチされる前に実行されることが保証されており、親 vnode ではその子 vnode よりも前に実行されることが保証されています。ただし、祖先または子孫の DOM 要素の存在は保証されません。oninit
メソッドから vnode.dom
にアクセスしないでください。
この hook は、要素が更新されたときには呼び出されませんが、要素がリサイクルされた場合には呼び出されます。
他の hook と同様に、oninit
コールバックの this
キーワードは vnode.state
を指します。
oninit
hook は、vnode.attrs
または vnode.children
を介して渡された引数に基づいてコンポーネントの状態を初期化するのに役立ちます。
function ComponentWithState() {
var initialData;
return {
oninit: function (vnode) {
initialData = vnode.attrs.data;
},
view: function (vnode) {
return [
// 初期化時のデータを表示:
m('div', 'Initial: ' + initialData),
// 現在のデータを表示:
m('div', 'Current: ' + vnode.attrs.data),
];
},
};
}
m(ComponentWithState, { data: 'Hello' });
このメソッド内でモデルデータを同期的に変更しないでください。oninit
は他の要素の状態に関する保証を行わないため、このメソッドからモデルを変更しても、次のレンダーサイクルまで UI 全体に反映されない場合があります。
oncreate
oncreate(vnode)
hook は、DOM 要素が作成され、ドキュメントにアタッチされた後に呼び出されます。oncreate
は、レンダーサイクルの最後に実行されることが保証されているため、このメソッド内で vnode.dom.offsetHeight
や vnode.dom.getBoundingClientRect()
などのレイアウト値を安全に読み取ることができます。
この hook は、要素が更新されたときには呼び出されません。
他の hook と同様に、oncreate
コールバックの this
キーワードは vnode.state
を指します。vnode に oncreate
hook がある DOM 要素はリサイクルされません。
oncreate
hook は、再描画を引き起こす可能性のあるレイアウト値の取得や、アニメーションの開始、DOM 要素への参照を必要とするサードパーティライブラリの初期化などに役立ちます。
var HeightReporter = {
oncreate: function (vnode) {
console.log('Initialized with height of: ', vnode.dom.offsetHeight);
},
view: function () {},
};
m(HeightReporter, { data: 'Hello' });
このメソッドからモデルデータを同期的に変更しないでください。oncreate
はレンダーサイクルの最後に実行されるため、このメソッドからモデルを変更しても、次のレンダーサイクルまで UI に反映されません。
onupdate
onupdate(vnode)
hook は、DOM 要素が更新され、ドキュメントにアタッチされた後に呼び出されます。onupdate
は、レンダーサイクルの最後に実行されることが保証されているため、このメソッドから vnode.dom.offsetHeight
や vnode.dom.getBoundingClientRect()
などのレイアウト値を安全に読み取ることができます。
この hook は、要素が前のレンダーサイクルに存在していた場合にのみ呼び出されます。要素が作成されたとき、またはリサイクルされたときには呼び出されません。
vnode に onupdate
hook がある DOM 要素はリサイクルされません。
onupdate
hook は、再描画をトリガーする可能性のあるレイアウト値を読み取ったり、モデルデータが変更された後、サードパーティライブラリで UI に影響を与える状態を動的に更新する際に役立ちます。
function RedrawReporter() {
var count = 0;
return {
onupdate: function () {
console.log('Redraws so far: ', ++count);
},
view: function () {},
};
}
m(RedrawReporter, { data: 'Hello' });
onbeforeremove
onbeforeremove(vnode)
hook は、DOM 要素がドキュメントから削除される前に呼び出されます。Promise が返された場合、Mithril.js はその Promise の完了を待ってから DOM 要素を削除します。
この hook は、その parentNode
を失う DOM 要素でのみ呼び出されますが、その子要素では呼び出されません。
他の hook と同様に、onbeforeremove
コールバックの this
キーワードは vnode.state
を指します。vnode に onbeforeremove
hook がある DOM 要素はリサイクルされません。
var Fader = {
onbeforeremove: function (vnode) {
vnode.dom.classList.add('fade-out');
return new Promise(function (resolve) {
setTimeout(resolve, 1000);
});
},
view: function () {
return m('div', 'Bye');
},
};
onremove
onremove(vnode)
hook は、DOM 要素がドキュメントから削除される前に呼び出されます。onbeforeremove
hook も定義されている場合、onremove
hook は onbeforeremove
から返された Promise が完了した後に実行されます。
この hook は、親要素から直接削除された場合でも、削除された別の要素の子要素として削除された場合でも、ドキュメントから削除されるすべての要素に対して呼び出されます。
他の hook と同様に、onremove
コールバックの this
キーワードは vnode.state
を指します。vnode に onremove
hook がある DOM 要素はリサイクルされません。
onremove
hook は、クリーンアップタスクを実行するのに役立ちます。
function Timer() {
var timeout = setTimeout(function () {
console.log('timed out');
}, 1000);
return {
onremove: function () {
clearTimeout(timeout);
},
view: function () {},
};
}
onbeforeupdate
onbeforeupdate(vnode, old)
hook は、vnode が更新によって差分処理される前に呼び出されます。この関数が定義され、false
を返した場合、Mithril.js はその vnode およびその子 vnode に対する差分処理を抑制します。
この hook 自体は、サブツリーがコンポーネント内にカプセル化されていない限り、仮想 DOM サブツリーの生成を防ぐものではありません。
他の hook と同様に、onbeforeupdate
コールバックの this
キーワードは vnode.state
を指します。
この hook は、DOM ツリーが過度に大きい場合に、更新の遅延を減らすのに役立ちます。
アンチパターンを避ける
Mithril.js は柔軟ですが、推奨されないコードパターンがいくつかあります。
時期尚早な最適化を避ける
onbeforeupdate
を使用して差分処理をスキップするのは、最終手段としてのみ使用してください。顕著なパフォーマンスの問題が発生した場合を除き、使用しないでください。
通常、onbeforeupdate
で修正できるパフォーマンスの問題は、1 つの大きなアイテムの配列に集約されます。このコンテキストでは、「大きい」とは、多数のノードを含む配列を指します。これは、広い範囲(悪名高い 5000 行に及ぶテーブルなど)または深く密なツリーのいずれかです。
パフォーマンスの問題が発生した場合は、まず UI が優れたユーザーエクスペリエンスを提供しているかどうかを検討し、そうでない場合は変更してください。たとえば、ユーザーが生データ 5000 行を直接閲覧するケースは稀であり、上位の最も関連性の高いアイテムのみを返す検索機能を使用する方がユーザーにとって簡単である可能性が非常に高いです。
設計ベースのソリューションが実現可能ではなく、多数の DOM 要素を持つ UI を最適化する必要がある場合は、最大の配列の親ノードに onbeforeupdate
を適用し、パフォーマンスを再評価します。ほとんどの場合、1 回のチェックで十分です。まれにそうでない場合は、繰り返しますが、新しい onbeforeupdate
宣言にはますます注意する必要があります。複数の onbeforeupdate
は、設計ワークフローにおける優先順位付けの問題を示唆する兆候です。
アプリケーションの他の領域に「念のため」最適化を適用しないでください。一般的に言って、コードが多いほど、コードが少ないよりも保守コストが高くなります。また、onbeforeupdate
関連のバグは、条件付きチェックにオブジェクトの同一性に依存している場合、特にトラブルシューティングが難しい可能性があることを忘れないでください。
繰り返しますが、onbeforeupdate
hook は、最終手段としてのみ使用してください。