模板转换成浏览器认识的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函数:
详细内容如下图,里面的_h, _c, _v, _s都是vue实例函数的简称
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);
这是一个匿名函数,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);