vue源码3----render函数
render函数定义在src/core/instance/render.js下,用来将模板渲染成虚拟dom(VNode)
Vue.prototype._render = function (): VNode { const vm: Component = this const { render, _parentVnode } = vm.$options 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 let vnode try { // There's no need to maintain a stack because 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)) { //如果产生了多个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 }
render函数的核心就是
vnode = render.call(vm._renderProxy, vm.$createElement)
首先看一下vm._renderProxy,在开发环境下,来源于initProxy(vm)
if (process.env.NODE_ENV !== 'production') { initProxy(vm) } else { vm._renderProxy = vm }
initProxy函数定义在src/core/instance/proxy.js
initProxy = function initProxy (vm) { if (hasProxy) {//首先判断当前环境是否支持proxy // 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)//创建一个代理 } else { vm._renderProxy = vm } }
options.render._withStripped这个只有在严格模式下不支持with时,手动设置为true才启用,所以一般都是使用hasHandler
const hasHandler = { has (target, key) { const has = key in target const isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data)) if (!has && !isAllowed) { if (key in target.$data) warnReservedPrefix(target, key) else warnNonPresent(target, key) } return has || !isAllowed } }
在这里对has进行了代理,allowedGlobals是一些关键字
const allowedGlobals = makeMap( 'Infinity,undefined,NaN,isFinite,isNaN,' + 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' + 'require' // for Webpack/Browserify )
warnReservedPrefix,访问$data上的属性会得到警告
const warnReservedPrefix = (target, key) => { warn( `Property "${key}" must be accessed with "$data.${key}" because ` + 'properties starting with "$" or "_" are not proxied in the Vue instance to ' + 'prevent conflicts with Vue internals. ' + 'See: https://vuejs.org/v2/api/#data', target ) }
warnNonPresent,这个就是我们常见的错误之一:属性未定义就使用了(建议复制粘贴)...
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: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', target ) }
接下来是vm.$createElement,首先来源于src/core/instance/render.js下的
// 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)
首先这两个函数区别在于最后一个参数,第一个用于编译生成的render,第二个用于用户手写的render

浙公网安备 33010602011771号