第八章 渲染系统的 h函数 实现【手摸手带你实现一个vue3】

第八章:框架实现:h 函数

大家好,我是作曲家种太阳,这一张来实现一下 h函数

步骤1:创建 ShapeFlags 枚举

在 packages/shared/src/shapeFlags.ts 中定义所有 VNode 类型标识:

ts 复制代码
export const enum ShapeFlags {
  ELEMENT = 1,
  FUNCTIONAL_COMPONENT = 1 << 1,
  STATEFUL_COMPONENT = 1 << 2,
  TEXT_CHILDREN = 1 << 3,
  ARRAY_CHILDREN = 1 << 4,
  SLOTS_CHILDREN = 1 << 5,
  COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
}

步骤2:实现 h 函数

在 packages/runtime-core/src/h.ts 中构建 h 函数:

ts 复制代码
import { isArray, isObject } from '@vue/shared'
import { createVNode, isVNode, VNode } from './vnode'

export function h(type: any, propsOrChildren?: any, children?: any): VNode {
	// 获取用户传递的参数数量
	const l = arguments.length
	// 如果用户只传递了两个参数,那么证明第二个参数可能是 props , 也可能是 children
	if (l === 2) {
		// 如果 第二个参数是对象,但不是数组。则第二个参数只有两种可能性:1. VNode 2.普通的 props
		if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
			// 如果是 VNode,则 第二个参数代表了 children
			if (isVNode(propsOrChildren)) {
				return createVNode(type, null, [propsOrChildren])
			}
			// 如果不是 VNode, 则第二个参数代表了 props
			return createVNode(type, propsOrChildren)
		}
		// 如果第二个参数不是单纯的 object,则 第二个参数代表了 props
		else {
			return createVNode(type, null, propsOrChildren)
		}
	}
	// 如果用户传递了三个或以上的参数,那么证明第二个参数一定代表了 props
	else {
		// 如果参数在三个以上,则从第二个参数开始,把后续所有参数都作为 children
		if (l > 3) {
			children = Array.prototype.slice.call(arguments, 2)
		}
		// 如果传递的参数只有三个,则 children 是单纯的 children
		else if (l === 3 && isVNode(children)) {
			children = [children]
		}
		// 触发 createVNode 方法,创建 VNode 实例
		return createVNode(type, propsOrChildren, children)
	}
}

步骤3:构建 VNode 类型

在 packages/runtime-core/src/vnode.ts 中:

ts 复制代码
import { isArray, isFunction, isObject, isString } from '@vue/shared'
import { normalizeClass } from 'packages/shared/src/normalizeProp'
import { ShapeFlags } from 'packages/shared/src/shapeFlags'

export const Fragment = Symbol('Fragment')
export const Text = Symbol('Text')
export const Comment = Symbol('Comment')

/**
 * VNode
 */
export interface VNode {
	__v_isVNode: true
	key: any
	type: any
	props: any
	children: any
	shapeFlag: number
}

export function isVNode(value: any): value is VNode {
	return value ? value.__v_isVNode === true : false
}

/**
 * 生成一个 VNode 对象,并返回
 * @param type vnode.type
 * @param props 标签属性或自定义属性
 * @param children? 子节点
 * @returns vnode 对象
 */
export function createVNode(type, props, children?): VNode {
	// 通过 bit 位处理 shapeFlag 类型
	const shapeFlag = isString(type)
		? ShapeFlags.ELEMENT
		: isObject(type)
		? ShapeFlags.STATEFUL_COMPONENT
		: 0

	if (props) {
		// 处理 class
		let { class: klass, style } = props
		if (klass && !isString(klass)) {
			props.class = normalizeClass(klass)
		}
	}

	return createBaseVNode(type, props, children, shapeFlag)
}

/**
 * 构建基础 vnode
 */
function createBaseVNode(type, props, children, shapeFlag) {
	const vnode = {
		__v_isVNode: true,
		type,
		props,
		shapeFlag,
		key: props?.key || null
	} as VNode

	normalizeChildren(vnode, children)

	return vnode
}

export { createVNode as createElementVNode }

/**
 * 创建注释节点
 */
export function createCommentVNode(text) {
	return createVNode(Comment, null, text)
}

export function normalizeChildren(vnode: VNode, children: unknown) {
	let type = 0
	const { shapeFlag } = vnode
	if (children == null) {
		children = null
	} else if (isArray(children)) {
		type = ShapeFlags.ARRAY_CHILDREN
	} else if (typeof children === 'object') {
		// TODO: object
	} else if (isFunction(children)) {
		// TODO: function
	} else {
		// children 为 string
		children = String(children)
		// 为 type 指定 Flags
		type = ShapeFlags.TEXT_CHILDREN
	}
	// 修改 vnode 的 chidlren
	vnode.children = children
	// 按位或赋值
	vnode.shapeFlag |= type
}

/**
 * 根据 key || type 判断是否为相同类型节点
 */
export function isSameVNodeType(n1: VNode, n2: VNode): boolean {
	return n1.type === n2.type && n1.key === n2.key
}

步骤3:isString实现

在 packages/shared/src/index.ts 中:

js 复制代码
/**
 * 判断是否为一个 string
 */
export const isString = (val: unknown): val is string => typeof val === 'string'

步骤4:实现

(后面会讲解 createVNode 细节,这里先留空)

步骤5:导出 h 函数

在 index.ts 导出 h。


小结

VNode 核心属性:

  • type
  • props
  • children
  • shapeFlag
  • __v_isVNode

整个 h 函数和 VNode 的搭建,奠定了 Vue3 渲染系统的基础结构。

下一步,我们将开始真正进入 render 渲染函数的开发!

相关推荐
IT_陈寒39 分钟前
Java中equals方法比了个寂寞?原来这才是正确的重写姿势
前端·人工智能·后端
默_笙39 分钟前
🚓 分诊台与拆题术:让 RAG 学会判断和规划
前端·javascript
CopyCode39 分钟前
用 AI 迁项目有多爽?我把 Webpack 迁 Vite 的全过程记下来了
前端·架构
去伪存真39 分钟前
Electron 自动化发布指南:GitHub Actions 跨平台打包全纪录
前端·electron
计算机魔术师40 分钟前
OpenAI智能体失控闯进美国政府网站,53张用户图片外泄背后
前端
颜进强40 分钟前
14 · NestJS ExecutionContext 执行上下文:守卫、拦截器、过滤器拿到的"同一个 context",为什么能力不一样?
前端·后端·ai编程
程序员Flycan40 分钟前
🚀 跨域终结者:前端代理服务器(Proxy)原理解析与配置总结
前端
怕浪猫40 分钟前
顶级模型一句话,AI 写出了能玩的 QQ飞车
前端·面试·github
huakoh40 分钟前
MCP 报错分不清?先看响应里是 result 还是 error
前端
呃呃呃呃ex40 分钟前
10. 现代前端工程化:ES6 React 项目实战与原理解析
前端·javascript