Re-learn Vue source code, according to Huang Yi big man Vue technology revealed, one by one, consolidate the Vue source knowledge points, after all, chewed up is their own, all the articles are synchronized in the public number (road in the front stack) and github.
The body of the
MountComponent mountComponent mountComponent mountComponent mountComponent mountComponent mountComponent mountComponent mountComponent mountComponent mountComponent mountComponent mountComponent mountComponent mountComponent
updateComponent = () = > {
vm._update(vm._render(), hydrating)
}
Copy the code
This function is used for the initial mount and subsequent updates, and the _render() method ends up calling the $option.render function, whose main purpose is to create a VNode. In addition to the Virtual DOM definition, there is a createElement function, Here’s what createElement does. It’s in SRC /core/vdom/create-element.js.
const SIMPLE_NORMALIZE = 1
const ALWAYS_NORMALIZE = 2
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
}
if (isTrue(alwaysNormalize)) {
normalizationType = ALWAYS_NORMALIZE
}
return _createElement(context, tag, data, children, normalizationType)
}
Copy the code
The createElement method supports six identifiers: context (vm instance), tag (VNode tag), data (VNode data), children (children).
if (Array.isArray(data) || isPrimitive(data)) {
normalizationType = children
children = data
data = undefined
}
Copy the code
Overloading is an overloading of an incoming parameter. If the incoming parameter is inconsistent, it will do a little processing, move all the parameters back one, and change data to NULL. Assign a normalizationType, return a _createElement function, and place the normalizationType in it. The createElement method ends up calling _createElement, and createElement itself does only one layer of processing on the argument. The VNode is actually created by _createElement.
export function _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: The ${JSON.stringify(data)}\n` +
'Always create fresh vnode data objects in each render! ',
context
)
return createEmptyVNode()
}
// object syntax in v-bind
if (isDef(data) && isDef(data.is)) {
tag = data.is
}
if(! tag) {// in case of component :is set to falsy value
return createEmptyVNode()
}
// warn against non-primitive key
if(process.env.NODE_ENV ! = ='production'&& isDef(data) && isDef(data.key) && ! isPrimitive(data.key) ) {if(! __WEEX__ || ! ('@binding' in data.key)) {
warn(
'Avoid using non-primitive value as key, ' +
'use string/number value instead.',
context
)
}
}
// support single function children as default scoped slot
if (Array.isArray(children) &&
typeof children[0= = ='function'
) {
data = data || {}
data.scopedSlots = { default: children[0] }
children.length = 0
}
if (normalizationType === ALWAYS_NORMALIZE) {
children = normalizeChildren(children)
} else if (normalizationType === SIMPLE_NORMALIZE) {
children = simpleNormalizeChildren(children)
}
let vnode, ns
if (typeof tag === 'string') {
let Ctor
ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
if (config.isReservedTag(tag)) {
// platform built-in elements
vnode = new VNode(
config.parsePlatformTagName(tag), data, children,
undefined.undefined, context
)
} else if (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 = new VNode(
tag, data, children,
undefined.undefined, context
)
}
} else {
// direct component options / constructor
vnode = createComponent(tag, data, context, children)
}
if (Array.isArray(vnode)) {
return vnode
} else if (isDef(vnode)) {
if (isDef(ns)) applyNS(vnode, ns)
if (isDef(data)) registerDeepBindings(data)
return vnode
} else {
return createEmptyVNode()
}
}
Copy the code
At the top:
if(isDef(data) && isDef((data: any).__ob__)) { process.env.NODE_ENV ! = ='production' && warn(
`Avoid using observed data object as vnode data: The ${JSON.stringify(data)}\n` +
'Always create fresh vnode data objects in each render! ',
context
)
return createEmptyVNode()
}
Copy the code
If data has an __ob__ attribute, then it is a responsive object. In the development environment, an error will be reported and an empty node will be created. VNode data is not allowed to be responsive. Create an empty VNode in SRC /core/vdom/vnode.js:
export const createEmptyVNode = (text: string = ' ') = > {
const node = new VNode()
node.text = text
node.isComment = true
return node
}
Copy the code
It then checks data.is and returns an empty VNode if it is not true. It also checks data, and then simply checks children.
To:
if (normalizationType === ALWAYS_NORMALIZE) {
children = normalizeChildren(children)
} else if (normalizationType === SIMPLE_NORMALIZE) {
children = simpleNormalizeChildren(children)
}
Copy the code
VNode = VNode; VNode = VNode; VNode = VNode; VNode = VNode; VNode = 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.
export function simpleNormalizeChildren (children: any) {
for (let i = 0; i < children.length; i++) {
if (Array.isArray(children[i])) {
return Array.prototype.concat.apply([], children)
}
}
return children
}
// 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.
export function normalizeChildren (children: any): ?Array<VNode> {
return isPrimitive(children)
? [createTextVNode(children)]
: Array.isArray(children)
? normalizeArrayChildren(children)
: undefined
}
function normalizeArrayChildren (children: any, nestedIndex? : string) :Array<VNode> {
const res = []
let i, c, lastIndex, last
for (i = 0; i < children.length; i++) {
c = children[i]
if (isUndef(c) || typeof c === 'boolean') continue
lastIndex = res.length - 1
last = res[lastIndex]
// nested
if (Array.isArray(c)) {
if (c.length > 0) {
c = normalizeArrayChildren(c, `${nestedIndex || ' '}_${i}`)
// merge adjacent text nodes
if (isTextNode(c[0]) && isTextNode(last)) {
res[lastIndex] = createTextVNode(last.text + (c[0]: any).text)
c.shift()
}
res.push.apply(res, c)
}
} else if (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 strings
res[lastIndex] = createTextVNode(last.text + c)
} else if(c ! = =' ') {
// convert primitive to vnode
res.push(createTextVNode(c))
}
} else {
if (isTextNode(c) && isTextNode(last)) {
// merge adjacent text nodes
res[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)
}
}
}
return res
}
Copy the code
That’s the first thing _createElement does: flatten children, and the second thing it does is create a vNode to determine whether it’s an HTML element, a component, or an unrecognized node. Vnode = render. Call (vm._renderProxy, vm.$createElement); _c = (a, b, c, d) => createElement(vm, a, b, c, d, false); So vNode is ultimately the result of createElement, which is the return value of vm._render, and then back to updateComponent:
updateComponent = () = > {
vm._update(vm._render(), hydrating)
}
Copy the code
The first parameter to vm._update is the return value of createElement. Now that we have a complete VNode, we’ll see how _update turns vNodes into real DOM nodes.