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"),
      ]);

可以正确渲染

相关推荐
To_OC5 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
To_OC5 小时前
LC 17 电话号码的字母组合:我的回溯算法,就是从这道题开窍的
javascript·算法·leetcode
vipbic6 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy6 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志6 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble7 小时前
c端系统,其实更像一个信息展示平台
前端
你怎么知道我是队长8 小时前
JavaScript的变量和数据类型介绍
开发语言·javascript·ecmascript
李姆斯8 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜9 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司9 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能