feat(runtime): 添加 h 函数和虚拟节点系统
- 实现了 h 函数用于创建虚拟节点 - 添加了 VNode 接口定义和创建逻辑 - 引入了 ShapeFlags 枚举来标记节点类型 - 实现了虚拟节点子元素标准化功能 - 在 runtime-core 中导出 h 函数 - 添加了 h 函数使用示例页面
This commit is contained in:
25
packages/runtime-core/src/h.ts
Normal file
25
packages/runtime-core/src/h.ts
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export { queuePreFlushCb } from './scheduler'
|
export { queuePreFlushCb } from './scheduler'
|
||||||
export { watch } from './apiWatch'
|
export { watch } from './apiWatch'
|
||||||
|
export { h } from './h'
|
||||||
|
|||||||
43
packages/runtime-core/src/vnode.ts
Normal file
43
packages/runtime-core/src/vnode.ts
Normal 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
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
export { ShapeFlags } from './shapeFlags'
|
||||||
//判断是否为一个数组
|
//判断是否为一个数组
|
||||||
export const isArray = Array.isArray
|
export const isArray = Array.isArray
|
||||||
|
|
||||||
@@ -14,6 +15,10 @@ export const isFunction = (val: unknown): val is Function => {
|
|||||||
return typeof val === 'function'
|
return typeof val === 'function'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isString = (val: unknown): val is string => {
|
||||||
|
return typeof val === 'string'
|
||||||
|
}
|
||||||
|
|
||||||
export const extend = Object.assign
|
export const extend = Object.assign
|
||||||
|
|
||||||
export const EMPTY_OBJ: { readonly [key: string]: any } = {}
|
export const EMPTY_OBJ: { readonly [key: string]: any } = {}
|
||||||
|
|||||||
30
packages/shared/src/shapeFlags.ts
Normal file
30
packages/shared/src/shapeFlags.ts
Normal 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
|
||||||
|
}
|
||||||
18
packages/vue/examples/runtime/h-element.html
Normal file
18
packages/vue/examples/runtime/h-element.html
Normal 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>
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
export { reactive, effect, ref, computed } from '@vue/reactivity'
|
export { reactive, effect, ref, computed } from '@vue/reactivity'
|
||||||
export { queuePreFlushCb, watch } from '@vue/runtime-core'
|
export { queuePreFlushCb, watch, h } from '@vue/runtime-core'
|
||||||
|
|||||||
Reference in New Issue
Block a user