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

相关推荐
nppe62 分钟前
sequlize操作mysql小记
前端·后端
Moment11 分钟前
面试官:一个接口使用postman这些测试很快,但是页面加载很慢怎么回事 😤😤😤
前端·后端·面试
诗书画唱15 分钟前
【前端面试题】JavaScript 核心知识点解析(第二十二题到第六十一题)
开发语言·前端·javascript
excel21 分钟前
前端必备:从能力检测到 UA-CH,浏览器客户端检测的完整指南
前端
前端小巷子28 分钟前
Vue 3全面提速剖析
前端·vue.js·面试
悟空聊架构35 分钟前
我的网站被攻击了,被干掉了 120G 流量,还在持续攻击中...
java·前端·架构
CodeSheep36 分钟前
国内 IT 公司时薪排行榜。
前端·后端·程序员
尖椒土豆sss40 分钟前
踩坑vue项目中使用 iframe 嵌套子系统无法登录,不报错问题!
前端·vue.js
遗悲风41 分钟前
html二次作业
前端·html
江城开朗的豌豆44 分钟前
React输入框优化:如何精准获取用户输入完成后的最终值?
前端·javascript·全栈