You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
exportfunction_createElement(context: Component,tag?: string|Class<Component>|Function|Object,data?: VNodeData,children?: any,normalizationType?: number): VNode|Array<VNode>{if(isDef(data)&&isDef((data: any).__ob__)){process.env.NODE_ENV!=='production'&&warn(`Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n`+'Always create fresh vnode data objects in each render!',context)returncreateEmptyVNode()}// object syntax in v-bindif(isDef(data)&&isDef(data.is)){tag=data.is}if(!tag){// in case of component :is set to falsy valuereturncreateEmptyVNode()}// warn against non-primitive keyif(process.env.NODE_ENV!=='production'&&isDef(data)&&isDef(data.key)&&!isPrimitive(data.key)){if(!__WEEX__||!('@binding'indata.key)){warn('Avoid using non-primitive value as key, '+'use string/number value instead.',context)}}// support single function children as default scoped slotif(Array.isArray(children)&&typeofchildren[0]==='function'){data=data||{}data.scopedSlots={default: children[0]}children.length=0}if(normalizationType===ALWAYS_NORMALIZE){children=normalizeChildren(children)}elseif(normalizationType ===SIMPLE_NORMALIZE){
children =simpleNormalizeChildren(children)}letvnode,nsif(typeoftag==='string'){letCtorns=(context.$vnode&&context.$vnode.ns)||config.getTagNamespace(tag)if(config.isReservedTag(tag)){// platform built-in elements
vnode =newVNode(config.parsePlatformTagName(tag),data,children,undefined,undefined,context)}elseif(isDef(Ctor=resolveAsset(context.$options,'components',tag))){// component
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 =newVNode(tag,data,children,undefined,undefined,context)}}else{// direct component options / constructor
vnode =createComponent(tag,data,context,children)}if(Array.isArray(vnode)){
return vnode}elseif(isDef(vnode)){if(isDef(ns))applyNS(vnode,ns)if(isDef(data))registerDeepBindings(data)returnvnode}else{returncreateEmptyVNode()}}
最上面:
if(isDef(data)&&isDef((data: any).__ob__)){process.env.NODE_ENV!=='production'&&warn(`Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n`+'Always create fresh vnode data objects in each render!',context)returncreateEmptyVNode()}
它的意思是判断 data 是不是一个响应式的对象,如果 data 有 __ob__ 属性,那么它就是一个响应式的,在开发环境中就会报错,然后返回创建一个空节点,就是说不允许 VNode 的 data 是响应式的。创建空 VNode 代码在 src/core/vdom/vnode.js 中:
对 children 做了一层处理,首先 children 是一个数组,这个处理是判断 children 数组是单层嵌套还是多层,然后把它拍平,把 children 里的每一项都变成 VNode,代码如下:
// The template compiler attempts to minimize the need for normalization by// statically analyzing the template at compile time.//// For plain HTML markup, normalization can be completely skipped because the// generated render function is guaranteed to return Array<VNode>. There are// two cases where extra normalization is needed:// 1. When the children contains components - because a functional component// may return an Array instead of a single root. In this case, just a simple// normalization is needed - if any child is an Array, we flatten the whole// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep// because functional components already normalize their own children.exportfunctionsimpleNormalizeChildren(children: any){for(leti=0;i<children.length;i++){if(Array.isArray(children[i])){returnArray.prototype.concat.apply([],children)}}returnchildren}// 2. When the children contains constructs that always generated nested Arrays,// e.g. <template>, <slot>, v-for, or when the children is provided by user// with hand-written render functions / JSX. In such cases a full normalization// is needed to cater to all possible types of children values.exportfunctionnormalizeChildren(children: any): ?Array<VNode>{returnisPrimitive(children)
? [createTextVNode(children)]
: Array.isArray(children)
? normalizeArrayChildren(children)
: undefined}functionnormalizeArrayChildren(children: any,nestedIndex?: string): Array<VNode>{
const res=[]leti, c, lastIndex, last
for(i=0;i<children.length;i++){
c =children[i]if(isUndef(c)||typeofc==='boolean')continuelastIndex=res.length-1last=res[lastIndex]// nestedif(Array.isArray(c)){if(c.length>0){c=normalizeArrayChildren(c,`${nestedIndex||''}_${i}`)// merge adjacent text nodesif(isTextNode(c[0])&&isTextNode(last)){res[lastIndex]=createTextVNode(last.text+(c[0]: any).text)c.shift()}res.push.apply(res,c)}}elseif(isPrimitive(c)){if(isTextNode(last)){// merge adjacent text nodes// this is necessary for SSR hydration because text nodes are// essentially merged when rendered to HTML stringsres[lastIndex]=createTextVNode(last.text+c)}elseif(c!==''){// convert primitive to vnode
res.push(createTextVNode(c))}}else{if(isTextNode(c)&&isTextNode(last)){// merge adjacent text nodesres[lastIndex]=createTextVNode(last.text+c.text)}else{// default key for nested array children (likely generated by v-for)if(isTrue(children._isVList)&&isDef(c.tag)&&isUndef(c.key)&&isDef(nestedIndex)){c.key=`__vlist${nestedIndex}_${i}__`}res.push(c)}}}returnres}
上一篇讲到挂载是用
mountComponent
函数来进行的,而mountCompnent
函数里面最主要的是一个updateComponent
函数执行:这个函数在初次挂载和之后的更新都用到了,而
_render()
方法最终会调用$option.render
函数, 函数主要的目的就是创建VNode
,除了Virtual DOM
的定义之外,还有一个createElement
函数,本篇就来说下createElement
都做了什么事情,它在src/core/vdom/create-element.js
中。createElement
方法支持六个参数,context
其实就是vm实例,tag
是VNode
的标签,data
就是VNode
的数据,children
就是一些子节点,后两个就是两个标识符,下面的:是对传入参数的一种重载,简单一点就是对于传入的参数不一致的话,会做一点处理,把所有的参数都往后移一个,然后把
data
改成null
。后面赋值一个normalizationType
,紧接着返回一个_createElement
函数,然后把刚才的normalizationType
放进去,也就是说createElement
方法最终调用的是_createElement
,而createElement
本身其实只对参数做了一层处理,而真正创建VNode
的是_createElement
,那来看下这个函数的内容:最上面:
它的意思是判断
data
是不是一个响应式的对象,如果data
有__ob__
属性,那么它就是一个响应式的,在开发环境中就会报错,然后返回创建一个空节点,就是说不允许VNode
的data
是响应式的。创建空VNode
代码在src/core/vdom/vnode.js
中:接着它判断了
data.is
,如果不为true
,也返回一个空VNode
,后面也是对data
做了一层判定,继续又简单的判断了children
。到后面:
对
children
做了一层处理,首先children
是一个数组,这个处理是判断children
数组是单层嵌套还是多层,然后把它拍平,把children
里的每一项都变成VNode
,代码如下:以上就是
_createElement
做的第一件事情:拍平children
,第二件事就是创建vnode
,判定分别是:html元素,组件,或者无法识别的节点。最终vnode
就是render.js
里面vnode = render.call(vm._renderProxy, vm.$createElement)
的第二个参数,而render.call
的结果就是vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
中的createElement
的返回值,所以vnode
最终就是createElement
的结果,接着createElement
是vm._render
的返回值,再回到之前的updateComponent
:vm._update
的第一个参数就是刚才的createElement
的返回值,到此,拿到了一个完整的vnode
,下篇说一下_update
是将vnode
如何变成真实的DOM
节点。The text was updated successfully, but these errors were encountered: