21세기소년, Javascript, 도구, 스터디

(Javascript | mithril) m.render – 가상 DOM 렌더링하기

mithril 관련 글

  1. mithril 앱의 기본 구성 및 m()
  2. * m.render – 가상 DOM 렌더링하기
  3. m.mountmithril을 이용한 양방향 바인딩 & 템플릿 렌더링
  4. m.prop 양방향 바인딩을 위한 데이터 래퍼
  5. m.withAttr 양방향 바인딩을 위한 이벤트 매퍼
  6. m.componentmithril 앱을 컴포넌트 화하기
  7. Todo
  8. m.route – 단일페이지 애플리케이션 및 라우팅 규칙
  9. m.request – 서버 API와 통신하기

m.render

m.render는 미리 구성된 컴포넌트 단위를 주어진 DOM 위에 렌더링하는 일을 담당한다. 이 메소드가 동일한 가상 DOM 세트에 대해서 한 번 이상 호출되는 경우에는 기존 DOM 트리와 신규 트리 사이의 차이를 지능적으로 분석하여 변경이 일어난 부분에 대해서만 재 렌더링을 하는 방식으로 성능을 높이게끔 동작한다.

links = 
  * title: 'item 1', url: \/item1
  * title: 'item 2', url: \/item2

m.render document.body, [
  m \ul.nav, 
    links.map (item) ->
      m \li, m \a {href: item.url}, item.title
]

위와 같이 호출하면 <body> 하위에 m()의 결과를 렌더링해준다.

<body>
  <ul class="nav">
    <li><a href="/item1">item 1</a></li>
    <li><a href="/item2">item 2</a></li>
  </ul>
</body>

m.render 의 시그니처는 다음과 같다.

void render(DOMElement rootElement, Children chidren [,Boolean foreceRecreateion])

forceRecreation 키는 기본적으로 false 값을 가지는데 true로 주어지는 경우에 전체 하위 노드들은 기존 노드를 무시하고 새롭게 생성되어 렌더링된다.

Children은 루트 요소 하위를 채울 가상 DOM 요소들인데 다음과 같은 패턴으로 정의된다.

Children :: String | VirtualElement virtualElement | SubtreeDerective directive | Array<Children>
where
    VirtualElement :: Object{ String tag, Attributes attributes, Children children}
    Attributes :: Object<Any | void config(DOMElement element)>
    SubtreeDirective :: Object{ String subtree }

이는 문자열이거나 m()으로 생성된 가상 DOM 요소일 수 있다. 추가적으로 Subtree directive를 명시할 수 있는데, 이는 정확하게 {subtree: "retained"} 의 한 값밖에 없다. 이는 동적으로 변하는 값이 영향을 주지 않는 컴포넌트에 대해서 매번 diff를 통해 이전 버전과의 차이점 분석을 하는 비용을 줄이는 목적으로 사용될 수 있다. 다음 예제에서 app.bind-once는 헤더 부분을 렌더링하는데, 최초에 m.render, m.mount를 통해서 렌더링 되는 시점에 생성된 가상 DOM은 이후 어떤 변경도 없이 계속 재사용되며, 그만큼 페이지 로딩을 빠르게 할 수 있다.

app = {}

app.bind-once = do
  cache = {}
  return (view) ->
    if !cache[view.to-string!] then
      cache[view.to-string] = true
      return view!
    return {subtree: 'retain'}

app.view = ->
  m \.layout,
  * app.bind-once -> # 여기는 한 번만 렌더링하고 그 이후는 계속 변경없이 재사용됨
      m \header, m \h1, 'this never changes'
    m \main, 'rest of app goes here' # m.render가 호출될 때마다 이전버전과 비교