Vue Internals①astを中心に 1-3compile(generate)

#はじめに
前回はAST作成までを見てきました。
今回はASTがどのように
```{render: "with(this){return _c('div',{attrs:{"id":"vue_example"}},[_v("\n  "+_s(message)+"\n")])}"```みたいな形になっていくか見ていきます。

#generateから

```js
var createCompiler = createCompilerCreator(function baseCompile (
    template,
    options
  ) {
    var ast = parse(template.trim(), options);
    if (options.optimize !== false) {
      optimize(ast, options);
    }
    
    //今回はここから
    var code = generate(ast, options);
    return {
      ast: ast,
      render: code.render,
      staticRenderFns: code.staticRenderFns
    }
  });
```

generateの本体はgetElement

```js
function generate (
    ast,
    options
  ) {
    var state = new CodegenState(options);
    var code = ast ? genElement(ast, state) : '_c("div")';
    return {
      render: ("with(this){return " + code + "}"),
      staticRenderFns: state.staticRenderFns
    }
  }
```

```js
function genElement (el, state) {
    if (el.parent) {
      el.pre = el.pre || el.parent.pre;
    }

    if (el.staticRoot && !el.staticProcessed) {
      return genStatic(el, state)
    } else if (el.once && !el.onceProcessed) {
      return genOnce(el, state)
    } else if (el.for && !el.forProcessed) {
      return genFor(el, state)
    } else if (el.if && !el.ifProcessed) {
      return genIf(el, state)
    } else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
      return genChildren(el, state) || 'void 0'
    } else if (el.tag === 'slot') {
      return genSlot(el, state)
    } else {
      // component or element
      var code;
      if (el.component) {
        code = genComponent(el.component, el, state);
      } else {
        var data;
        if (!el.plain || (el.pre && state.maybeComponent(el))) {
          data = genData$2(el, state);
        }

        var children = el.inlineTemplate ? null : genChildren(el, state, true);
        code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")";
      }
      // module transforms
      for (var i = 0; i < state.transforms.length; i++) {
        code = state.transforms[i](el, code);
      }
      return code
    }
  }
```
getElementはstaticだったりforとか特殊な奴はgenStaticとかで処理、今回は特殊な処理もなくgetData$2とgetChildrenのみ

getData$2

```js
  function genData$2 (el, state) {
    var data = '{';

    // directives first.
    // directives may mutate the el's other properties before they are generated.
    var dirs = genDirectives(el, state);
    if (dirs) { data += dirs + ','; }

    // key
    if (el.key) {
      data += "key:" + (el.key) + ",";
    }
    // ref
    if (el.ref) {
      data += "ref:" + (el.ref) + ",";
    }
    if (el.refInFor) {
      data += "refInFor:true,";
    }
    // pre
    if (el.pre) {
      data += "pre:true,";
    }
    // record original tag name for components using "is" attribute
    if (el.component) {
      data += "tag:\"" + (el.tag) + "\",";
    }
    // module data generation functions
    for (var i = 0; i < state.dataGenFns.length; i++) {
      data += state.dataGenFns[i](el);
    }
    // attributes
    if (el.attrs) {
      data += "attrs:" + (genProps(el.attrs)) + ",";
    }
    // DOM props
    if (el.props) {
      data += "domProps:" + (genProps(el.props)) + ",";
    }
    // event handlers
    if (el.events) {
      data += (genHandlers(el.events, false)) + ",";
    }
    if (el.nativeEvents) {
      data += (genHandlers(el.nativeEvents, true)) + ",";
    }
    // slot target
    // only for non-scoped slots
    if (el.slotTarget && !el.slotScope) {
      data += "slot:" + (el.slotTarget) + ",";
    }
    // scoped slots
    if (el.scopedSlots) {
      data += (genScopedSlots(el, el.scopedSlots, state)) + ",";
    }
    // component v-model
    if (el.model) {
      data += "model:{value:" + (el.model.value) + ",callback:" + (el.model.callback) + ",expression:" + (el.model.expression) + "},";
    }
    // inline-template
    if (el.inlineTemplate) {
      var inlineTemplate = genInlineTemplate(el, state);
      if (inlineTemplate) {
        data += inlineTemplate + ",";
      }
    }
    data = data.replace(/,$/, '') + '}';
    // v-bind dynamic argument wrap
    // v-bind with dynamic arguments must be applied using the same v-bind object
    // merge helper so that class/style/mustUseProp attrs are handled correctly.
    if (el.dynamicAttrs) {
      data = "_b(" + data + ",\"" + (el.tag) + "\"," + (genProps(el.dynamicAttrs)) + ")";
    }
    // v-bind data wrap
    if (el.wrapData) {
      data = el.wrapData(data);
    }
    // v-on data wrap
    if (el.wrapListeners) {
      data = el.wrapListeners(data);
    }
    return data
  }
```

state.dataGenFnsでstyleとclassが追加される、ほかにもいろいろdataに追加しているんだけど、今回はattrだけ

![genProps1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/6e41e1b5-d87e-0a3c-f3d1-9674b7956f68.png)

staticProps.slice(0,-1）で最後の,をとって{"id":"vue_example"}として、最終的に
data="{attrs:{"id":"vue_example"},"となる

```data.replace(/,$/, '') + '}'```で,をとって}を追加し、getData$2からは
"{attrs:{"id":"vue_example"}}"が返る

getChildren
![genChildren.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/3200ff49-e4a2-c6b2-37f2-f172c7c98dcb.png)
genChildrenはchildrenからchildを一つ一つとりだして、childにgen=genNodeを適応させて、その返り値を,で繋げていって[gen(child1),gen(child2)]みたいにしていく

getNode
![genNode+genText.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/62bed134-03cd-70f3-3bf8-3d1e23e62a04.png)

genNodeからの返り値が_v("\n  "+_s(message)+"\n")で今回はchildが一つなのでgenChildrenの返り値は
"[_v("\n  "+_s(message)+"\n")]"

最終的に生成されるcodeは

```js

"_c('div',{attrs:{"id":"vue_example"}},[_v("\n  "+_s(message)+"\n")])"
```
となる

これがgenerateのcode=getElement()のcodeでgenerateからの返り値は

```js
render: "with(this){return _c('div',{attrs:{"id":"vue_example"}},[_v("\n  "+_s(message)+"\n")])}"
staticRenderFns: []
```

ここまででcompileが終わり、baseCompie→compile→compileToFunctionまで戻る
compileToFunction

![compileToFunctionAfter.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/808f773e-13c0-e9db-e887-2f724187496a.png)
ここでcompiledは

```js
ast: {type: 1, tag: "div", attrsList: Array(1), attrsMap: {…}, rawAttrsMap: {…}, …}
render: "with(this){return _c('div',{attrs:{"id":"vue_example"}},[_v("\n  "+_s(message)+"\n")])}"
staticRenderFns: []
errors: []
tips: []
__proto__: Object
```

res.renderにcreateFunctionでつくったやつが入る
res.render

```js
(function anonymous(
) {
with(this){return _c('div',{attrs:{"id":"vue_example"}},[_v("\n  "+_s(message)+"\n")])}
})
```

最後にcacheを作ってcompileToFunction終わり

```js
return (cache[key] = res)

key="<div id="vue_example">
  {{message}}
</div>"
res=render: ƒ anonymous( )
length: 0
name: "anonymous"
arguments: null
caller: null
prototype: {constructor: ƒ}
__proto__: ƒ ()
[[FunctionLocation]]: VM46968:1
[[Scopes]]: Scopes[1]
staticRenderFns: []
__proto__: Object
```

そして、Vue.$mountまで戻って、this.$optionsにrenderとstaticRenderFnsを追加して、this.$optionは

```
components: {}
directives: {}
filters: {}
_base: ƒ Vue(options)
el: "#vue_example"
data: ƒ mergedInstanceDataFn()
render: ƒ anonymous( )
staticRenderFns: []
__proto__: Object
```
となります。

![$mountAfter.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/c04cab6d-9193-bf45-df2e-f24521b5ba84.png)

ここまででcompile部分終了です、次回はmountComponentでやっと本当のdomを作成していくます、ここで大目標の一つ{{message}}の処理方法がわかります!