가상돔의 실체는 무엇인가 대해서 조사해보자.
Virtual DOM은 무엇인가요?Virtual DOM (VDOM)은 UI의 이상적인 또는 “가상”적인 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 “실제” DOM과 동기화하는 프로그래밍 개념입니다. 이 과정을 재조정이라고 합니다. |
"가상"적인 표현은 리엑트 객체내에 저장하든지, 아니면 DOM에 저장될지 둘중 하나일 것이다.
React.render를 하기 전과 후를 Object.keys로 비교해본다.
결과: 언더바(_)로 시작하는 네가지가 $0의 새 구성원이 된것을 확인 할 수 있다.
c98pj33.. 하는 부분은 첫 render를 할 때마다 랜덤하게 부여되고 재 호출시에는 유지되는 결과로 볼 때.
저들중 한놈, "가상"적인 표현을 저장하는놈이 있을지도 모른다라는 느낌을 받는다.
마우스 왼쪽을 딸깍거린지 몇십 초 후.
느낌이 적중했는지, FiberNode 라는 놈이 토픽의 주인공인거 같다.
트리비스무리 한 구조였다.
자식노드들이 동일선상에 있는게 아니라, 첫자식, 나머지자식들 형태로 분리 하였다.
그리고 alternate라는게 있어 DOM의 속성을 변경하면 번갈아 가면서 저장하므로
변경되기 이전 값을 항상 가지고있다.
만약 0부터 1씩 올리면서 변경 하면, alternate는 항상 짝수에 해당하는 값이 저장된다.
그리고 중요한 부분인 StateNode는 해당하는 DOM을 가리키고 있다.
다시 render 할 때, 값 변경되지 않는다면 DOM을 재사용하고 있다는 결론에 다다를 것이다.
실험1
function Example() {
const [count, setCount] = React.useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
const render = () => {
ReactDOM.render(
React.createElement(Example),
document.getElementById('root')
);
}
render();
document.getElementById('bt').addEventListener("click", () => {
const p = document.querySelector('p');
document.body.appendChild(p);
render();
})
결과 : bt를 클릭하고 Click me를 클릭 했을 때, 새로운 DOM을 생성하지 않고 집나간 P가 갱신되는 모습을 보여준다.
공식홈의 내용을 살펴보면, 휴리스틱한 알고리즘을 사용하므로
엘리멘트 타입이 바뀌지 않는 이상, 그것의 속성값만 변경한다고 한다.
그러니 가출하여 다른노드의 자식이 된다한들 크게 문제삼지 않는다.
정리를 하자면,
리엑트 엘리멘트(React.CreateElement)는 트리구조를 정의하기 위한 선언부이고,
실제로 관리되어지는 값은 DOM의 프로퍼티에 존재한다.
이런한 사실을 바탕으로 소스작성.
class Fnode {
constructor() {
this.dom = null;
this.child = null;
this.prop = null;
this.type = null;
}
}
function rmNode(node) {
if (node.dom) node.dom.remove();
if (!node.child) return;
node.child.forEach(element => {
rmNode(element)
});
}
function fnodeLoad(node, ele, parent_dom) {
if (typeof ele != 'object') {
const type = '$' + ele;
if (type == node.type) return;
if (!node.dom) {
node.dom = document.createTextNode('');
parent_dom.appendChild(node.dom);
}
node.dom.textContent = ele;
node.type = type;
return;
}
if (ele.type != node.type) {
node.type = ele.type;
let newdom = null;
if (typeof ele.type != 'function') {
newdom = document.createElement(ele.type);
if (node.dom) {
parent_dom.insertBefore(newdom, node.dom)
} else {
parent_dom.appendChild(newdom);
}
}
rmNode(node);
node.dom = newdom;
if (newdom) node.dom._node = node;
node.child = [];
}
if (typeof ele.type == 'function') {
ele.child.push(ele.type.call(null, ele.prop));
} else {
node.prop = node.prop || {};
Object.keys(node.prop).filter(k => !ele.prop[k]).forEach(key => {
node.dom.removeAttribute(key);
});
Object.keys(ele.prop).forEach(key => {
if (node.prop[key] != ele.prop[key]) {
node.dom.setAttribute(key, ele.prop[key]);
}
});
node.prop = ele.prop;
parent_dom = node.dom;
}
if (!ele.child) return;
for (var i = 0; i < ele.child.length; i++) {
if (node.child.length <= i) node.child.push(new Fnode())
fnodeLoad(node.child[i], ele.child[i], parent_dom)
}
node.child.slice(i).forEach(element => {
rmNode(element)
});
node.child = node.child.slice(0, i)
}
const MyRT = {
createElement: function (type, prop, child) {
if (!type) throw new Error("type Error");
prop = prop || {};
if (typeof prop !== 'object') throw new Error("Attribute Error");
if (arguments.length > 2) child = [...arguments].slice(2);
child = child || []
const element = { type, prop, child }
return element;
},
render: function (ele, dom) {
if (dom && typeof dom != 'object') throw new Error(dom + "is not an object");
dom._rootNode = dom._rootNode || new Fnode();
fnodeLoad(dom._rootNode, ele, dom)
}
}
짧고 간결하게 핵심적인 부분만 이해하기 쉽게 하려고 하였으나 많이 길어졌다.
테스트 소스.
function Example(prop) {
if (!prop.cnt) return "";
const color = prop.color % 360;
var ele = MyRT.createElement(
"span",
{style : `background-color: hsl(${color},100%,70%)`},
prop.cnt & 1,
MyRT.createElement(Example, { cnt: prop.cnt >> 1 ,color: (color+10) })
);
return ele;
}
var cnt = 0;
setInterval(() => {
cnt++;
MyRT.render(MyRT.createElement(Example, { cnt, color:cnt*10 }),
document.getElementById('root'));
}, 50);
동작확인.
'React' 카테고리의 다른 글
JSX TO JS 변환 구현 (0) | 2021.11.24 |
---|---|
useState 동작실험 및 구현 (0) | 2021.11.10 |