Vue3源码梳理:运行时之基于h函数生成vnode的内部流程

本文主要是介绍Vue3源码梳理:运行时之基于h函数生成vnode的内部流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

VNode 节点类型

  • 对于vnode而言,具备很多节点类型
  • vue源码中patch函数switch处理包含了好几种类型,常见类型如下
    • Text:文本节点
    • Comment:注释节点
    • Static:静态dom节点
    • Fragment:包含多个根节点的模板被表示为一个片段 fragment
    • ELEMENT:DOM 节点
    • COMPONENT:组件
    • TELEPORT:新的内置组件
    • SUSPENSE:新的内置组件

h函数源码解析

1 )使用 h 函数,示例demo程序

<script src='../../dist/vue.global.js'></script><div id='app'></div><script>const { h } = Vueconst vnode = h('div', {class: 'test'}, 'hello render')console.log('vnode: ', vnode)
</script>

2 )对源码进行debug, 进入h函数

// Actual implementation
export function h(type: any, propsOrChildren?: any, children?: any): VNode {const l = arguments.lengthif (l === 2) {if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {// single vnode without propsif (isVNode(propsOrChildren)) {return createVNode(type, null, [propsOrChildren])}// props without childrenreturn createVNode(type, propsOrChildren)} else {// omit propsreturn createVNode(type, null, propsOrChildren)}} else {if (l > 3) {children = Array.prototype.slice.call(arguments, 2)} else if (l === 3 && isVNode(children)) {children = [children]}return createVNode(type, propsOrChildren, children)}
}

h 函数需要三个参数: type, propsOrChildren, children

  • 注意第二个参数,propsOrChildren 是一个对象,它可以是props也可以是children
  • 内部是基于传入的长度和类型来判断的,先长度(先基于2来判断的)后类型
  • 最终返回 createVNode
  • h函数本身只是对用户传递的参数的处理,其本质是 createVNode
  • 使得 createVNode调用时,更加的方便

3 ) createVNode 源码


export const createVNode = (__DEV__ ? createVNodeWithArgsTransform : _createVNode
) as typeof _createVNodefunction _createVNode(type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,props: (Data & VNodeProps) | null = null,children: unknown = null,patchFlag: number = 0,dynamicProps: string[] | null = null,isBlockNode = false
): VNode {if (!type || type === NULL_DYNAMIC_COMPONENT) {if (__DEV__ && !type) {warn(`Invalid vnode type when creating vnode: ${type}.`)}type = Comment}if (isVNode(type)) {// createVNode receiving an existing vnode. This happens in cases like// <component :is="vnode"/>// #2078 make sure to merge refs during the clone instead of overwriting itconst cloned = cloneVNode(type, props, true /* mergeRef: true */)if (children) {normalizeChildren(cloned, children)}if (isBlockTreeEnabled > 0 && !isBlockNode && currentBlock) {if (cloned.shapeFlag & ShapeFlags.COMPONENT) {currentBlock[currentBlock.indexOf(type)] = cloned} else {currentBlock.push(cloned)}}cloned.patchFlag |= PatchFlags.BAILreturn cloned}// class component normalization.if (isClassComponent(type)) {type = type.__vccOpts}// 2.x async/functional component compatif (__COMPAT__) {type = convertLegacyComponent(type, currentRenderingInstance)}// class & style normalization.if (props) {// for reactive or proxy objects, we need to clone it to enable mutation.props = guardReactiveProps(props)!let { class: klass, style } = propsif (klass && !isString(klass)) {props.class = normalizeClass(klass)}if (isObject(style)) {// reactive state objects need to be cloned since they are likely to be// mutatedif (isProxy(style) && !isArray(style)) {style = extend({}, style)}props.style = normalizeStyle(style)}}// encode the vnode type information into a bitmapconst shapeFlag = isString(type)? ShapeFlags.ELEMENT: __FEATURE_SUSPENSE__ && isSuspense(type)? ShapeFlags.SUSPENSE: isTeleport(type)? ShapeFlags.TELEPORT: isObject(type)? ShapeFlags.STATEFUL_COMPONENT: isFunction(type)? ShapeFlags.FUNCTIONAL_COMPONENT: 0if (__DEV__ && shapeFlag & ShapeFlags.STATEFUL_COMPONENT && isProxy(type)) {type = toRaw(type)warn(`Vue received a Component which was made a reactive object. This can ` +`lead to unnecessary performance overhead, and should be avoided by ` +`marking the component with \`markRaw\` or using \`shallowRef\` ` +`instead of \`ref\`.`,`\nComponent that was made reactive: `,type)}return createBaseVNode(type,props,children,patchFlag,dynamicProps,shapeFlag,isBlockNode,true)
}
  • 其本质上触发的是 _createVNode,进入它,有6个参数
  • type, props, children, patchFlag, dynamicProps, isBlockNode,我们主要关注其中三个参数
    • type
    • props
    • children
  • 代码往下走,看下 isVNode 函数,判断比较简单
    • return value ? value.__v_isVNode === true: false
    • 就是根据value的属性来的
  • 之后在判断是否是class
  • 在之后判断 props,这里执行
    • guardReactiveProps(props) 解析props的逻辑暂时不去管它
      • vue会有class和style的增强,这块先不去管它
  • 之后走到一个比较复杂的三目运算 shapeFlag
  • 它本身是一个枚举类,定义了很多类型
  • 代码继续执行,直到 return createBaseVNode

createBaseVNode 函数

function createBaseVNode(type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,props: (Data & VNodeProps) | null = null,children: unknown = null,patchFlag = 0,dynamicProps: string[] | null = null,shapeFlag = type === Fragment ? 0 : ShapeFlags.ELEMENT,isBlockNode = false,needFullChildrenNormalization = false
) {const vnode = {__v_isVNode: true,__v_skip: true,type,props,key: props && normalizeKey(props),ref: props && normalizeRef(props),scopeId: currentScopeId,slotScopeIds: null,children,component: null,suspense: null,ssContent: null,ssFallback: null,dirs: null,transition: null,el: null,anchor: null,target: null,targetAnchor: null,staticCount: 0,shapeFlag,patchFlag,dynamicProps,dynamicChildren: null,appContext: null} as VNodeif (needFullChildrenNormalization) {normalizeChildren(vnode, children)// normalize suspense childrenif (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {;(type as typeof SuspenseImpl).normalize(vnode)}} else if (children) {// compiled element vnode - if children is passed, only possible types are// string or Array.vnode.shapeFlag |= isString(children)? ShapeFlags.TEXT_CHILDREN: ShapeFlags.ARRAY_CHILDREN}// validate keyif (__DEV__ && vnode.key !== vnode.key) {warn(`VNode created with invalid key (NaN). VNode type:`, vnode.type)}// track vnode for block treeif (isBlockTreeEnabled > 0 &&// avoid a block node from tracking itself!isBlockNode &&// has current parent blockcurrentBlock &&// presence of a patch flag indicates this node needs patching on updates.// component nodes also should always be patched, because even if the// component doesn't need to update, it needs to persist the instance on to// the next vnode so that it can be properly unmounted later.(vnode.patchFlag > 0 || shapeFlag & ShapeFlags.COMPONENT) &&// the EVENTS flag is only for hydration and if it is the only flag, the// vnode should not be considered dynamic due to handler caching.vnode.patchFlag !== PatchFlags.HYDRATE_EVENTS) {currentBlock.push(vnode)}if (__COMPAT__) {convertLegacyVModelProps(vnode)defineLegacyVNodeProperties(vnode)}return vnode
}
  • 进入这个函数
    • type, props, children, patchFlag, dynamicProps, shapeFlag, isBlockNode, needFullChildrenNormalization
    • 接下来,创建 vnode对象,包含
      • __v_isVNode
    • 这时候构建出了一个初始的vnode对象
      • 初始化很多属性,我们只需要关注对我们有用的
  • 继续执行,到 normalizeChildren(vnode, children)
    • 这个函数里面涉及到一个 进位符 & 和 按位或赋值 |=
    • |= 这里是按位或运算
    • 这里展开下:
      • 10进制的1转换成二进制是: 01,
      • 10(2) === 2(10) 括号里面是进制
      • 在vue的运算里,其实他们都是32位的
    • 32位是指有32个比特位
      • 00000000 00000000 00000000 00000000
    • 二进制的1是:
      • 00000000 00000000 00000000 00000001
      • 当前调试debug的flag的值,10进制是1,也是如上表示
    • 二进制的8是:
      • 00000000 00000000 00000000 00001000
    • 上述1和8执行或运算(有一个1则是1),得到
      • 00000000 00000000 00000000 00001001

总结下

  • h函数本质上是处理一个参数的问题
  • 核心代码是在 _createVNode 中进行的
  • 里面生成vnode的核心方法,做了一件重要的事情是构建了一个 shapeFlag
  • 第一次构建的时候,它的flag是ELEMENT类型
  • 接下来return 了 createBaseVNode 函数
  • 它根据 type, props, childrenshapeFlag 生成了一个 vnode 节点
  • 通过按位或运算,来改变flag的值,重新赋值给 shapeFlag
  • 最终 return vnode 对象

这篇关于Vue3源码梳理:运行时之基于h函数生成vnode的内部流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/506302

相关文章

CSS place-items: center解析与用法详解

《CSSplace-items:center解析与用法详解》place-items:center;是一个强大的CSS简写属性,用于同时控制网格(Grid)和弹性盒(Flexbox)... place-items: center; 是一个强大的 css 简写属性,用于同时控制 网格(Grid) 和 弹性盒(F

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

CSS Anchor Positioning重新定义锚点定位的时代来临(最新推荐)

《CSSAnchorPositioning重新定义锚点定位的时代来临(最新推荐)》CSSAnchorPositioning是一项仍在草案中的新特性,由Chrome125开始提供原生支持需... 目录 css Anchor Positioning:重新定义「锚定定位」的时代来了! 什么是 Anchor Pos

CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比

《CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比》CSS中的position属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布... css 中的 position 属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布局和层叠关

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

全面解析HTML5中Checkbox标签

《全面解析HTML5中Checkbox标签》Checkbox是HTML5中非常重要的表单元素之一,通过合理使用其属性和样式自定义方法,可以为用户提供丰富多样的交互体验,这篇文章给大家介绍HTML5中C... 在html5中,Checkbox(复选框)是一种常用的表单元素,允许用户在一组选项中选择多个项目。本

HTML5 搜索框Search Box详解

《HTML5搜索框SearchBox详解》HTML5的搜索框是一个强大的工具,能够有效提升用户体验,通过结合自动补全功能和适当的样式,可以创建出既美观又实用的搜索界面,这篇文章给大家介绍HTML5... html5 搜索框(Search Box)详解搜索框是一个用于输入查询内容的控件,通常用于网站或应用程

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五