8.Vue3虚拟节点的实现

runtime-core包

vue3中节点的渲染操作主要在runtime-core包中,runtime-core不关心运行的平台。

createVNode函数

vue3中通过函数createVNode函数创建一个虚拟节点,其传入三个参数,分别是节点名,属性对象,孩子,如:

		 const {createVNode,render,h} =  VueRuntimeDOM
         console.log(createVNode('h1',{key:1},'Hello world'))

调用改函数,创建一个h1标签,输出内容如下:
在这里插入图片描述

可以看到,输出的对象就是一个虚拟节点,其对象包含内容如下:

  • children:节点包含的孩子内容,可以是一个字符串,也可能是一个虚拟节点数组
  • el:改虚拟节点后面渲染成真实节点后,会被el记录,即 vnode.el 就是其真实节点
  • key:虚拟节点的标识,也用作区分不同的节点
  • props:虚拟节点的属性
  • shapeFlags:节点的类型标识
  • type:节点名
  • __v_isVNode:判断是否是真实节点
h函数
  • h函数似乎拥有与createVnode函数类似的功能,如上述例子改成h函数
console.log(h('h1',{key:1},'Hello world'))

在这里插入图片描述

  • 可以看到其结果依然是一个虚拟节点,其实h函数就是在createVNode函数的基础上,做了一层封装,createVNode函数必须传入三个参数,而h函数则不用,如h('h1','Hello world'),内部会将其转换成createVNode('h1,null,'Hello world')

createVNode函数的实现

形状标识的创建
  • 虚拟节点中有了type来识别节点类型,为什么还要有shapeFlags类型呢,原因就是为了判断该节点是否有孩子及孩子类型是什么
  • 首先规定不同的孩子类型对应不同的标识号
export const enum ShapeFlags { // vue3提供的形状标识
    ELEMENT = 1, // 1
    FUNCTIONAL_COMPONENT = 1 << 1, // 2
    STATEFUL_COMPONENT = 1 << 2, // 4
    TEXT_CHILDREN = 1 << 3, // 8
    ARRAY_CHILDREN = 1 << 4, // 16
    SLOTS_CHILDREN = 1 << 5, // 32
    TELEPORT = 1 << 6, // 64 
    SUSPENSE = 1 << 7, // 128
    COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8,
    COMPONENT_KEPT_ALIVE = 1 << 9,
    COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
}
  • 而ShapeFlags就是记录这些标识,比如:
    • 一个创建一个vnode,其type为h1
    • 那么其类型属于ELEMENT,
    • 即ShapeFlags = 1
    • 若为其添加一个文本“hello world”,那么改文本节点属于TEXT_CHILDREN
    • 则 ShapeFlags变为 1 | 8 = 9,即shapeFlags变为9
    • 同样拿到一个节点如何判断其包含某个节点呢,如一个节点的 shapeFlags为17,
    • 那么其包含TEXT_CHILDREN吗? 只需 shapeFlags & ShapeFlags.TEXT_CHILDREN
    • 即上述结果变成17 & 8 ,结果为0,则不包含
    • 那么其包含ARRAY_CHILDREN吗,同样操作shapeFlags & ShapeFlags.ARRAY_CHILDREN
    • 即上述结果变为17 & 16 ,结果为1,说明包含
  • 可以看到,通过给不同节点类型不同的编码,然后利用按位与(&)与按位或(|)就可以识别改节点的子节点类型
  • 调用createVNode函数后,会默认创建一个vnode对象,该对象会根据传入的参数设定默认值
    • 首先判断type类型是否为字符串,若是则shapeFlags默认为1
    • 其次判断孩子类型,若是字符串,则 shapeFlags改为 shapeFlags | ShapeFlags.TEXT_CHILDREN
    • 若为数组,则改为 shapeFlags | ShapeFlags.ARRAY_CHILDREN
  • 最终返回节点
export function createVNode(type,props = null, children = null){

    // 后续判断有不同类型的虚拟节点
    let shapeFlag = isString(type) ? ShapeFlags.ELEMENT : 0 // 标记出来了自己是什么类型
    // 我要将当前的虚拟节点 和 自己儿子的虚拟节点映射起来  权限组合 位运算
    const vnode = { // vnode 要对应真实际的节点
        __v_isVNode:true,
        type,
        props,
        children,
        key: props && props.key,
        el:null,
        shapeFlag
        // 打个标记
    }
    if(children){
        let temp = 0;
        if(isArray(children)){ // 走到createVnode 要么是数组要么是字符串 h()中会对children做处理
            temp = ShapeFlags.ARRAY_CHILDREN
        }else{
            children = String(children);
            temp = ShapeFlags.TEXT_CHILDREN
        }
        vnode.shapeFlag  =  vnode.shapeFlag | temp;
    }
    // shapeFlags  我想知到这个虚拟节点的儿子是数组 还是元素 还是文本 
    return vnode;
}

h函数的实现

  • h函数是对createVNode函数的封装,默认接收三个参数,也可以接收两个参数 ,接收两个参数的情况有两种
    • 元素+属性
    • 元素+孩子
  • 首先判断函数传入的参数长度,如果为三个参数
    • 判断第三个参数的类型,如果是单个孩子节点,则封装成数组(孩子只划分为两种:字符串或者孩子节点数组)
    • 若是两个参数
      • 判断第二个参数是否为数组
      • 若是,则说明是孩子数组,完善createVNode的参数即可
      • 若不是,则需要判断这个参数是孩子节点还是属性对象
export function h(type,propsOrChildren,children){
    // h方法 如果参数为两个的情况  1) 元素 + 属性  2) 元素 + 儿子
    const l = arguments.length;
    if(l === 2){
        // 如果propsOrChildren是对象的话 可能是属性 可能是儿子节点 
        if(isObject(propsOrChildren) && !isArray(propsOrChildren)){ // h(type,属性或者元素对象)
            // 要么是元素对象 要么是属性
            if(isVnode(propsOrChildren)){ // h(type,元素对象) 
                return createVNode(type,null,[propsOrChildren])
            }
            return createVNode(type,propsOrChildren) // h(type,属性)
        }else{
            // 属性 + 儿子的情况   儿子是数组 或者 字符
            return createVNode(type,null,propsOrChildren) // h(type,[] )  h(type,'文本‘)
        }
    }else{
        if(l === 3 && isVnode(children)){ // h(type,属性,儿子)
            children = [children]
        }else if(l > 3){
            children = Array.from(arguments).slice(2)// h(type,属性,儿子数组)
        }
        return createVNode(type,propsOrChildren,children)
        // l > 3
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

问也去

创作不易,感谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值