From 42eefe3f5e7eed689c6ec595a7f2f487659e5e9e Mon Sep 17 00:00:00 2001 From: chb6734 Date: Mon, 23 Dec 2024 02:53:35 +0900 Subject: [PATCH 1/9] =?UTF-8?q?feat:=20VNode=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=ED=86=B5=EA=B3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/createVNode.js | 15 ++++++++++++++- src/lib/normalizeVNode.js | 8 ++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/lib/createVNode.js b/src/lib/createVNode.js index 9991337..ec984f4 100644 --- a/src/lib/createVNode.js +++ b/src/lib/createVNode.js @@ -1,3 +1,16 @@ export function createVNode(type, props, ...children) { - return {}; + console.log("log: ", children); + return { + type, + props, + children: children + .flat(Infinity) + .filter( + (value) => + value !== undefined && + value !== null && + value !== false && + value !== "", + ), + }; } diff --git a/src/lib/normalizeVNode.js b/src/lib/normalizeVNode.js index 7dc6f17..bf9538b 100644 --- a/src/lib/normalizeVNode.js +++ b/src/lib/normalizeVNode.js @@ -1,3 +1,11 @@ export function normalizeVNode(vNode) { + if ( + typeof vNode === "boolean" || + typeof vNode === "undefined" || + vNode === null + ) { + return ""; + } + console.log(vNode); return vNode; } From af45ad8c9088bbc026b5fd430e0665cebd325466 Mon Sep 17 00:00:00 2001 From: chb6734 Date: Wed, 25 Dec 2024 18:20:28 +0900 Subject: [PATCH 2/9] =?UTF-8?q?feat:=20normalizeVnode=20=EC=A0=95=EA=B7=9C?= =?UTF-8?q?=ED=99=94=20test=20=ED=86=B5=EA=B3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/normalizeVNode.js | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/lib/normalizeVNode.js b/src/lib/normalizeVNode.js index bf9538b..98dfd23 100644 --- a/src/lib/normalizeVNode.js +++ b/src/lib/normalizeVNode.js @@ -1,4 +1,6 @@ export function normalizeVNode(vNode) { + //null, undefined, boolean 처리 + if ( typeof vNode === "boolean" || typeof vNode === "undefined" || @@ -6,6 +8,29 @@ export function normalizeVNode(vNode) { ) { return ""; } - console.log(vNode); - return vNode; + // 문자열, 숫자 -> 문자열로 처리 + if (typeof vNode === "number" || typeof vNode === "string") { + return vNode.toString(); + } + // vNode가 function일 때 + if (typeof vNode === "object") { + if (typeof vNode.type === "function") { + return normalizeVNode( + vNode.type({ ...vNode.props, children: vNode.children }), + ); + } + if (Array.isArray(vNode.children)) { + return { + type: vNode.type, + props: { ...vNode.props }, + children: vNode.children.map((child) => normalizeVNode(child)), + }; + } + } + + return { + type: vNode.type, + props: vNode.props, + children: vNode.children, + }; } From 21e44b88273240ba89aa25b48007ca4d89b1e690 Mon Sep 17 00:00:00 2001 From: chb6734 Date: Wed, 25 Dec 2024 22:53:09 +0900 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20normalizeVnode=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=86=B5=EA=B3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/normalizeVNode.js | 41 +++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/lib/normalizeVNode.js b/src/lib/normalizeVNode.js index 98dfd23..48dbf7c 100644 --- a/src/lib/normalizeVNode.js +++ b/src/lib/normalizeVNode.js @@ -10,27 +10,34 @@ export function normalizeVNode(vNode) { } // 문자열, 숫자 -> 문자열로 처리 if (typeof vNode === "number" || typeof vNode === "string") { - return vNode.toString(); + return String(vNode); } // vNode가 function일 때 - if (typeof vNode === "object") { - if (typeof vNode.type === "function") { - return normalizeVNode( - vNode.type({ ...vNode.props, children: vNode.children }), - ); - } - if (Array.isArray(vNode.children)) { - return { - type: vNode.type, - props: { ...vNode.props }, - children: vNode.children.map((child) => normalizeVNode(child)), - }; - } + if (typeof vNode.type === "function") { + return normalizeVNode( + vNode.type({ + ...vNode.props, + children: vNode.children.filter( + (child) => child !== null && child !== undefined && child !== false, + ), + }), + ); + } + if (Array.isArray(vNode.children)) { + return { + type: vNode.type, + props: vNode.props, + children: vNode.children + .filter( + (child) => + child !== null && child !== undefined && typeof child !== "boolean", + ) + .map((child) => normalizeVNode(child)), + }; } return { - type: vNode.type, - props: vNode.props, - children: vNode.children, + ...vNode, + children: normalizeVNode(vNode.children), }; } From 33ffa5fa6b8bd7de5f00b30424fb8b01afab638d Mon Sep 17 00:00:00 2001 From: chb6734 Date: Thu, 26 Dec 2024 02:39:21 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feat:=20createElement=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=86=B5=EA=B3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/createElement.js | 55 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/src/lib/createElement.js b/src/lib/createElement.js index 5d39ae7..a5a20ce 100644 --- a/src/lib/createElement.js +++ b/src/lib/createElement.js @@ -1,5 +1,54 @@ -import { addEvent } from "./eventManager"; +//import { addEvent } from "./eventManager"; -export function createElement(vNode) {} +export function createElement(vNode) { + // undifined, null, boolean은 빈텍스트 노드로 변환 + if ( + typeof vNode === "boolean" || + vNode === null || + typeof vNode === "undefined" + ) { + return document.createTextNode(""); + } -function updateAttributes($el, props) {} + //string과 number은 텍스트 노드로 변환 + if (typeof vNode === "string" || typeof vNode === "number") { + return document.createTextNode(vNode); + } + + // vNode가 배열일 때 fragment로 리턴 + if (Array.isArray(vNode)) { + const fragment = document.createDocumentFragment(); + vNode.forEach((node) => { + console.log("test", node); + fragment.append(createElement(node)); + }); + return fragment; + } + // 컴포넌트를 createElement로 처리하려고 하면 오류 발생 + if (typeof vNode === "object" && typeof vNode === "function") { + throw new Error("error"); + } + + //컴포넌트 정규화 + const { type, props, children } = vNode; + const $el = document.createElement(type); + updateAttributes($el, props); + + if (children) { + children.forEach((child) => { + const childElement = createElement(child); + $el.append(childElement); + }); + } + return $el; +} + +function updateAttributes($el, props) { + Object.entries(props || {}).forEach(([key, value]) => { + if (key === "className") { + $el.classList = value; + return; + } + $el.setAttribute(key, value); + }); +} From a1ca31f85e307bf0999620a97c47c62205556378 Mon Sep 17 00:00:00 2001 From: chb6734 Date: Fri, 27 Dec 2024 08:13:37 +0900 Subject: [PATCH 5/9] =?UTF-8?q?feat:=20addEvent=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=86=B5=EA=B3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/eventManager.js | 43 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/src/lib/eventManager.js b/src/lib/eventManager.js index 24e4240..d488379 100644 --- a/src/lib/eventManager.js +++ b/src/lib/eventManager.js @@ -1,5 +1,42 @@ -export function setupEventListeners(root) {} +let events = new Map(); -export function addEvent(element, eventType, handler) {} +export function setupEventListeners(root) { + events.forEach((element) => { + element.forEach((eventType, handler) => { + root.addEventListener(handler, eventType); + console.log("===================================="); + console.log("root eVent : ", root); + console.log("===================================="); + }); + }); +} -export function removeEvent(element, eventType, handler) {} +//createElement("button") === createElement("button") 인가? +// ==> nope 각각 다른 객체 + +// 엘리먼트 별로 핸들러를 관리하도록 해보기 ==> 통과 + +// 엘리먼트별로 핸들러를 관리하는데 버블링 우째처리하노... +// 각각 처리되도록 해야하나...? + +export function addEvent(element, eventType, handler) { + if (!events.has(element)) { + events.set(element, new Map()); + } + + if (!events.get(element).has(eventType)) { + events.get(element).set(eventType, handler); + } +} + +export function removeEvent(element, eventType, handler) { + if (events.has(element) && events.get(element).has(eventType)) { + events.get(element).delete(eventType); + console.log("===================================="); + console.log("events: ", events.get(element).has(handler)); + console.log("===================================="); + if (events.get(element).size === 0) { + events.delete(element); + } + } +} From eef1fefe7046a2bb1fcc3a54685a3974c3e0ea01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B0=A8=ED=98=84=EB=B9=88?= Date: Fri, 27 Dec 2024 11:25:27 +0900 Subject: [PATCH 6/9] =?UTF-8?q?feat:=20renderElement=20=ED=95=98=EB=82=98?= =?UTF-8?q?=EB=9D=BC=EB=8F=84=20=ED=86=B5=EA=B3=BC...!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/eventManager.js | 12 ++++++++---- src/lib/renderElement.js | 13 ++++++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/lib/eventManager.js b/src/lib/eventManager.js index d488379..20ca75f 100644 --- a/src/lib/eventManager.js +++ b/src/lib/eventManager.js @@ -1,12 +1,16 @@ let events = new Map(); export function setupEventListeners(root) { + if (events.size > 0) { + events.forEach((element) => { + element.forEach((eventType, handler) => { + root.removeEventListener(handler, eventType); + }); + }); + } events.forEach((element) => { element.forEach((eventType, handler) => { root.addEventListener(handler, eventType); - console.log("===================================="); - console.log("root eVent : ", root); - console.log("===================================="); }); }); } @@ -33,7 +37,7 @@ export function removeEvent(element, eventType, handler) { if (events.has(element) && events.get(element).has(eventType)) { events.get(element).delete(eventType); console.log("===================================="); - console.log("events: ", events.get(element).has(handler)); + console.log("events: ", events.get(handler)); console.log("===================================="); if (events.get(element).size === 0) { events.delete(element); diff --git a/src/lib/renderElement.js b/src/lib/renderElement.js index 0429572..ec620fd 100644 --- a/src/lib/renderElement.js +++ b/src/lib/renderElement.js @@ -1,10 +1,21 @@ import { setupEventListeners } from "./eventManager"; import { createElement } from "./createElement"; import { normalizeVNode } from "./normalizeVNode"; -import { updateElement } from "./updateElement"; +//import { updateElement } from "./updateElement"; + +let oldNode = null; export function renderElement(vNode, container) { // 최초 렌더링시에는 createElement로 DOM을 생성하고 // 이후에는 updateElement로 기존 DOM을 업데이트한다. // 렌더링이 완료되면 container에 이벤트를 등록한다. + + const newNode = normalizeVNode(vNode); + if (oldNode === null) { + const element = createElement(newNode); + container.append(element); + oldNode = newNode; + setupEventListeners(container); + return; + } } From 2484f466d55fb65d8e34ac50fd18f250d055dd00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B0=A8=ED=98=84=EB=B9=88?= Date: Fri, 27 Dec 2024 15:07:26 +0900 Subject: [PATCH 7/9] =?UTF-8?q?feat:=20render=20=ED=95=98=EB=82=98?= =?UTF-8?q?=EB=9D=BC=EB=8F=84=20=EB=8D=94....?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/createElement.js | 8 ++++++++ src/lib/renderElement.js | 16 +++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/lib/createElement.js b/src/lib/createElement.js index a5a20ce..4bf634a 100644 --- a/src/lib/createElement.js +++ b/src/lib/createElement.js @@ -1,5 +1,7 @@ //import { addEvent } from "./eventManager"; +import { addEvent } from "./eventManager"; + export function createElement(vNode) { // undifined, null, boolean은 빈텍스트 노드로 변환 if ( @@ -49,6 +51,12 @@ function updateAttributes($el, props) { $el.classList = value; return; } + if (key.startsWith("on")) { + const eventType = key.slice(2).toLowerCase(); + const handler = props[key]; + addEvent($el, eventType, handler); + return; + } $el.setAttribute(key, value); }); } diff --git a/src/lib/renderElement.js b/src/lib/renderElement.js index ec620fd..baf4102 100644 --- a/src/lib/renderElement.js +++ b/src/lib/renderElement.js @@ -1,14 +1,12 @@ import { setupEventListeners } from "./eventManager"; import { createElement } from "./createElement"; import { normalizeVNode } from "./normalizeVNode"; -//import { updateElement } from "./updateElement"; +import { updateElement } from "./updateElement"; let oldNode = null; export function renderElement(vNode, container) { // 최초 렌더링시에는 createElement로 DOM을 생성하고 - // 이후에는 updateElement로 기존 DOM을 업데이트한다. - // 렌더링이 완료되면 container에 이벤트를 등록한다. const newNode = normalizeVNode(vNode); if (oldNode === null) { @@ -18,4 +16,16 @@ export function renderElement(vNode, container) { setupEventListeners(container); return; } + // 이후에는 updateElement로 기존 DOM을 업데이트한다. + + if (oldNode !== null) { + updateElement(container, newNode, oldNode); + oldNode = newNode; + } else { + const element = createElement(newNode); + container.append(element); + oldNode = newNode; + } + // 렌더링이 완료되면 container에 이벤트를 등록한다. + setupEventListeners(container); } From ec85beca262c368f00b0eb880b6fb3550590d4c2 Mon Sep 17 00:00:00 2001 From: chb6734 Date: Fri, 27 Dec 2024 21:23:24 +0900 Subject: [PATCH 8/9] =?UTF-8?q?feat:=20eventManger=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=86=B5=EA=B3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/eventManager.js | 73 ++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/src/lib/eventManager.js b/src/lib/eventManager.js index 20ca75f..e7014fd 100644 --- a/src/lib/eventManager.js +++ b/src/lib/eventManager.js @@ -1,18 +1,27 @@ let events = new Map(); +let oldRoot = null; + export function setupEventListeners(root) { - if (events.size > 0) { - events.forEach((element) => { - element.forEach((eventType, handler) => { - root.removeEventListener(handler, eventType); - }); - }); - } - events.forEach((element) => { - element.forEach((eventType, handler) => { - root.addEventListener(handler, eventType); - }); - }); + if (oldRoot !== root) { + oldRoot = root; + events.forEach((element) => { + element.forEach((eventType, handler) => { + root.addEventListener(handler, eventType); + }); + }); + } else { + events.forEach((element) => { + element.forEach((eventType, handler) => { + oldRoot.addEventListener(handler, eventType); + }); + }); + events.forEach((element) => { + element.forEach((eventType, handler) => { + root.addEventListener(handler, eventType); + }); + }); + } } //createElement("button") === createElement("button") 인가? @@ -24,23 +33,35 @@ export function setupEventListeners(root) { // 각각 처리되도록 해야하나...? export function addEvent(element, eventType, handler) { - if (!events.has(element)) { - events.set(element, new Map()); - } + if (!events.has(element)) { + events.set(element, new Map()); + } - if (!events.get(element).has(eventType)) { - events.get(element).set(eventType, handler); - } + if (!events.get(element).has(eventType)) { + events.get(element).set(eventType, handler); + } } export function removeEvent(element, eventType, handler) { - if (events.has(element) && events.get(element).has(eventType)) { - events.get(element).delete(eventType); - console.log("===================================="); - console.log("events: ", events.get(handler)); - console.log("===================================="); - if (events.get(element).size === 0) { - events.delete(element); + console.log(handler); + events.forEach((element) => { + element.forEach((eventType, handler) => { + oldRoot.removeEventListener(handler, eventType); + }); + }); + + if (events.has(element) && events.get(element).has(eventType)) { + events.get(element).delete(eventType); + + if (events.get(element).size === 0) { + events.delete(element); + } + + // 이벤트 다시 등록 + events.forEach((element) => { + element.forEach((eventType, handler) => { + oldRoot.addEventListener(handler, eventType); + }); + }); } - } } From 6ba0d49907694870a02b0d30de646a267aa2e63d Mon Sep 17 00:00:00 2001 From: chb6734 Date: Fri, 27 Dec 2024 21:28:00 +0900 Subject: [PATCH 9/9] =?UTF-8?q?refactor:=20eventManager=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/eventManager.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/lib/eventManager.js b/src/lib/eventManager.js index e7014fd..8132e77 100644 --- a/src/lib/eventManager.js +++ b/src/lib/eventManager.js @@ -3,6 +3,7 @@ let events = new Map(); let oldRoot = null; export function setupEventListeners(root) { + // 초기등록 로직 if (oldRoot !== root) { oldRoot = root; events.forEach((element) => { @@ -10,12 +11,15 @@ export function setupEventListeners(root) { root.addEventListener(handler, eventType); }); }); - } else { + } //이미 등록한적이 있을 때 + else { + //지우고 events.forEach((element) => { element.forEach((eventType, handler) => { - oldRoot.addEventListener(handler, eventType); + oldRoot.removeEventListener(handler, eventType); }); }); + //다시 등록 events.forEach((element) => { element.forEach((eventType, handler) => { root.addEventListener(handler, eventType); @@ -43,21 +47,23 @@ export function addEvent(element, eventType, handler) { } export function removeEvent(element, eventType, handler) { + // handler변수 안쓰지만 테스트 코드 바꾸기 귀찮아서 로그로 사용 console.log(handler); + //root에 저장된 모든 이벤트 삭제 events.forEach((element) => { element.forEach((eventType, handler) => { oldRoot.removeEventListener(handler, eventType); }); }); - + // Map에 저장된 해당 이벤트 삭제 if (events.has(element) && events.get(element).has(eventType)) { events.get(element).delete(eventType); - + // 만약 삭제 후 해당 엘리먼트에 어떤 이벤트도 없으면 엘리먼트도 삭제 if (events.get(element).size === 0) { events.delete(element); } - // 이벤트 다시 등록 + // root에 이벤트 다시 등록 events.forEach((element) => { element.forEach((eventType, handler) => { oldRoot.addEventListener(handler, eventType);