h函數作為創(chuàng)建vnode對象的函數的封裝君旦,
在vnode創(chuàng)建時確定其flag
function h () {
return {
_isVNode: true,
flags: VNodeFlags.ELEMENT_HTML,
tag: 'h1',
data: null,
children: null,
childFlags: ChildrenFlags.NO_CHILDREN,
el: null
};
}
為了讓h函數更靈活昂验,我們可以添加參數店煞,我們只需要把tag获讳、data 和 children提取為參數即可
function h (tag, data,children) {/*...*/}
對于flags屬性,我們可以通過tag屬性值的特征來判斷
function h (tag, data,children) {
let flags = null;
if (typeof tag === 'string') {
flags = tag === 'svg' ? VNodeFlags. ELEMENT_SVG : VNodeFlags.ELEMENT_HTML;
}
}
對于Fragment
類型的VNode
export const Fragment = Symbol()
function h (tag, data, children) {
let flags = null;
if (typeof tag === 'string') {
flags = tag === 'svg' ? VNodeFlags.ELEMENT_SVG : VNodeFlags.ELEMENT_HTML
} else if (tag === Fragment) {
flags = VNodeFlags.FRAGMENT
}
}
這時我們就可以像如下這樣調用h函數愤诱,創(chuàng)建Fragment
import { Fragment, h } from 'vue'
h(Fragment, null, chidren)
類似的對于Portal類型的VNode云头,他的tag屬性也可以是字符串,這樣就和普通標簽的VNode沖突淫半,所以給Portal一個唯一標識
export const Fragment = Symbol()
export const Portal = Symbol()
function h (tag, data, children) {
let flags = null;
if (typeof tag === 'string') {
flags = tag === 'svg' ? VNodeFlags.ELEMENT_SVG : VNodeFlags.ELEMENT_HTML
} else if (tag === Fragment) {
flags = VNodeFlags.FRAGMENT
} else if (tag === Portal) {
flags = VNodeFlags.Portal
tag = data && data.target
}
}
不滿足以上所有條件的話溃槐,就是組件了。
當是文本的時候科吭,一般是直接在children參數傳文本
export const Fragment = Symbol()
export const Portal = Symbol()
function h (tag, data, children) {
let flags = null;
if (typeof tag === 'string') {
flags = tag === 'svg' ? VNodeFlags.ELEMENT_SVG : VNodeFlags.ELEMENT_HTML
} else if (tag === Fragment) {
flags = VNodeFlags.FRAGMENT
} else if (tag === Portal) {
flags = VNodeFlags.Portal
tag = data && data.target
} else if (typeof tag === 'function') {
flags = tag.prototype && tag.prototype.render
? VNodeFlags.COMPONENT_STATEFUL_NORMAL // 有狀態(tài)組件
: VNodeFlags.COMPONENT_FUNCTIONAL
}
}
在vue2中昏滴,用一個對象作為組件的描述,vue3中对人,是一個繼承了基類的類谣殊,所以可以通過原型鏈中是否有render函數的定義來判斷是否是有狀態(tài)組件
一旦確定了vnode的類型就可以返回帶有正確類型的vnode。
在vnode創(chuàng)建時確定其children的類型
可以通過檢測chidren來確定childFlags的值牺弄。根據h函數的調用可以很容易的確定參數children包含哪些值姻几。
1.children是一個數組
h('ul', null, [
h('li'),
h('li')
])
- chidren是一個vnode對象
h('div', null, h('span'))
- 沒有children
h('div')
4、children 是一個普通文本字符串:
h('div', null, '我是文本')
以上是常見的h函數的調用方式势告,根據這四種方式中children的不同形式鲜棠,就可以確定childFlags的值
function h (tag, data, children) {
// 省略flag部分的代碼
if (Array.isArray(children)) {
const { length } = children
if (length === 0) {
childFlags = ChildrenFlags.NO_CHILDREN
} else if (length === 1) {
childFlags = ChildrenFlags.SINGLE_VNODE
children = children[0]
} else {
// 多個子節(jié)點
childFlags = ChildrenFlags.KEYED_VNODES
children = normalizeVNodes(children)
}
} else if (children === null) {
childFlags = ChildrenFlags.NO_CHILDREN
} else if (children._isVNode) {
// 單個子節(jié)點
childFlags = ChildrenFlags.SINGLE_VNODE
} else {
// 其他情況都作為文本節(jié)點處理
childFlags = ChildrenFlags.SINGLE_VNODE
children = createTextVNode(children + '')
}
}
為什么多個子節(jié)點的時候會當做有key的vnodes?因為key是可以認為創(chuàng)造的
function normalizeVNodes (children) {
const newChildren = []
for (let i = 0; i < children.length; i++) {
const child = children[i]
if (!child.key) {
// 如果原來的vnode中沒有key培慌,則key就用豎線|與該VNode在數組中的索引拼接而成的字符串作為key
child.key = '|' + i
newChildren.push(child)
}
}
// 返回新的children豁陆,此時 children 的類型就是ChildrenFlags.KEYED_VNODES
return newChildren
}
function createTextVNode (text) {
return {
_isVNode: true,
// flags 是 VNodeFlags.TEXT
flags: VNodeFlags.TEXT,
tag: null,
data: null,
// 純文本類型的 VNode,其 children 屬性存儲的是與之相符的文本內容
children: text,
// 文本節(jié)點沒有子節(jié)點
childFlags: ChildrenFlags.NO_CHILDREN,
el: null
}
}
以上的childFlags只適用于非組件類型的vnode吵护,因為對于組件類型vnode來說盒音,他并沒有子節(jié)點,所有的子節(jié)點都應該作為slots存在馅而,所以如果使用h函數創(chuàng)建一個組件類型的vnode祥诽,那么我們應該把children的內容轉化為slots。