Vue2源码梳理:render函数的实现
- IT业界
- 2026-07-21 12:00:01
render
在 $mount 时,会调用 render 方法在写 template 时,最终也会转换成 render 方法Vue 的 _render 方法是实例的一个私有方法,它用来把实例渲染成一个虚拟 Node它的定义在 src/core/instance/render.js 文件中,它返回的是一个vnode这里看下它的实现Vue.prototype._render = function (): VNode {
const vm: Component = this
// 从 $options 中拿到这个 render 函数
// 这个 render 函数, 可以是用户自己写,也可以通过编译生成
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 {
// call的第一个参数是当前上下文, vm._renderProxy 在生产环境下,就是 VM,也就是this本身, 在开发环境它可能是一个proxy对象
// 这个 vm.$createElement 也是一个函数,会在下面来说
// 总体来说就是生成 vnode
vnode = render.call(vm._renderProxy, vm.$createElement)
} catch (e) {
// 捕获并处理错误,尝试再次生成 vnode
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)) {
// 如果拿到的 vnode 是个数组,说明模板有多个根节点,这个在后续vue3中支持,在vue2中会报下面的警告
// 在vue2 中 vnode 是一个 vdom,
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
}
注意,上面的 vm.$createElement 在 initRender 函数中被初始化定义而 initRender 是在一开始 new Vue 的时候,会调用 _init 函数中被执行// initRender 函数中
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
看到上面两个函数区别,就是最后一个参数不一样_c 这个函数它实际上是被编译生成的render函数所使用的一个方法$createElement 是给我们手写render函数提供了一个创建vnode的一个方法var app = new Vue({
el: '#app',
// 手写 render 函数
render(createElement) {
return createElement('div', {
attrs: {
id: 'app2' // 这些写后,这里挂载的 div 元素,会替换掉之前的 #app 容器
}
}, this.message)
},
data() {
return {
message: 'Hello'
}
}
})
这里,使用render和在html中写是不一样的,它没有一个把从差值变换的这个过程之前的写法是在HTML里面定义了这个差值// 之前的写法
<div id="app">
{{ message }}
</div>
它在不执行vue的时候,它会先把这个东西渲染出来然后在new vue之后, 执行 mount 的时候,再把它从差值的那个message给替换成真实的数据在这里是通过纯 render 函数,当它执行完毕以后,直接挂载到页面上,这样的话体验会更好一点因为这里手写了 render 函数,所以, 整个渲染流程中,就不会再执行把 template 转换 render 函数那一步了所以就相当于直接就达到了一样的效果 vm._renderProxy 的定义,也是发生在 src/core/instance/init.js 中在 _init 函数中,有一个判断/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
当前如果说是生产环境,vm._renderProxy = vm开发阶段开发阶段,则执行 initProxy(vm) 定义在 src/core/instance/proxy.js 中// 报错提示函数
const warnNonPresent = (target, key) => {
warn(
`Property or method "${key}" is not defined on the instance but ` +
'referenced during render. Make sure that this property is reactive, ' +
'either in the data option, or for class-based components, by ' +
'initializing the property. ' +
'See: vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',
target
)
}
//
const hasHandler = {
has (target, key) {
const has = key in target
const isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data)) // 全局方法或者私有方法,或不在$data中
// 属性不在 target 下,并且不被允许,则报错提示,比如:使用了 一个没有在 data 中定义的变量,就会报错
if (!has && !isAllowed) {
if (key in target.$data) warnReservedPrefix(target, key)
else warnNonPresent(target, key)
}
return has || !isAllowed
}
}
// 判断当前浏览器是否支持 proxy,Proxy作用是对对象的访问做一个劫持
const hasProxy = typeof Proxy !== 'undefined' && isNative(Proxy)
initProxy = function initProxy (vm) {
if (hasProxy) {
// determine which proxy handler to use
const options = vm.$options
const handlers = options.render && options.render._withStripped
? getHandler
: hasHandler
vm._renderProxy = new Proxy(vm, handlers) // 在这里 handlers 是 hasHandler
} else {
vm._renderProxy = vm
}
}
这里可以看到,支持 Proxy 则 vm._renderProxy = new Proxy(vm, handlers) 通过以上分析可知,vm._render 最终是通过执行 createElement 方法并返回的是 vnode而返回的vnode 它是一个虚拟 Node
Vue2源码梳理:render函数的实现由讯客互联IT业界栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Vue2源码梳理:render函数的实现”