생명주기 메서드
사용법
컴포넌트와 가상 DOM 노드는 생명주기 메서드(또는 훅)를 가질 수 있으며, 이 훅은 DOM 요소의 생명주기 동안 특정 시점에 호출됩니다.
// 컴포넌트의 생명주기 훅 예시
var ComponentWithHook = {
oninit: function (vnode) {
console.log('컴포넌트 초기화');
},
view: function () {
return 'hello';
},
};
// vnode의 생명주기 훅 예시
function initializeVnode() {
console.log('vnode 초기화');
}
m(ComponentWithHook, { oninit: initializeVnode });
모든 생명주기 메서드는 vnode를 첫 번째 인수로 받으며, this
키워드는 vnode.state
에 바인딩됩니다.
생명주기 메서드는 m.render()
호출의 결과로만 호출됩니다. Mithril 외부에서 DOM이 수정되면 이 훅은 호출되지 않습니다.
DOM 요소 생명주기
DOM 요소는 일반적으로 생성되어 문서에 추가됩니다. 그 후 UI 이벤트가 발생하고 데이터가 변경될 때 속성이나 자식 노드가 업데이트될 수 있습니다. 또한 요소는 문서에서 제거될 수도 있습니다.
요소가 제거된 후에는 메모리 풀에 일시적으로 보관될 수 있습니다. 풀링된 요소는 이후 업데이트에서 재사용될 수 있습니다(이를 _DOM 재활용_이라고 함). 요소를 재활용하면 최근에 존재했던 요소의 복사본을 다시 생성하는 데 드는 성능 비용을 줄일 수 있습니다.
oninit
oninit(vnode)
훅은 가상 DOM 엔진이 vnode를 처리하기 전에 호출됩니다. oninit
은 DOM 요소가 문서에 연결되기 전에 실행되는 것이 보장되고, 부모 vnode가 자식 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('초기 높이: ', 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('다시 그리기 횟수: ', ++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('시간 초과');
}, 1000);
return {
onremove: function () {
clearTimeout(timeout);
},
view: function () {},
};
}
onbeforeupdate
onbeforeupdate(vnode, old)
훅은 업데이트 과정에서 vnode의 diff 연산이 수행되기 직전에 호출됩니다. 이 함수가 정의되어 있고 false
를 반환하면, Mithril.js는 해당 vnode와 그 자식 노드에 대한 diff 연산을 건너뜁니다.
이 훅 자체는 서브트리가 컴포넌트 내에 캡슐화되지 않는 한 가상 DOM 서브트리가 생성되는 것을 막지는 않습니다.
다른 훅과 마찬가지로 onbeforeupdate
콜백 내에서 this
키워드는 vnode.state
를 가리킵니다.
이 훅은 DOM 트리가 지나치게 클 경우 업데이트 지연을 줄이는 데 유용합니다.
안티 패턴 피하기
Mithril.js는 유연하지만 일부 코드 패턴은 권장되지 않습니다.
조기 최적화는 피해야 합니다
onbeforeupdate
를 사용하여 diff를 건너뛰는 것은 최후의 수단으로만 사용해야 합니다. 눈에 띄는 성능 문제가 있는 경우가 아니면 사용하지 마십시오.
일반적으로 onbeforeupdate
로 해결할 수 있는 성능 문제는 하나의 큰 배열로 귀결됩니다. 이 컨텍스트에서 "큰" 배열은 넓게 퍼져 있든(예: 5000행 테이블) 깊고 조밀한 트리이든 많은 수의 노드를 포함하는 배열을 의미합니다.
성능 문제가 발생하면 먼저 UI가 좋은 사용자 경험을 제공하는지 확인하고, 그렇지 않다면 변경해야 합니다. 예를 들어 사용자가 5000행의 원시 테이블 데이터를 직접 검색하는 것은 드물며, 상위 몇 개의 가장 관련성이 높은 항목만 반환하는 검색 기능을 사용하는 것이 사용자에게 더 나은 경험을 제공할 가능성이 높습니다.
설계 기반 솔루션이 불가능하고 많은 수의 DOM 요소로 UI를 최적화해야 하는 경우 가장 큰 배열의 부모 노드에 onbeforeupdate
를 적용하고 성능을 다시 평가하십시오. 대부분의 경우 단일 검사로 충분해야 합니다. 드물게 그렇지 않은 경우 반복해서 적용하되, 각 새 onbeforeupdate
선언에 대해 점점 더 주의해야 합니다. 여러 onbeforeupdate
는 설계 워크플로의 우선 순위 지정 문제를 나타내는 코드 스멜입니다.
애플리케이션의 다른 영역에는 '혹시나' 하는 생각으로 최적화를 적용하지 마십시오. 일반적으로 코드가 많을수록 유지 관리 비용이 더 많이 들고, 조건부 검사에 객체 ID를 사용하는 경우 onbeforeupdate
관련 버그는 특히 해결하기 어려울 수 있습니다.
다시 말하지만, onbeforeupdate
훅은 최후의 수단으로만 사용해야 합니다.