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

1.依赖关系

  • runtime-dom 依赖于runtime-core,runtime-core 依赖于reactivity和shared
  • runtime-core提供跨平台的渲染方法createRenderer,用户可以自己传递节点渲染的渲染方法renderOptions,本身不关心用户使用什么API
  • runtime-dom提供了为浏览器而生的渲染方法render,render方法调用runtime-core的createRenderer方法传递的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"),
      ]);

可以正确渲染

相关推荐
LucianaiB9 小时前
【元宝搜索开发者征文大赛】AI时代,学得慢就不用学?用元宝搜索打造一个教程保质期,确保每一个教程符合你的现状
前端·人工智能·ai编程
excel9 小时前
JavaScript 对象池:减少高频对象创建与 GC 压力
前端
陈随易11 小时前
纯 Go 写的 JS 引擎,跑分超过了 C 语言的 QuickJS
前端·后端·程序员
子兮曰12 小时前
Chrome 权限元素实战:144 与 151 的渐进增强写法
前端·javascript·chrome
IT_陈寒12 小时前
Redis连接池泄漏那次,我差点把生产环境搞崩了
前端·人工智能·后端
强尼银手13 小时前
我花一天做了个格斗游戏原型,判定全对,然后被「美术」劝退了
前端·javascript
石小石Orz13 小时前
AI 正在蚕食我们的编码能力
前端·后端·算法
陈随易13 小时前
专注飞机 FocusFlight,两个人把番茄钟做成了一趟航班
前端·后端·程序员
总要学点什么14 小时前
第98篇:三维特效-材质线效果——给一条线换十种材质,虚线的图案只填了一半
开发语言·前端·javascript·ecmascript·gis·webgl·材质
10share14 小时前
基于大模型的 i18n 方案:把中文原文当 Key,轻松搞定多语言维护
前端·vue.js