生命周期方法
用法
組件和虛擬 DOM 節點可以擁有生命週期方法,也稱為 hooks(鉤子),它們會在 DOM 元素生命週期的不同階段被調用。
// 組件中的範例鉤子
var ComponentWithHook = {
oninit: function (vnode) {
console.log('initialize component');
},
view: function () {
return 'hello';
},
};
// vnode 中的範例鉤子
function initializeVnode() {
console.log('initialize vnode');
}
m(ComponentWithHook, { oninit: initializeVnode });
所有生命週期方法都會收到 vnode 作為它們的第一個參數,並且它們的 this
關鍵字會綁定到 vnode.state
。
生命週期方法僅作為 m.render()
呼叫的副作用而被調用。 如果 DOM 在 Mithril 之外被修改,它們不會被調用。
DOM 元素生命週期
DOM 元素通常會被建立並附加到文件中。 然後,當 UI 事件被觸發且資料被更改時,它可能會更新屬性或子節點;並且該元素也可能會從文件中移除。
在元素被移除後,它可能會暫時保留在記憶體緩衝池中。 已池化的元素可能會在後續更新中被重複使用(在一個稱為 DOM 回收 的過程中)。 回收元素可以避免產生重新建立最近存在的元素副本的效能開銷。
oninit
oninit(vnode)
鉤子會在 vnode 被虛擬 DOM 引擎處理之前被調用。 oninit
保證在其 DOM 元素附加到文件之前執行,並且保證在父 vnode 的子節點之前執行,但它不提供關於祖先或後代 DOM 元素存在的任何保證。 您絕對不應從 oninit
方法訪問 vnode.dom
。
當元素被更新時,這個鉤子不會被調用,但如果元素被回收,它會被調用。
與其他鉤子一樣,oninit
回調中的 this
關鍵字指向 vnode.state
。
oninit
鉤子對於基於透過 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)
鉤子會在 DOM 元素被建立並附加到文件後被調用。 oncreate
保證在渲染週期的結束時執行,因此從這個方法讀取版面配置數值(例如 vnode.dom.offsetHeight
和 vnode.dom.getBoundingClientRect()
)是安全的。
當元素被更新時,這個鉤子不會被調用。
與其他鉤子一樣,oncreate
回調中的 this
關鍵字指向 vnode.state
。 其 vnode 具有 oncreate
鉤子的 DOM 元素不會被回收。
oncreate
鉤子對於讀取可能觸發重繪的版面配置數值、啟動動畫以及初始化需要引用 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)
鉤子會在 DOM 元素被更新時調用,同時附加到文件。 onupdate
保證在渲染週期的結束時執行,因此從這個方法讀取版面配置數值(例如 vnode.dom.offsetHeight
和 vnode.dom.getBoundingClientRect()
)是安全的。
只有當元素存在於先前的渲染週期中時,才會調用這個鉤子。 當元素被建立或回收時,它不會被調用。
其 vnode 具有 onupdate
鉤子的 DOM 元素不會被回收。
onupdate
鉤子對於讀取可能觸發重繪的版面配置數值,以及在模型資料被更改後動態更新第三方函數庫中影響 UI 的狀態非常有用。
function RedrawReporter() {
var count = 0;
return {
onupdate: function () {
console.log('Redraws so far: ', ++count);
},
view: function () {},
};
}
m(RedrawReporter, { data: 'Hello' });
onbeforeremove
onbeforeremove(vnode)
鉤子會在 DOM 元素從文件中分離之前被調用。 如果返回一個 Promise,Mithril.js 僅在 promise 完成後分離 DOM 元素。
這個鉤子僅在其 parentNode
丟失的 DOM 元素上被調用,但它不會在其子元素中被調用。
與其他鉤子一樣,onbeforeremove
回調中的 this
關鍵字指向 vnode.state
。 其 vnode 具有 onbeforeremove
鉤子的 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)
鉤子會在 DOM 元素從文件中移除之前被調用。 如果也定義了 onbeforeremove
鉤子,則 onremove
鉤子會在從 onbeforeremove
返回的 promise 完成後執行。
這個鉤子會在從文件中移除的任何元素上被調用,無論它是直接從其父元素分離,還是它是被分離的另一個元素的子元素。
與其他鉤子一樣,onremove
回調中的 this
關鍵字指向 vnode.state
。 其 vnode 具有 onremove
鉤子的 DOM 元素不會被回收。
onremove
鉤子對於執行清理任務非常有用。
function Timer() {
var timeout = setTimeout(function () {
console.log('timed out');
}, 1000);
return {
onremove: function () {
clearTimeout(timeout);
},
view: function () {},
};
}
onbeforeupdate
onbeforeupdate(vnode, old)
鉤子會在 vnode 在更新中被差異比較之前被調用。 如果定義了這個函數並且返回 false,Mithril.js 會阻止對 vnode 進行差異比較,因此也會阻止對 vnode 的子節點進行差異比較。
除非子樹被封裝在組件中,否則這個鉤子本身不會阻止虛擬 DOM 子樹的生成。
與其他鉤子一樣,onbeforeupdate
回調中的 this
關鍵字指向 vnode.state
。
在存在過大的 DOM 樹的情況下,這個鉤子對於減少更新中的延遲非常有用。
避免反模式
雖然 Mithril.js 具有彈性,但有些程式碼模式是不鼓勵的:
避免過早優化
您應該僅在最後的手段下使用 onbeforeupdate
來跳過差異比較。 除非您有明顯的效能問題,否則請避免使用它。
通常,可以透過 onbeforeupdate
修正的效能問題可以歸結為一個大型項目陣列。 在這種情況下,通常“大型”表示包含大量節點的任何陣列,無論是在廣泛的分布中(著名的 5000 行表格),還是在深度、密集的樹中。
如果您確實遇到效能問題,首先請考慮 UI 是否提供了良好的使用者體驗,如果沒有,請變更它。 例如,使用者不太可能篩選 5000 行原始表格資料,並且使用者更有可能使用僅返回前幾個最相關項目的搜尋功能。
如果基於設計的解決方案不可行,並且您必須優化具有大量 DOM 元素的 UI,請將 onbeforeupdate
應用於最大陣列的父節點,然後重新評估效能。 在絕大多數情況下,單一檢查應該足夠。 在極少數情況下,如果不是,請重複執行,但您應該越來越警惕每個新的 onbeforeupdate
宣告。 多個 onbeforeupdate
是一種程式碼壞味道,表示設計工作流程中存在優先級問題。
避免將優化應用於應用程式的其他區域“以防萬一”。 請記住,一般来说,更多的程式碼會產生比更少的程式碼更高的維護負擔,並且如果您依賴物件識別進行條件檢查,則與 onbeforeupdate
相關的錯誤可能特別難以除錯。
再次強調,onbeforeupdate
鉤子應該僅作為最後的手段使用。