vue3源码(七)渲染原理-h()

1.定义

h函数是创建节点, 可实现展示template如何渲染到html中的过程,因为vue渲染到页面上是通过loader打包成模板字符串拼接渲染的,所以 h 函数同样也是通过字符串渲染到html

h函数就是vue中的createElement方法,这个函数作用就是创建虚拟dom,追踪dom变化的

2.使用

/**
       * h()参数数量
       * 1个:类型
       * 2个:类型 和 属性、节点、儿子
       * 多个:第三个及其以后都是子节点
       * 不能出现三个参数,2不是属性 只要有三个参数就会认为第二个参数是属性
       */
      const ele = h("h1");
      const ele1 = h("h1", "hello world");
      const ele2 = h("h1", { style: { color: "red" } });
      const ele3 = h("h1", ["hello world","goodbye"]);
      const ele4 = h("h1", {},"hello world","goodbye");

如果有三个及其以上的参数,第二个参数代表属性,如果没有属性,可以写成ele3或者ele4的形式

3.实现

2.1 创建虚拟节点

import { isString, ShapeFlags } from "@vue/shared";

export function isVnode(value) {
  return value && value?.__v_isVnode;
}
export function createVnode(type, props, children) {
  const shapeFlag = isString(type) ? ShapeFlags.ELEMENT : 0;
  const vnode = {
    __v_isVnode: true,
    type,
    props,
    children,
    key: props?.key, // diff需要的key
    el: null, // 虚拟节点对应的真实节点
    shapeFlag,
  };
  if(children){
    let type = 0;
    if(Array.isArray(children)){
        type = ShapeFlags.ARRAY_CHILDREN;
    }else{
        children = String(children);
        type = ShapeFlags.TEXT_CHILDREN
    }
    vnode.shapeFlag |= type
}
return vnode;
}

2.2 h实现

import { isObject } from "@vue/shared";
import { createVnode, isVnode } from "./createVnode";

export function h(type, propsOrChildren?, children?) {
  let l = arguments.length;
  if (l === 2) {
    // 虚拟节点 | 属性
    if (isObject(propsOrChildren) && !Array.isArray(propsOrChildren)) {
      if (isVnode(propsOrChildren)) {
        return createVnode(type, null, [propsOrChildren]);
      } else {
        return createVnode(type, propsOrChildren, null);
      }
    }
    // 数组 | 文本
    return createVnode(type, null, propsOrChildren);
  } else {
    if (l > 3) {
      children = Array.from(arguments).slice(2);
    }
    if (l == 3 && isVnode(children)) {
      children = [children];
    }
    return createVnode(type, propsOrChildren, children);
  }
}

2.3 卸载DOM

const ele3 = h("h1", 'hello');
      render(ele3, app);
      setTimeout(()=>{
        render(null, app);
      },1000)

在挂载元素时纪录真实节点

const mountElement = (vnode, container) => {
    const { type, children, props, shapeFlag } = vnode;
    // 第一次渲染的时候我们让虚拟节点和真实的dom创建关联
    // 第二次渲染新的vnode,可以和上一次的vnode做比对,之后更新对应的el元素,可以复用这个dom元素

    let el = (vnode.el = hostCreateElement(type));
    ...
    }

在渲染时判断

 const unmount = (vnode) => hostRemove(vnode.el)
  // core 中不关心如何渲染
  const render = (vnode, container) => {
    if (vnode == null) {
      // 移除dom元素
      if (container._vnode) {
        unmount(container._vnode)
      }
    }
    ...
  }

2.4 对比节点

通过判断两个节点的类型和key值是否相同判定为是否为同一节点,如果不是,则删除n1,创建n2,如果是同一节点,则采用diff算法对比更新复用节点

export function isSameVnode(n1,n2){
  return n1.type === n2.type && n1.key === n2.key
}

更改patch逻辑

const patch = (n1, n2, container) => {
    if (n1 == n2) {
      return;
    }
    if (n1 && !isSameVnode(n1, n2)) {
      unmount(n1);
      n1 = null; //为了执行后续的n2的初始化
    }
    if (n1 == null) {
      mountElement(n2, container);
    }else{
      // diff 算法
    }
  };

diff算法见下篇啦~~~

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值