Vue模板编译,生成render函数

模板转换成浏览器认识的HTML过程如下:

  • template -> AST render (compiler解析template)
  • AST render -> vNode (render方法运行)
  • vNode -> DOM (vdom.patch)

这里总结下第一步模板编译成AST render函数的方式,生成的render函数都会加在vue实例的$options上或者$options原型上,调用实例的_render方法时被调用。可以看vue的源码:

Vue.prototype._render = function () {
    var vm = this;
    var ref = vm.$options;
    var render = ref.render;
    var _parentVnode = ref._parentVnode;

    if (_parentVnode) {
      vm.$scopedSlots = normalizeScopedSlots(
        _parentVnode.data.scopedSlots,
        vm.$slots,
        vm.$scopedSlots
      );
    }

    // set parent vnode. this allows render functions to have access
    // to the data on the placeholder node.
    vm.$vnode = _parentVnode;
    // render self
    var vnode;
    try {
      // There's no need to maintain a stack becaues all render fns are called
      // separately from one another. Nested component's render fns are called
      // when parent component is patched.
      currentRenderingInstance = vm;
      vnode = render.call(vm._renderProxy, vm.$createElement);
    } catch (e) {
      handleError(e, vm, "render");
      // return error render result,
      // or previous vnode to prevent render error causing blank component
      /* istanbul ignore else */
      if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {
        try {
          vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e);
        } catch (e) {
          handleError(e, vm, "renderError");
          vnode = vm._vnode;
        }
      } else {
        vnode = vm._vnode;
      }
    } finally {
      currentRenderingInstance = null;
    }
    // if the returned array contains only a single node, allow it
    if (Array.isArray(vnode) && vnode.length === 1) {
      vnode = vnode[0];
    }
    // return empty vnode in case the render function errored out
    if (!(vnode instanceof VNode)) {
      if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {
        warn(
          'Multiple root nodes returned from render function. Render function ' +
          'should return a single root node.',
          vm
        );
      }
      vnode = createEmptyVNode();
    }
    // set parent
    vnode.parent = _parentVnode;
    return vnode
  };
}

1. webpack打包工具,引入的”vue-template-compiler”编译生成render函数

这也是在使用打包工具时,只需引入运行时的vue, 如vue/dist/vue.runtime.esm.js

单文件 HelloWorld.vue

<template>
  <div class="example">{{ msg }}</div>
</template>

<script>
export default {
  data () {
    return {
      msg: 'Hello world!'
    }
  }
}
</script>

测试文件main.js

import HelloWorld from './components/HelloWorld'
console.log("===========vue-template-compiler===========")
console.log(HelloWorld);

单文件HelloWorld.vue经过vue-loader处理,由vue-template-compiler编译生成的render函数:

《Vue模板编译,生成render函数》

详细内容如下图,里面的_h, _c, _v, _s都是vue实例函数的简称
《Vue模板编译,生成render函数》

2. vue源码完整版,vue全局API Vue.compile编译生成的render函数

import Vue from 'vue'

let templateHelloworld = `<div class="example">{{ msg }}</div>`
// Vue完整版里的compiler
let vueCompilerResult = Vue.compile(templateHelloworld);
console.log("===========vue compiler===========")
console.log(vueCompilerResult);

《Vue模板编译,生成render函数》

《Vue模板编译,生成render函数》

这是一个匿名函数,with包裹作用域,_c同上,是vue实例的方法简写

3 用jsx写render函数,经过编译后的结果:

import Vue from 'vue'
let jsxHellowWorld = new Vue({
  render: function(h){
    return(
      <div className = "example">
        {msg}
      </div>
    )
  }
})

console.log("===========jsx===========")
console.log(jsxHellowWorld.$options.render);

《Vue模板编译,生成render函数》

    原文作者:zeroyl
    原文地址: https://segmentfault.com/a/1190000020013226
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞