vue3源码(六)渲染原理-runtime-core

1.依赖关系

  • runtime-dom 依赖于runtime-core,runtime-core 依赖于reactivityshared
  • runtime-core提供跨平台的渲染方法createRenderer,用户可以自己传递节点渲染的渲染方法renderOptions,本身不关心用户使用什么API
  • runtime-dom提供了为浏览器而生的渲染方法renderrender方法调用runtime-corecreateRenderer方法传递的renderOptions runtime-dom封装好的一系列关于渲染浏览器dom节点的操作
const renderOptions = Object.assign({ patchProp }, nodeOps);
export const render = (vnode,container)=>{
    return createRenderer(renderOptions).render(vnode,container)
}

2.init

2.1 package init

runtime-core/package.json

{
  "name": "@vue/runtime-core",
  "version": "1.0.0",
  "main": "index.js",
  "module": "dist/runtime-core.esm-bundler.js",
  "unpkg": "dist/runtime-core.global.js",
  "buildOptions": {
    "name": "RuntimeCore",
    "formats": [
      "esm-bundler",
      "esm-browser",
      "cjs",
      "global"
    ]
  },
  "dependencies": {
    "@vue/reactivity": "^3.4.30",
    "@vue/shared": "*"
  }
}

2.2 调整runtime-dom/index依赖


import { nodeOps } from "./nodeOps";
import patchProp from "./patchProp";
import {createRenderer} from '@vue/runtime-core'

const renderOptions = Object.assign({ patchProp }, nodeOps);
export { renderOptions };

// 如果我们采用的是runtime-dom中的render方法,我们不需要传递renderOptions,因为会把runtime-dom 这一层的dom处理方法传递进去,主要为浏览器而生的
// 如果我们用的是runtime-core 中的createRenderer,需要用户自己传递renderOptions   并不关心采用什么api

// runtime-dom 是内置的dom api 会去调用createRenderer,传入渲染选项,返回的渲染器有一个render方法
// 采用dom api 进行渲染
export const render = (vnode,container)=>{
    return createRenderer(renderOptions).render(vnode,container)
}

export  * from "@vue/runtime-core"

3.实现

3.1 init

createRenderer接受一个参数dom渲染相关配置,提供一个render方法,参数为虚拟节点和真实的dom元素

export function createRenderer(renderOptions) {
  const {
    insert: hostInsert,
    remove: hostRemove,
    patchProp: hostPatchProp,
    createElement: hostCreateElement,
    createText: hostCreateText,
    setText: hostSetText,
    setElementText: hostSetElementText,
    parentNode: hostParentNode,
    nextSibling: hostNextSibling,
  } = renderOptions;

  const render = (vnode, container) => {
    // 将虚拟节点变成真实节点进行渲染 
  };
  return {
    render,
  };
}

3.2 render实现

const mountElement = (vnode, container) => {
    console.log(vnode);
    const { type, children, props } = vnode;

    let el = hostCreateElement(type);
    if (props) {
      for (let key in props) {
        hostPatchProp(el, key, null, props[key]);
      }
    }
    hostSetElementText(el, children);
    hostInsert(el, container);
  };

  const patch = (n1, n2, container) => {
    if (n1 == n2) {
      return;
    }
    if (n1 == null) {
      mountElement(n2, container);
    }
  };
  // core 中不关心如何渲染
  const render = (vnode, container) => {
    // 将虚拟节点变成真实节点进行渲染
    patch(container._vnode || null, vnode, container);
    container._vnode = vnode;
  };

vnode如图:
在这里插入图片描述

const ele1 = h(
        "h1",
        { style: { color: "red" }},
        "hello world"
      );
      const ele2 = h(
        "h1",
        { style: { color: "green" } },
        "hello world"
      );
      render(ele1, document.getElementById("app"));
      setTimeout(()=>{
        render(ele2, document.getElementById("app"));
      },3000)

此时可以实现基础渲染,由于我们知道节点children是文本,可以直接使用文本进行渲染,那如果dom里面又嵌套一个dom呢?

3.3 shapeFlag

为了能够判断子节点的类型,定义一个枚举

export const enum ShapeFlags { // vue3提供的形状标识
    ELEMENT = 1,
    FUNCTIONAL_COMPONENT = 1 << 1,
    STATEFUL_COMPONENT = 1 << 2,
    TEXT_CHILDREN = 1 << 3,
    ARRAY_CHILDREN = 1 << 4,
    SLOTS_CHILDREN = 1 << 5,
    TELEPORT = 1 << 6,
    SUSPENSE = 1 << 7,
    COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8,
    COMPONENT_KEPT_ALIVE = 1 << 9,
    COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
}

比如const ele1 = h("h1", { style: { color: "red" } }, "hello world");是节点和文本的组合,节点为1,文本为8,采用或运算,得出节点类型数据9,可以看到上图中节点的shapeFlag为9,采用&运算得出节点具体类型 8&9=1000&1001=1000>0 则证明包含这个类型

const mountChildren = (children, container) => {
    for(let i=0;i<children.length;i++) {
    // 数组可能为字符串而不是节点
        patch(null, children[i], container)
    }
  };
  
 const { type, children, props, shapeFlag } = vnode;
 if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
      hostSetElementText(el, children);
    } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
      mountChildren(children, el);
    }

此处判断了TEXT_CHILDREN是文本,ARRAY_CHILDREN是数组

const ele3 = h("h1", { style: { color: "red" } }, [
        h("p", "hello"),
        h("p", "world"),
      ]);

可以正确渲染

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值