일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- webpack
- 타입스크립트
- 서비스프로그램
- dbms
- 프론트엔드
- Oracle
- 선점 스케줄링
- javascript
- 오라클
- 리액트
- sql
- 마이그레이션
- 데이터베이스
- react
- 프로덕트구조
- 블록체인용어
- 감시프로그램
- 코드서울
- roadhog
- 운영체제
- useCallback
- typescirpt
- 자바스크립트
- typescript
- Database
- Migration
- 처리프로그램
- 프로덕트관리
- 제어프로그램
- react코어
- Today
- Total
Develop+
리액트 코어 - 리액트의 virtual dom 그리는 방식 파해쳐보기 본문
일단 바닐라js로 리액트 코어를 만들어보려고 한다.
컴포넌트의 ui를 만드는 JSX 는 js문법이 아니기 때문에 js로 바꿔야 하기 때문에 babel을 사용해 변환을 해줄 것이다.
시작하기 전 리액트 버츄어돔에 대한 이해
리엑트는 버츄얼돔을 가지고 있다.
버츄얼 돔을 만든다면 어떻게 만들 수 있을까???
리액트의 버츄어돔도 오픈소스이기 때문에 전부 공개되어있다.
버츄얼돔은 기본적으로 HTML태그를 변환시키는 구조로 되어있을 것이다.
그러면 html태그는 어떻게 생겼을까.
<div>태그가 있고 그 안에 속성 id등등이 있고, 태그는 열리고 닫히고 내부에는 자식 노드들이 있다.
이걸 버츄어 돔으로 js친화적인 데이터로 만들면 어떨까?
js 친화적긴 데이터란 객체이다.
<div>
<span>EXAMPLE</span>
</div>
이걸 객체로 바꾸면 어떨까
객체도 태그 구성과 유사하다
{
tagName: 'div',
props: {
id: "root",
className: "container",
},
children: [
{
tagName: 'span',
props: {},
children: 'EAAMPLE',
}
]
}
HTML을 객체로 변환해 생각해 보았을때 이런 구조를 가질 것이라고 생각하고 이제 react.js를 만들어보자.
react.js 생성
일단 우리는 react 패키지로 다운받아서 사용할 게 아니기 때문에 src폴더에 react.js를 만들어
그 안에 react 메서드들을 구현시켜 볼 것이다.
src/react.js
export function render() {
}
export function createElement() {
}
react에서 사용하는 render 메서드와 createElement 메서드를 구현된 코드는 없지만 메서드 바디만 작성해두었다.
그리고 Title이라는 jsx를 리턴하는 펑셔널 컴포넌트를 만들어
우리가 만들었던 react.js를 import해 메서드를 불러서 사용하려고 한다.
import { createElement, render } from './react.js';
function Title() {
return (
<h2>정말 동작할까?</h2>
)
};
render(<Title />, document.querySelector('#root'));
Title이라는 컴포넌트를 만들었고 react가 사용하듯이 Title을 render에 인자로 넣었고, 이 파일을 바벨로 변환해보았다.
build/index.js
import { createElement, render } from './react.js';
function Title() {
return /*#__PURE__*/React.createElement("h2", null, "\uC815\uB9D0 \uB3D9\uC791\uD560\uAE4C?");
}
;
render( /*#__PURE__*/React.createElement(Title, null), document.querySelector('#root'));
빌드된 build/index.js를 열어보면 신기하게도 우리는 React 패키지를 사용한 적도 없는데 React.createElement를 사용해 타이틀이라는 반환 객체를 만들었다. 이 이유는 바벨에서 jsx를 변환할 때 내장되어있는 디폴트 값인 React 사용하기 때문이라고 한다.
위코드의 createElement 메서드를 보면
React.createElement("h2", null, "\uC815\uB9D0 \uB3D9\uC791\uD560\uAE4C?");
첫번째 인자에는 tagName, 두번째에는 props, 세번째에는 children이 들어가는 구조이다.
이를 보고 바디만 작성했던 createElement를 작성해보자
아래의 코드는 리액트 코어를 담당하는 코드를 심플하게 구현한 코드이다.
export class Component {
// 클래스 형식의 jsx인지 체크하기 위한 compoent 클래스 구조 정의
}
function renderRealDOM(vdom) {
// 자식요소가 문자열이거나
if(typeof vdom === 'string') {
return document.createTextNode(vdom);
}
// 마지막 노드일 때 재귀를 탈출합니다.
if(vdom === undefined) return;
// element객체를 만들어줍니다.
const $el = document.createElement(vdom.tagName);
//vdom의 자식들을 돌며 만든 태그 안에 차곡차곡 append 해줍니다.
vdom.children.map(renderRealDOM).forEach(node => {
$el.appendChild(node);
});
return $el;
}
// container에 map으로 전체 노드를 돌아 append된 vdom을 append해줍니다.
export const render = (function(){
// 이전 상태를 저장하기 위해 클로저를 만들어줍니다.
let preVdom = null;
return function (nextVdom, container) {
if(preVdom === null) {
preVdom = nextVdom;
}
container.appendChild(renderRealDOM(nextVdom))
}
})();
export function createElement(tagName, props, ...children) {
// 클래스와 function와 구분할 방법이 없기 때문에
// 상속받은 function인지를 체크해줍니다.
if(typeof tagName === 'function') {
// 코드에서 Component를 상속받았는지를 체크합니다.
if(tagName.prototype instanceof Component) {
//클래스를 인스턴스화 한 후
const instance = new tagName({...props, children});
// 노드를 뱉는 render를 실행시켜 줍니다.
return instance.render();
}else {
return tagName.apply(null, [props, ...children]);
}
}
return {tagName, props, children}
}
2부에서 계속!
'React' 카테고리의 다른 글
Redux 코어 구현해보기 CreateStore, reducer, dispatch (0) | 2022.11.22 |
---|---|
리액트 코어 - 훅 만들어보기 (0) | 2022.11.14 |
웹뷰 개발을 설계하는 방식 (1) | 2022.10.17 |
[Vue.js] URIError: Failed to decode param '/%3C...' 에러 [해결/Solved] (0) | 2021.11.15 |
타입스크립트 doest not exist on type 'Readonly'.. 에러 (TypeScript error) [해결 Solved] (0) | 2021.11.05 |