feat(runtime): 添加 h 函数和虚拟节点系统

- 实现了 h 函数用于创建虚拟节点
- 添加了 VNode 接口定义和创建逻辑
- 引入了 ShapeFlags 枚举来标记节点类型
- 实现了虚拟节点子元素标准化功能
- 在 runtime-core 中导出 h 函数
- 添加了 h 函数使用示例页面
This commit is contained in:
dj
2026-02-26 22:35:22 +08:00
parent 164bae388f
commit a3a7b8c7e4
7 changed files with 123 additions and 1 deletions

View File

@@ -0,0 +1,25 @@
import { createVNode, isVNode, VNode } from './vnode'
import { isObject } from '@vue/shared'
export function h(type: any, propsOrChildren?: any, children?: any): VNode {
const l = arguments.length
if (l === 2) {
//是对象
if (isObject(propsOrChildren) && !Array.isArray(propsOrChildren)) {
if (isVNode(propsOrChildren)) {
return createVNode(type, null, [propsOrChildren])
}
return createVNode(type, propsOrChildren, [])
} else {
//是数组
return createVNode(type, null, propsOrChildren)
}
} else {
if (l > 3) {
children = Array.prototype.slice.call(arguments, 2) //生成新的children
} else if (l === 3 && isVNode(children)) {
children = [children]
}
return createVNode(type, propsOrChildren, children)
}
}

View File

@@ -1,2 +1,3 @@
export { queuePreFlushCb } from './scheduler'
export { watch } from './apiWatch'
export { h } from './h'

View File

@@ -0,0 +1,43 @@
import { isArray, isFunction, isString, ShapeFlags } from '@vue/shared'
export interface VNode {
__v_isVNode: true
type: any
props: any
children: any
shapeFlag: number
}
export function isVNode(value): value is VNode {
return value ? value.__v_isVNode === true : false
}
export function createVNode(type, props, children): VNode {
const shapeFlag = isString(type) ? ShapeFlags.ELEMENT : 0
return createBaseVNode(type, props, children, shapeFlag)
}
function createBaseVNode(type, props, children, shapeFlag) {
const vnode = {
__v_isVNode: true,
type,
props,
shapeFlag
} as VNode
//解析/标准化当前vnode下的children
normalizeChildren(vnode, children)
return vnode
}
export function normalizeChildren(vnode: VNode, children: unknown) {
let type = 0
const { shapeFlag } = vnode
if (children == null) {
children = null
} else if (isArray(children)) {
} else if (typeof children === 'object') {
} else if (isFunction(children)) {
} else {
children = String(children)
type = ShapeFlags.TEXT_CHILDREN
}
vnode.children = children
vnode.shapeFlag |= type
}

View File

@@ -1,3 +1,4 @@
export { ShapeFlags } from './shapeFlags'
//判断是否为一个数组
export const isArray = Array.isArray
@@ -14,6 +15,10 @@ export const isFunction = (val: unknown): val is Function => {
return typeof val === 'function'
}
export const isString = (val: unknown): val is string => {
return typeof val === 'string'
}
export const extend = Object.assign
export const EMPTY_OBJ: { readonly [key: string]: any } = {}

View File

@@ -0,0 +1,30 @@
export const enum ShapeFlags {
/**
* type=Element
*/
ELEMENT = 1,
/**
* 函数组件
*/
FUNCTIONAL_COMPONENT = 1 << 1,
/**
* 有状态(响应数据)组件
*/
STATEFUL_COMPONENT = 1 << 2,
/**
* children=Text
*/
TEXT_CHILDREN = 1 << 3,
/**
* children=Array
*/
ARRAY_CHILDREN = 1 << 4,
/**
* children=slot
*/
SLOTS__CHILDREN = 1 << 5,
/**
* 组件: 有状态(响应数据)组件 | 函数组件
*/
COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
}

View File

@@ -0,0 +1,18 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Document</title>
<script src="../../dist/vue.js"></script>
</head>
<body>
<div id="app"></div>
</body>
<script>
const {h} =Vue
const vnode=h('div',{
class:'test'
},'hello render')
console.log(vnode)
</script>
</html>

View File

@@ -1,2 +1,2 @@
export { reactive, effect, ref, computed } from '@vue/reactivity'
export { queuePreFlushCb, watch } from '@vue/runtime-core'
export { queuePreFlushCb, watch, h } from '@vue/runtime-core'