Skip to content

Commit

Permalink
Merge pull request #7 from zigsong/main
Browse files Browse the repository at this point in the history
[Virtual DOM 만들기] 지그(송지은) 미션 제출합니다.
  • Loading branch information
yujo11 authored Oct 26, 2021
2 parents 60b4095 + 2272212 commit 4b8e57b
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 24 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.cache/
dist/
node_modules/
12 changes: 6 additions & 6 deletions STEP1-REQUIREMENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

### 필수 요구사항

- [ ] VirtualDOM 생성 팩토리 함수 구현 (aka createElement)
- [ ] 렌더러 구현
- [ ] 직접 구현한 렌더러와 VirtualDOM으로 Counter App 렌더링
- [ ] DOM Node 비교
- [ ] 업데이트 DOM Node
- [ ] 렌더링한 Counter App 동작시키기
- [x] VirtualDOM 생성 팩토리 함수 구현 (aka createElement)
- [x] 렌더러 구현
- [x] 직접 구현한 렌더러와 VirtualDOM으로 Counter App 렌더링
- [x] DOM Node 비교
- [x] 업데이트 DOM Node
- [x] 렌더링한 Counter App 동작시키기

### 심화 요구사항

Expand Down
1 change: 1 addition & 0 deletions index.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ body {

.btn-group {
border-radius: var(--main-border-radius);
margin-bottom: 1rem;
}

.btn-group button {
Expand Down
21 changes: 3 additions & 18 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,11 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="./index.css" rel="stylesheet" />
<title>Simple Counter</title>
<title>Zig Counter</title>
<script type="module" src="./src/index.js" defer></script>
</head>

<body>
<div id="root">
<div class="container">
<span class="count">0</span>

<div class="btn-group">
<button>
<strong>-</strong>
</button>
<button>
<strong>RESET</strong>
</button>
<button>
<strong>+</strong>
</button>
</div>
</div>
</div>
<div id="root"></div>
</body>
</html>
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "javascript-own-ui-library",
"version": "1.0.0",
"main": "index.js",
"repository": "https://github.com/zigsong/javascript-own-ui-library.git",
"author": "zigsong <wldms5764@gmail.com>",
"license": "MIT",
"scripts": {
"build": "parcel build index.html"
}
}
34 changes: 34 additions & 0 deletions src/components/Counter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Zig from '../lib/zig-react';

const Counter = () => {
const [getCount, setCount] = Zig.useState(0);

const decrease = () => {
setCount(getCount() - 1);
};

const increase = () => {
setCount(getCount() + 1);
};

const reset = () => {
setCount(0);
};

return () =>
Zig.createElement(
'div',
{ className: 'container' },
Zig.createElement('span', { className: 'count' }, getCount()),
Zig.createElement(
'div',
{ className: 'btn-group' },
Zig.createElement('button', { id: 'decrement', onclick: decrease }, Zig.createElement('strong', {}, '-')),
Zig.createElement('button', { id: 'reset', onclick: reset }, Zig.createElement('strong', {}, 'RESET')),
Zig.createElement('button', { id: 'increment', onclick: increase }, Zig.createElement('strong', {}, '+'))
),
'이러다 다 죽어~! 🔫'
);
};

export default Counter;
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import ZigDom from './lib/zig-react-dom';
import Counter from './components/Counter';

ZigDom.render(Counter(), document.getElementById('root'));
43 changes: 43 additions & 0 deletions src/lib/reconciliate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
let isMarked = false;

const isPrimitive = (test) => test !== Object(test);

const deepEqual = (prevDom, newDom) => {
if (isPrimitive(prevDom) && isPrimitive(newDom)) {
return prevDom === newDom;
}

if (prevDom.type !== newDom.type) {
return false;
}

if (Object.keys(prevDom)?.length != Object.keys(newDom)?.length) {
return false;
}

for (const key in prevDom) {
if (newDom.hasOwnProperty(key)) {
if (!deepEqual(prevDom[key], newDom[key])) {
// element type이라면
if (newDom.type && !isMarked) {
newDom['marked'] = true;
isMarked = true;
}
return false;
}
}
}

return true;
};

const reconciliate = (prevDom, newDom) => {
isMarked = false;

const markedDom = { ...newDom };
const isEqual = deepEqual(prevDom, markedDom);

return { isEqual, markedDom };
};

export default reconciliate;
80 changes: 80 additions & 0 deletions src/lib/zig-react-dom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import reconciliate from './reconciliate';

const ZigDom = (function () {
let currDom;
let domFactory;
let root;

const createDom = (element, container) => {
const { type, props } = element;

const fragment = document.createDocumentFragment();
const initialNode = type === 'text' ? document.createTextNode('') : document.createElement(type);

const vDom = Object.entries(props).reduce((totalNode, [key, value]) => {
if (key !== 'children' && key !== 'marked') {
totalNode[key] = value;
}

return totalNode;
}, initialNode);

props.children.forEach((child) => createDom(child, vDom));

(container || fragment).appendChild(vDom);

return container || fragment;
};

const _render = (element, container) => {
const vDom = createDom(element);

container.appendChild(vDom);
};

const render = (component, container) => {
domFactory = domFactory || component;
root = root || container;

if (typeof domFactory !== 'function') {
console.error('component should return function');

return;
}

const vDom = domFactory();
currDom = vDom;

_render(vDom, root);
};

const rerender = () => {
const newDom = domFactory();

const { isEqual, markedDom } = reconciliate(currDom, newDom);

currDom = newDom;

// (쉬운 ver.)diff하지 않고 전부 리렌더링
// root.innerHTML = '';
// _render(newDom, root);

if (!isEqual) {
repaint(markedDom, root.firstChild);
}
};

const repaint = (element, originDom) => {
if (element.marked) {
originDom.replaceWith(createDom(element));
}

element.props.children.forEach((child, idx) => {
repaint(child, originDom.childNodes[idx]);
});
};

return { render, rerender };
})();

export default ZigDom;
47 changes: 47 additions & 0 deletions src/lib/zig-react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import ZigDom from './zig-react-dom';

const Zig = (function () {
const createElement = (type, props, ...children) => {
return {
type,
props: {
...props,
children: children.map((child) => {
if (typeof child === 'string' || typeof child === 'number') {
return createTextElement(child);
} else {
return child;
}
}),
},
};
};

const createTextElement = (textValue) => {
return createElement('text', { nodeValue: textValue });
};

let hooks = [];
let idx = 0;

const useState = (initialValue) => {
const _idx = idx;
hooks[_idx] = initialValue;

const getState = () => hooks[_idx] || initialValue;

const setState = (newValue) => {
hooks[_idx] = newValue;

ZigDom.rerender();
};

idx++;

return [getState, setState];
};

return { createElement, useState };
})();

export default Zig;
4 changes: 4 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


0 comments on commit 4b8e57b

Please sign in to comment.