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节点的操作
js 复制代码
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

js 复制代码
{
  "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依赖

js 复制代码
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元素

js 复制代码
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实现

js 复制代码
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如图:

js 复制代码
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

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

js 复制代码
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 则证明包含这个类型

js 复制代码
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是数组

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

可以正确渲染

相关推荐
茶茶只知道学习8 分钟前
通过鼠标移动来调整两个盒子的宽度(响应式)
前端·javascript·css
蒟蒻的贤11 分钟前
Web APIs 第二天
开发语言·前端·javascript
清灵xmf14 分钟前
揭开 Vue 3 中大量使用 ref 的隐藏危机
前端·javascript·vue.js·ref
su1ka11120 分钟前
re题(35)BUUCTF-[FlareOn4]IgniteMe
前端
测试界柠檬21 分钟前
面试真题 | web自动化关闭浏览器,quit()和close()的区别
前端·自动化测试·软件测试·功能测试·程序人生·面试·自动化
多多*22 分钟前
OJ在线评测系统 登录页面开发 前端后端联调实现全栈开发
linux·服务器·前端·ubuntu·docker·前端框架
2301_8010741523 分钟前
TypeScript异常处理
前端·javascript·typescript
ᅠᅠᅠ@23 分钟前
异常枚举;
开发语言·javascript·ecmascript
小阿飞_24 分钟前
报错合计-1
前端
caperxi25 分钟前
前端开发中的防抖与节流
前端·javascript·html