render
vm._render()
它的作用就是把实例渲染成虚拟节点vnode,定义在src/core/instance/render.js文件中。
js
Vue.prototype._render = function (): VNode {
const vm: Component = this
// 获取用户定义的render 或者模版渲染的render
const { render, _parentVnode } = vm.$options
...
try{
// vm.$createElement 就是一般传入的参数h,生成虚拟dom
vnode = render.call(vm._renderProxy, vm.$createElement)
}
...
}首先获取模版编译生成的render函数或者用户自定义的render函数,重点render函数的调用, 用户自定义的render函数一般如下,这里的h就是vm.$createElement方法。
js
render: function (h) {
return h(
)
},vm.$createElement方法定义在函数initRender中
js
export function initRender(vm: Component) {
// internal version is used by render functions compiled from templates
// 对编译生成的 render 函数进行渲染的方法
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.
// 对手写 render 函数进行渲染的方法,也就是 h
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
}这里不论是模版编译生成的render函数,还是用户传入的render函数,内部都调用了createElement函数,只是最后一个标识参数不同。createElement函数将会生成 vnode。
createElement()
js
export function createElement(
context: Component,
tag: any,
data: any,
children: any,
normalizationType: any,
alwaysNormalize: boolean
): VNode | Array<VNode> {
// 是否是数组(子节点)或原始值(标签中的内容)
if (Array.isArray(data) || isPrimitive(data)) {
normalizationType = children
children = data
data = undefined
}
// 用户传入的render,为了后续处理children
if (isTrue(alwaysNormalize)) {
normalizationType = ALWAYS_NORMALIZE
}
return _createElement(context, tag, data, children, normalizationType)
}它的主要作用是先处理一下参数,然后内部再调用_createElement()生成具体的 Vnode, 传递给_createElement函数的参数有:
- context 表示
VNode的上下文环境 - tag 标签,类型为字符串或
Component - data
VNode的数据 - children 当前 VNode的 子节点,会经过规范化处理,子节点也要处理成
VNode格式 - normalizationType,子节点规范的类型,会依据
render函数是编译生成的(SIMPLE_NORMALIZE)还是用户手写的(ALWAYS_NORMALIZE)
在_createElement()中,先规范化children子节点,然后根据不同tag类型创建VNode。
js
let vnode, ns
if (typeof tag === 'string') {
let Ctor
ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
// 是否是html的保留标签
if (config.isReservedTag(tag)) {
// platform built-in elements
if (process.env.NODE_ENV !== 'production' && isDef(data) && isDef(data.nativeOn) && data.tag !== 'component') {
warn(
`The .native modifier for v-on is only valid on components but it was used on <${tag}>.`,
context
)
}
vnode = new VNode(
config.parsePlatformTagName(tag), data, children,
undefined, undefined, context
)
} else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
// component
// tag为已注册的组件
// 查找自定义组件构造函数的声明
vnode = createComponent(Ctor, data, context, children, tag)
} else {
// unknown or unlisted namespaced elements
// check at runtime because it may get assigned a namespace when its
// parent normalizes children
vnode = new VNode(
tag, data, children,
undefined, undefined, context
)
}
} else {
// direct component options / constructor
// tag为组件或组件构造函数
vnode = createComponent(tag, data, context, children)
}先判断 tag 的类型,
string类型,则继续判断如果为内置的保留节点,则直接创建一个普通VNode; 如果是一个已注册的组件名,则通过createComponent创建一个组件类型的VNode,否则创建一个未知的标签的VNodeComponent类型,直接调用createComponent创建一个组件类型的VNode节点
总结
当vm._render执行完成生成 VNode 后,需要渲染成真实Dom,接下来就由vm._update来将VNode渲染成真实Dom。