-
介绍
版本:2.5.17。
我们使用vue-vli创建基于Runtime+Compiler的vue脚手架。
学习文档:https://ustbhuangyi.github.io/vue-analysis/data-driven/render.html
在上一节中我们提到了在src/core/instance/lifecycle.js 中通过渲染Watcher实时去监测调用updateComponent方法,从而实现的页面实时渲染,vm._render()主要是生成的VNode(虚拟DOM)
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
-
vm._render
vue 的
_render
方法是实例的一个私有方法,它用来把实例渲染成一个虚拟 Node。它的定义在src/core/instance/render.js
文件中:Vue.prototype._render = function (): VNode { const vm: Component = this const { render, _parentVnode } = vm.$options // reset _rendered flag on slots for duplicate slot check if (process.env.NODE_ENV !== 'production') { for (const key in vm.$slots) { // $flow-disable-line vm.$slots[key]._rendered = false } } if (_parentVnode) { vm.$scopedSlots = _parentVnode.data.scopedSlots || emptyObject } // set parent vnode. this allows render functions to have access // to the data on the placeholder node. vm.$vnode = _parentVnode // render self let vnode try { 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') { if (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 } } else { vnode = vm._vnode } } // 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、拿到vue实例参数中的render函数,这个render函数可以使用户自己写的,也可以是template编译成的render函数
const { render, _parentVnode } = vm.$options
2、去调用这个render函数
try { vnode = render.call(vm._renderProxy, vm.$createElement) } catch (e) { }
vm.$createElement创建虚拟的DOM ,它的定义
export function initRender (vm: Component) { // 编译时创建VNode的方法 vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false) // 手写render函数的时候创建VNode的方法 vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true) }
其中initRender这个函数在new Vue的时候会去调用在src/core/instance/init.js中可以看到。
对于vm.$createElement我们可举一个例子
<div id="app"> {{ message }} </div>
相当于我们编写如下
render
函数:render: function (createElement) { return createElement('div', { attrs: { id: 'app' }, }, this.message) }
再回到
_render
函数中的render
方法的调用:vnode = render.call(vm._renderProxy, vm.$createElement)
可以看到,
render
函数中的createElement
方法就是vm.$createElement
方法:export function initRender (vm: Component) { // ... // bind the createElement fn to this instance // so that we get proper render context inside it. // args order: tag, data, children, normalizationType, alwaysNormalize // internal version is used by render functions compiled from templates vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false) // normalization is always applied for the public version, used in // user-written render functions. vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true) }
实际上,vm.$createElement
方法定义是在执行 initRender
方法的时候,可以看到除了 vm.$createElement
方法,还有一个 vm._c
方法,它是被模板编译成的 render
函数使用,而 vm.$createElement
是用户手写 render
方法使用的, 这俩个方法支持的参数相同,并且内部都调用了 createElement
方法。
-
总结
vm._render
最终是通过执行 createElement
方法并返回的是 vnode
,它是一个虚拟 Node。Vue 2.0 相比 Vue 1.0 最大的升级就是利用了 Virtual DOM。