Методы жизненного цикла
Использование
Компоненты и узлы виртуального DOM могут иметь хуки жизненного цикла, которые вызываются в различные моменты времени существования 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)
вызывается до того, как виртуальный DOM-движок начнет обработку vnode. oninit
гарантированно запускается до того, как его DOM-элемент будет присоединен к документу, и он гарантированно запускается на родительских vnode до их дочерних элементов, но он не дает никаких гарантий относительно существования родительских или дочерних DOM-элементов. Вы не должны обращаться к vnode.dom
из метода oninit
.
Этот хук не вызывается при обновлении элемента, но он вызывается, если элемент перерабатывается.
Как и в других хуках, ключевое слово this
в обратном вызове oninit
указывает на 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()
из этого метода.
Этот хук не вызывается при обновлении элемента.
Как и в других хуках, ключевое слово this
в обратном вызове oncreate
указывает на vnode.state
. DOM-элементы, чьи vnode имеют хук oncreate
, не перерабатываются.
Хук 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()
из этого метода.
Этот хук вызывается только в том случае, если элемент существовал в предыдущем цикле рендеринга. Он не вызывается при создании элемента или при его переработке.
DOM-элементы, чьи vnode имеют хук onupdate
, не перерабатываются.
Хук 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 отсоединит DOM-элемент только после разрешения promise.
Этот хук вызывается только для DOM-элемента, который теряет свой parentNode
, но не вызывается для его дочерних элементов.
Как и в других хуках, ключевое слово this
в обратном вызове onbeforeremove
указывает на vnode.state
. DOM-элементы, чьи vnode имеют хук onbeforeremove
, не перерабатываются.
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
запускается после разрешения promise, возвращенного из onbeforeremove
.
Этот хук вызывается для любого элемента, который удаляется из документа, независимо от того, был ли он непосредственно отсоединен от своего родителя или является дочерним элементом другого элемента, который был отсоединен.
Как и в других хуках, ключевое слово this
в обратном вызове onremove
указывает на vnode.state
. DOM-элементы, чьи vnode имеют хук onremove
, не перерабатываются.
Хук onremove
полезен для выполнения задач очистки.
function Timer() {
var timeout = setTimeout(function () {
console.log('timed out');
}, 1000);
return {
onremove: function () {
clearTimeout(timeout);
},
view: function () {},
};
}
onbeforeupdate
Хук onbeforeupdate(vnode, old)
вызывается непосредственно перед этапом сравнения (diffing) виртуального DOM при обновлении. Если эта функция определена и возвращает false, Mithril.js предотвращает сравнение vnode и, следовательно, дочерних элементов vnode.
Сам по себе этот хук не предотвращает создание поддерева виртуального DOM, если поддерево не инкапсулировано внутри компонента.
Как и в других хуках, ключевое слово this
в обратном вызове onbeforeupdate
указывает на vnode.state
.
Этот хук полезен для уменьшения задержки в обновлениях в случаях, когда существует чрезмерно большое DOM-дерево.
Избегайте антипаттернов
Хотя Mithril.js является гибким, некоторые шаблоны кода не рекомендуются:
Избегайте преждевременных оптимизаций
Вы должны использовать onbeforeupdate
для пропуска сравнения только в крайнем случае. Избегайте его использования, если у вас нет заметных проблем с производительностью.
Как правило, проблемы с производительностью, которые можно исправить с помощью onbeforeupdate
, сводятся к одному большому массиву элементов. В этом контексте обычно "большой" означает любой массив, содержащий большое количество узлов, будь то широкая плоская структура (например, таблица на 5000 строк) или глубокое, плотное дерево.
Если у вас есть проблема с производительностью, сначала подумайте, обеспечивает ли UI хороший пользовательский опыт, и измените его, если это не так. Например, маловероятно, что пользователь когда-либо просмотрит 5000 строк необработанных данных таблицы, и весьма вероятно, что пользователю будет проще использовать функцию поиска, которая возвращает только несколько наиболее релевантных элементов.
Если решение, основанное на дизайне, невозможно, и вы должны оптимизировать UI с большим количеством DOM-элементов, примените onbeforeupdate
к родительскому узлу самого большого массива и повторно оцените производительность. В большинстве случаев одной проверки должно быть достаточно. В редких случаях, когда это не так, продолжайте применять этот подход, но вам следует все больше опасаться каждой новой декларации onbeforeupdate
. Множественное использование onbeforeupdate
может указывать на потенциальные проблемы в архитектуре или процессе проектирования, требующие дополнительного анализа.
Избегайте применения оптимизации к другим областям вашего приложения "на всякий случай". Помните, что, вообще говоря, больше кода влечет за собой более высокие затраты на обслуживание, чем меньше кода, а ошибки, связанные с onbeforeupdate
, могут быть особенно трудными для устранения, если вы полагаетесь на сравнение объектов для его условий проверки.
Хук onbeforeupdate
следует использовать только в крайнем случае.