일반적으로 브라우저가 웹페이지를 화면에 보일 때에는 크게 2가지의 경우의 수가 존재한다.
1. html 문서를 파싱하여 DOM 트리를 만든다
2. 그 후, DOM의 요소가 변경될 때마다 Reflow, Repaint를 진행한다.
<브라우저의 페이지 랜더링 모식도>
이게 단순히 한두가지의 컴포넌트가 변경되는 것이 아닌 여러개의 자식들이 동시에 변경되야 한다면 몹시 퍼포먼스가 느려진다
virtual dom은 이 느린 퍼포먼스를 해결하기 위해 만들어진 React의 방식이다
만약 getDerivedStateFromProps의 값이 존재한다면, shouldComponentUpdate로 true를 리턴한다. (클래스 컴포넌트)
React.memo(component, (nextState,prevState)=> nextState.value === prevState.value)로 이루어짐 (함수 컴포넌트)
class Example extends React.Component {
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.value !== prevState.value) {
return { value: nextProps.value }
}
return null
}
}
componentDidUpdate는 shouldComponentUpdate의 리턴값이 true일때만 호출된다.
render을 호출하여 virtual DOM이 이루어지는건 알겠는데, 그 세부적인 사항에 대해서 조금 더 들어가자면
예를들어, 초기 랜더링을 통해 실제 DOM에 반영되는 요소들이 위와 같다면 virtual DOM 의 상태는 아래와 같다
사실 실제 DOM과 크게 다르지 않고, 어트리뷰트 노드가 props라고 하는 jsx객체 안에 전부 다 들어가있는 형태로 되어있는 차이정도가 있다.
만약 여기서 add버튼을 실제로 누르게 된다면
//Calculator.js
<button id="add" onClick={ () => {
IntegerA = parseInt(ReactDOM.findDOMNode(this.refs.input1).value)
IntegerB = parseInt(ReactDOM.findDOMNode(this.refs.input2).value)
IntegerC = IntegerA+IntegerB
this.setState({output:IntegerC})
}
}>Add</button>
DOM의 event객체는 react의 커스텀 이벤트 객체의 래퍼객체이므로, 리엑트 객체는 해당 이벤트를 감지할 수 있다
이벤트가 감지가 완료되면, 리엑트는 해당 이벤트 핸들러를 호출한다. 여기서 볼 것은 이벤트 핸들러의 내부에 "this.setState" 라고 되어있는 부문으로,
여기서의 this는 화살표 함수로 인해 상위 스코프의 This, 즉 Calulator 컴포넌트가 가리키는 this가 된다.
따라서, 이 this의 상태업데이트 정보에 따라 리엑트는 자신들의 내부에 있는 "Dirty Component 리스트에 해당 컴포넌트를 삽입한다
후에 이 Dirty component리스트를 순회하며 배치업데이트를 실행한다(배치업데이트는 더티 컴포넌트 리스트를 해제하면서 차례대로 업데이트를 실행한다)
그 후 도출되는 pending state(변경된 상태값) 들을 queue로 집어넣는다
그 큐들을 보면서 shouldComponentUpdate의 호출이 발생했을 시 새로운 상태로 랜더링을 진행한 후, virtual dom에 있는 내용을 토대로 실제 dom의 내용을 변경시킨다.
이때, 랜더링을 위해 참조하는 값들이 type과 key라고 한다.
실제로는 이것보다 더 많고, 복잡한 과정이 엄청나게 많지만 현재 내가 다 이해하기는 무리일 것 같으므로 이해가 되는 부분들만 간략하게 정리하였다
how to virtual dom update real dom
deep understand of virtual dom