From a3a7b8c7e4218db717d38252249d17cf5c4f6cc9 Mon Sep 17 00:00:00 2001 From: dj <1042039504@qq.com> Date: Thu, 26 Feb 2026 22:35:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(runtime):=20=E6=B7=BB=E5=8A=A0=20h=20?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E5=92=8C=E8=99=9A=E6=8B=9F=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现了 h 函数用于创建虚拟节点 - 添加了 VNode 接口定义和创建逻辑 - 引入了 ShapeFlags 枚举来标记节点类型 - 实现了虚拟节点子元素标准化功能 - 在 runtime-core 中导出 h 函数 - 添加了 h 函数使用示例页面 --- packages/runtime-core/src/h.ts | 25 ++++++++++++ packages/runtime-core/src/index.ts | 1 + packages/runtime-core/src/vnode.ts | 43 ++++++++++++++++++++ packages/shared/src/index.ts | 5 +++ packages/shared/src/shapeFlags.ts | 30 ++++++++++++++ packages/vue/examples/runtime/h-element.html | 18 ++++++++ packages/vue/src/index.ts | 2 +- 7 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 packages/runtime-core/src/h.ts create mode 100644 packages/runtime-core/src/vnode.ts create mode 100644 packages/shared/src/shapeFlags.ts create mode 100644 packages/vue/examples/runtime/h-element.html diff --git a/packages/runtime-core/src/h.ts b/packages/runtime-core/src/h.ts new file mode 100644 index 0000000..e494433 --- /dev/null +++ b/packages/runtime-core/src/h.ts @@ -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) + } +} diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index c1885a8..a2fbb14 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -1,2 +1,3 @@ export { queuePreFlushCb } from './scheduler' export { watch } from './apiWatch' +export { h } from './h' diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts new file mode 100644 index 0000000..4c5865a --- /dev/null +++ b/packages/runtime-core/src/vnode.ts @@ -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 +} diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index f740520..84dee3c 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -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 } = {} diff --git a/packages/shared/src/shapeFlags.ts b/packages/shared/src/shapeFlags.ts new file mode 100644 index 0000000..7a06c3f --- /dev/null +++ b/packages/shared/src/shapeFlags.ts @@ -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 +} diff --git a/packages/vue/examples/runtime/h-element.html b/packages/vue/examples/runtime/h-element.html new file mode 100644 index 0000000..2752667 --- /dev/null +++ b/packages/vue/examples/runtime/h-element.html @@ -0,0 +1,18 @@ + +
+ + +