第八章 渲染系统的 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 渲染函数的开发!

相关推荐
小小小小宇2 小时前
手写 zustand
前端
Hamm2 小时前
用装饰器和ElementPlus,我们在NPM发布了这个好用的表格组件包
前端·vue.js·typescript
小小小小宇3 小时前
前端国际化看这一篇就够了
前端
大G哥3 小时前
PHP标签+注释+html混写+变量
android·开发语言·前端·html·php
whoarethenext3 小时前
html初识
前端·html
小小小小宇3 小时前
一个功能相对完善的前端 Emoji
前端
m0_627827523 小时前
vue中 vue.config.js反向代理
前端
Java&Develop3 小时前
onloyoffice历史版本功能实现,版本恢复功能,编辑器功能实现 springboot+vue2
前端·spring boot·编辑器
白泽talk3 小时前
2个小时1w字| React & Golang 全栈微服务实战
前端·后端·微服务
摆烂工程师3 小时前
全网最详细的5分钟快速申请一个国际 “edu教育邮箱” 的保姆级教程!
前端·后端·程序员