처음부터 간단한 가상 DOM을 설명하고 생성했습니다.
31045 단어 reactjavascripthtmlbeginners
돔은 무엇입니까?
DOM(Document Object Model)은 프로그래머와 사용자가 문서를 보다 쉽게 볼 수 있도록 구조화된 계층 방식으로 웹 페이지를 표현하는 방법입니다. DOM을 사용하면 문서 개체에서 제공하는 명령이나 메서드를 사용하여 태그, ID, 클래스, 속성 또는 요소에 쉽게 액세스하고 조작할 수 있습니다.
객체 모델이라고 부르는 이유는 무엇입니까?
문서는 객체를 사용하여 모델링되며, 모델에는 문서의 구조뿐만 아니라 문서의 동작과 HTML의 속성을 가진 유사한 태그 요소로 구성된 객체가 포함됩니다.
DOM의 구조:
DOM은 트리 또는 포리스트(둘 이상의 트리)로 생각할 수 있습니다. 구조 모델이라는 용어는 때때로 문서의 트리형 표현을 설명하는 데 사용됩니다. DOM 구조 모델의 중요한 속성 중 하나는 구조적 동형입니다. 동일한 문서의 표현을 생성하기 위해 두 개의 DOM 구현을 사용하는 경우 정확히 동일한 개체 및 관계를 사용하여 동일한 구조 모델을 생성합니다.
More information
가상 DOM이란 무엇입니까?
가상 DOM은 개체의 실제 DOM 요소를 메모리 내에서 표현한 것입니다. 예시:
const myButton = {
tagName: 'button',
attrs: {
id: 'btn',
class: 'save-btn'
},
children: ['save']
};
html 등가물
<button id="btn" class="save-btn">save</button>
이 모든 것을 이해하고 시작합시다 😊
요소를 나타내는 객체를 생성하고 이 객체를 반환하는 함수가 필요합니다.
// createElement.js
function createElement(tagName, { attrs = {}, children = [] } = {}){
return {
tagName,
attrs,
children
}
}
export default createElement;
이제 요소를 렌더링하는 함수를 만들어야 합니다.
// render.js
function render({ tagName, attrs = {}, children = [] }){
let element = document.createElement(tagName);
// insert all children elements
children.forEach( child => {
if (typeof child === 'string'){
// if the children is a kind of string create a text Node object
element.appendChild(document.createTextNode(child));
}
else {
// repeat the process with the children elements
element.appendChild(render(child));
}
});
// if it has attributes it adds them to the element
if (Object.keys(attrs).length){
for (const [key, value] of Object.entries(attrs)) {
element.setAttribute(key, value);
}
}
return element;
};
export default render;
그런 다음 요소를 DOM에 삽입하는 함수를 만듭니다.
// insert.js
function insertElement(element, domElement){
domElement.replaceWith(element);
return element;
}
export default insertElement;
이제 도구가 준비되었으니 사용해 봅시다!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>my vDOM</title>
</head>
<body>
<div id="root">
</div>
<script src="./main.js" type="module"></script>
</body>
</html>
// main.js
import createElement from './createElement.js';
import render from './render.js';
import insertElement from './insert.js';
let myVirtualElement = createElement("div", {
attrs: { id: "container" },
children: [
createElement("p", {
attrs: { id: "text" },
children: ["hello world"],
}),
]
});
let element = render(myVirtualElement);
let rootElemet = insertElement(element, document.querySelector('#root'));
모든 웹 서버에서 이것을 실행하십시오. 나는 vscode에서 라이브 서버로 실행합니다.
우리는 그것을 얻었다! 🥳
이제 Post에서 만든 가상 요소 사이의 차이를 만드는 알고리즘을 사용하여 더 흥미롭게 만들 수 있습니다.
// diff.js
import render from './render.js';
const zip = (xs, ys) => {
const zipped = [];
for (let i = 0; i < Math.max(xs.length, ys.length); i++) {
zipped.push([xs[i], ys[i]]);
}
return zipped;
};
const diffAttrs = (oldAttrs, newAttrs) => {
const patches = [];
// set new attributes
for (const [k, v] of Object.entries(newAttrs)) {
patches.push($node => {
$node.setAttribute(k, v);
return $node;
});
}
// remove old attributes
for (const k in oldAttrs) {
if (!(k in newAttrs)) {
patches.push($node => {
$node.removeAttribute(k);
return $node;
});
}
}
return $node => {
for (const patch of patches) {
patch($node);
}
};
};
const diffChildren = (oldVChildren, newVChildren) => {
const childPatches = [];
oldVChildren.forEach((oldVChild, i) => {
childPatches.push(diff(oldVChild, newVChildren[i]));
});
const additionalPatches = [];
for (const additionalVChild of newVChildren.slice(oldVChildren.length)) {
additionalPatches.push($node => {
$node.appendChild(render(additionalVChild));
return $node;
});
}
return $parent => {
for (const [patch, child] of zip(childPatches, $parent.childNodes)) {
patch(child);
}
for (const patch of additionalPatches) {
patch($parent);
}
return $parent;
};
};
const diff = (vOldNode, vNewNode) => {
if (vNewNode === undefined) {
return $node => {
$node.remove();
return undefined;
};
}
if (typeof vOldNode === 'string' || typeof vNewNode === 'string') {
if (vOldNode !== vNewNode) {
return $node => {
const $newNode = render(vNewNode);
$node.replaceWith($newNode);
return $newNode;
};
} else {
return $node => undefined;
}
}
if (vOldNode.tagName !== vNewNode.tagName) {
return $node => {
const $newNode = render(vNewNode);
$node.replaceWith($newNode);
return $newNode;
};
}
const patchAttrs = diffAttrs(vOldNode.attrs, vNewNode.attrs);
const patchChildren = diffChildren(vOldNode.children, vNewNode.children);
return $node => {
patchAttrs($node);
patchChildren($node);
return $node;
};
};
export default diff;
이제 우리는 main.js를 변경합니다
// main.js
import createElement from './createElement.js';
import render from './render.js';
import insertElement from './insert.js';
import diff from './diff.js';
let myElement = createElement('div', {
attrs: { class: 'container'},
children: [createElement('img', {
attrs: { id: 'img', src: 'https://i.picsum.photos/id/1/200/300.jpg' },
children: []
})]
})
let element = render(myElement);
let rootElemet = insertElement(element, document.querySelector('#root'));
let count = 0;
setInterval(()=> {
count += 1;
let myVirtualElemet = createElement('div', {
attrs: { class: 'img'},
children: [createElement('img', {
attrs: { id: 'img', src: `https://i.picsum.photos/id/${count}/200/300.jpg` },
children: []
})]
})
const patch = diff(myElement, myVirtualElemet);
rootElemet = patch(rootElemet);
myElement = myVirtualElemet;
}, 1000);
실행 🤞
우리는 그것을 얻었다! 🥳
매초 링크 내부의 새 ID로 src 속성을 변경하여 DOM의 변경 사항을 업데이트하고 적용합니다.
영어를 못해서 글을 너무 못써서 죄송합니다
Reference
이 문제에 관하여(처음부터 간단한 가상 DOM을 설명하고 생성했습니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/buttercubz/explained-and-created-a-simple-virtual-dom-from-scratch-5765
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여(처음부터 간단한 가상 DOM을 설명하고 생성했습니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/buttercubz/explained-and-created-a-simple-virtual-dom-from-scratch-5765텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)