Beomy

[Inside Vue] 8. View Render - baseCompile 함수

  7 mins read  

이번 포스트에서는 baseCompile 함수에서 사용하는 parse, optimize, generate 함수에 대해 이야기합니다. parse, optimize, generate 함수는 모두 많은 양의 코드를 보유(?)하고 있습니다. 자세히 코드를 살펴보지는 않고 어떤 작업들을 하는지만 살펴볼 것입니다.

baseCompile 함수

// `createCompilerCreator` allows creating compilers that use alternative
// parser/optimizer/codegen, e.g the SSR optimizing compiler.
// Here we just export a default compiler using the default parts.
export const createCompiler = createCompilerCreator(function baseCompile (
  template: string,
  options: CompilerOptions
): CompiledResult {
  const ast = parse(template.trim(), options)
  if (options.optimize !== false) {
    optimize(ast, options)
  }
  const code = generate(ast, options)
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})

baseCompile 함수를 보면, parse 함수에서 단순한 문자열인 templateast(Abstract Syntax Tree)로 변환하는 것을 볼 수 있습니다. 그 후 optimize 함수와 generate 함수를 거쳐 render 함수와 staticRenderFns를 생성합니다.

...
const ast = parse(template.trim(), options)
console.log('[AFTER PARSE]', ast)
optimize(ast, options)
console.log('[AFTER OPTIMIZE]', ast)
const code = generate(ast, options)
console.log('[AFTER GENERATE]', code)
...

위의 코드와 같이 console.logastcode를 살펴볼 것입니다. CodePen에서 위의 코드와 같이 빌드한 Vue를 사용하였습니다.

parse 함수

parse 함수는 compiler/parser/index.js 파일에 정의되어 있습니다. 코드량이 많기 때문에 이번 포스트에서 코드 언급 하지는 않겠습니다. parse 함수는 헬퍼 함수들을 정의하고 parseHTML 함수를 호출합니다.

parse 함수는 templateast로 빌드하는 함수입니다. 단순 문자열인 template를 이해하기 쉬운 트리 형태로 변경해 줍니다. astoptimize 함수와 generate 함수에서도 사용됩니다.

하나의 코드를 예로들어, AST를 살펴보도록 하겠습니다.

<div id="app">
  {{ newName ? newName + 'true' : newName + 'false' }}
  <span>This is static node</span>
</div>

<script>
new Vue({
  el: '#app',
  name: 'app',
  data () {
    return {
      name: 'foo'
    }
  },
  computed: {
    newName () {
      return this.name + 'new!'
    }
  }  
});
</script>

위의 코드에서 parse 함수를 통해 생성되는 ast는 아래와 같습니다.

{  
  type:1,
  tag:'div',
  attrsList:[  
    {  
      name:'id',
      value:'app'
    }
  ],
  attrsMap:{  
    id:'app'
  },
  parent:undefined,
  children:[  
    {  
      type:2,
      expression:'"\\n  "+_s(newName ? newName + \'true\' : newName + \'false\')+"\\n  "',
      text:'\n  {{ newName ? newName + \'true\' : newName + \'false\' }}\n  '
    },
    {  
      type:1,
      tag:'span',
      attrsList:[  

      ],
      attrsMap:{  

      },
      parent:[  
        Circular
      ],
      children:[  
        Array
      ],
      plain:true
    }
  ],
  plain:false,
  attrs:[  
    {  
      name:'id',
      value:'"app"'
    }
  ]
}

CodePen에서 ast를 살펴볼 수 있습니다.

optimize 함수

optimize 함수는 compiler/optimizer.js 파일에 정의되어 있습니다. optimize 함수는 AST에서 정적인 부분을 찾는 함수입니다. optimize 함수 실행 후의 AST를 살펴보도록 하겠습니다.

{  
  type:1,
  tag:'div',
  attrsList:[  
    {  
      name:'id',
      value:'app'
    }
  ],
  attrsMap:{  
    id:'app'
  },
  parent:undefined,
  children:[  
    {  
      type:2,
      expression:'"\\n  "+_s(newName ? newName + \'true\' : newName + \'false\')+"\\n  "',
      text:'\n  {{ newName ? newName + \'true\' : newName + \'false\' }}\n  ',
      static:false
    },
    {  
      type:1,
      tag:'span',
      attrsList:[  

      ],
      attrsMap:{  

      },
      parent:[  
        Circular
      ],
      children:[  
        Array
      ],
      plain:true,
      static:true,
      staticInFor:false,
      staticRoot:false
    }
  ],
  plain:false,
  attrs:[  
    {  
      name:'id',
      value:'"app"'
    }
  ],
  static:false,
  staticRoot:false
}

parse 함수 이후의 AST와 optimize 함수 이후의 AST의 차이점은 static 플래그들이 추가된 점입니다. 정적인 요소들은 static 플래그가 true로 설정됩니다. CodePen에서 ast를 살펴볼 수 있습니다.

generate 함수

generate 함수는 compiler/codegen/index.js 파일에 정의되어 있습니다.

{  
  render:'with(this){return _c(\'div\',{attrs:{"id":"app"}},[_v("\\n  "+_s(newName ? newName + \'true\' : newName + \'false\')+"\\n  "),_c(\'span\',[_v("This is static node")])])}',
  staticRenderFns:[  

  ]
}

generate 함수를 통해 생경되는 code는 위의 코드와 같습니다. code 객체는 문자열인 render와 배열인 staticRenderFns로 구성됩니다. CodePen에서 code 객체를 살펴볼 수 있습니다.

code 객체에 포함된 render 함수는 vnode = render.call(vm._renderProxy, vm.$createElement) 이런 방법으로 호출합니다. call 메소드를 사용하여 render 함수 내의 thisvm._renderProxy가 됩니다. 또 render 함수는 with(this) 메소드를 사용합니다. 이런 과정 때문에 Vue에서 template를 사용 할 때, this를 사용하지 않아도 됩니다.

요약

이번 포스트에서는 컴파일러가 어떻게 template를 랜더링 함수로 만들어 내는지 이야기 하였습니다.

baseCompile 함수에서 parse, optimize, generate 함수를 통해 ast, code.render, code.staticRenderFns가 생성됩니다.

  1. parse 함수를 통해 단순 문자열인 templateast로 변경됩니다.
  2. optimize 함수를 통해 static 플래그들이 추가 됩니다. 정적인 요소들은 static 플래그가 true로 설정됩니다.
  3. generate 함수를 통해 render 함수와 staticRenderFns가 생성됩니다.

다음으로 볼 것

다음 포스트(9. View Render - Patch)에서는 생성된 render 함수와 __patch__() 함수로 웹페이지를 업데이트 하는 과정을 이야기 하도록 하겠습니다.

참고