전체코드 보기(https://github.com/ujw0712/virtual-dom-with-js)
📚 Virtual DOM을 구현하기 위한 정리 📚
- React Element
- JSX vs JS
- Virtual DOM
React Element
- React 앱의 가장 작은 단위.
- 컴포넌트의 구성 요소.
- 브라우저 DOM element 와 다름.
- Document안의 모든 객체가 상속하는 제일 범용적인 기반 클래스.
- 공통 메서드와 속성만 가짐.
- 특정 요소를 더 상세하게 표현하는 클래스가 element를 상속.
ex. HTMLElement 인터페이스 : HTML 요소의 기반 인터페이스.
ex. SVGElement 인터페이스 : 모든 SVG 요소의 기초.
Root DOM Node
- React로 구현된 애플리케이션은 일반적으로 하나의 루트(root) DOM 노드가 있음.
- 이 안에 들어가는 모든 element를 React Dom 에서 관리함.
<div id="root"></div>
- React element를 루트 DOM 노드에 렌더링하려면 둘 다 ReactDOM.render()로 전달.
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
JSX vs JS
- JSX : JavaScript를 확장한 문법. React element를 생성함.
- 각 JSX element 는
React.createElement(component, props, ...children)
를 호출하기 위한 Syntax Sugar.
- JSX 구문 자체는 브라우저에서 읽을 수 없음. 일반 JavaScript로 변환해야 함.
- Babel
: JSX를 React.createElement 함수를 호출하는 JavaScript로 변환.
class Hello extends React.Component {
render() {
return <div>Hello {this.props.toWhat}</div>;
}
}
ReactDOM.render(
<Hello toWhat="World" />,
document.getElementById('root')
);
class Hello extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.toWhat}`);
}
}
ReactDOM.render(
React.createElement(Hello, {toWhat: 'World'}, null),
document.getElementById('root')
);
<ul className="list">
<li>item 1</li>
<li>item 2</li>
</ul>
React.createElement('ul', { className: 'list' },
React.createElement('li', {}, 'item 1'),
React.createElement('li', {}, 'item 2'),
);
Virtual DOM (VDOM)
1.DOM Tree 표현하기.
- DOM Tree를 JavaScript Object(Virtual DOM)로 메모리에 저장하기.
<ul class="list">
<li>item 1</li>
<li>item 2</li>
</ul>
{
type: 'ul', props: { 'class': 'list' }, children: [
{ type: 'li', props: {}, children: ['item 1'] },
{ type: 'li', props: {}, children: ['item 2'] }
]
}
function h(type, props, ...children) {
return { type, props, children: children.flat() }
}
h('ul', { 'class': 'list' },
h('li', {}, 'item 1'),
h('li', {}, 'item 2'),
);
const a = (
<ul className="list">
<li>item 1</li>
<li>item 2</li>
</ul>
);
const a = (
h('ul', { className: 'list' },
h('li', {}, 'item 1'),
h('li', {}, 'item 2'),
);
);
const a = (
{ type: 'ul', props: { className: 'list' }, children: [
{ type: 'li', props: {}, children: ['item 1'] },
{ type: 'li', props: {}, children: ['item 2'] }
] }
);
2. JavaScript Object(Virtual DOM)로 Real DOM 표현하기
- Virtual DOM을 Real DOM으로 변경하기.
- 아래 코드의 3가지 규칙.
- 모든 변수를 "$"로 시작하는 Real DOM(element, text node)로 작성.
- Virtual DOM 표현은 node 변수에 포함.
- 하나의 root node만 가짐. 모든 다른 node는 root node 안에 위치.
- props은 제외. (속성은 Virtual DOM의 기본 개념을 이해하는데 필요하지 않음)
function createElement(node) {
if (typeof node === 'string') {
return document.createTextNode(node);
}
const $el = document.createElement(node.type);
node.children
.map(createElement)
.forEach($el.appendChild.bind($el));
return $el;
}
3. 변경사항 처리
- Virtual Tree의 변화 감지.
- 뷰(HTML)에 변화가 있을 때, 구 가상돔(Old Node)과 새 가상돔(New Node)을 비교하여 변경된 내용만 DOM에 적용.
function changed(node1, node2) {
return typeof node1 !== typeof node2 ||
typeof node1 === 'string' && node1 !== node2 ||
node1.type !== node2.type
}
function updateElement($parent, newNode, oldNode, index = 0) {
if (!oldNode) {
$parent.appendChild(
createElement(newNode)
);
} else if (!newNode) {
$parent.removeChild(
$parent.childNodes[index]
);
}
} else if (changed(newNode, oldNode)) {
$parent.replaceChild(
createElement(newNode),
$parent.childNodes[index]
);
} else if (newNode.type) {
const newLength = newNode.children.length;
const oldLength = oldNode.children.length;
for (let i = 0; i < newLength || i < oldLength; i++) {
updateElement(
$parent.childNodes[index],
newNode.children[i],
oldNode.children[i],
i
);
}
}
}
전체코드 보기(https://github.com/ujw0712/virtual-dom-with-js)