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

相关推荐
i听风逝夜9 小时前
Web 3D地球实时统计访问来源
前端·后端
iMonster9 小时前
React 组件的组合模式之道 (Composition Pattern)
前端
呐呐呐呐呢9 小时前
antd渐变色边框按钮
前端
元直数字电路验证10 小时前
Jakarta EE Web 聊天室技术梳理
前端
wadesir10 小时前
Nginx配置文件CPU优化(从零开始提升Web服务器性能)
服务器·前端·nginx
牧码岛10 小时前
Web前端之canvas实现图片融合与清晰度介绍、合并
前端·javascript·css·html·web·canvas·web前端
灵犀坠10 小时前
前端面试八股复习心得
开发语言·前端·javascript
9***Y4810 小时前
前端动画性能优化
前端
网络点点滴10 小时前
Vue3嵌套路由
前端·javascript·vue.js
牧码岛10 小时前
Web前端之Vue+Element打印时输入值没有及时更新dom的问题
前端·javascript·html·web·web前端