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

可以正确渲染

相关推荐
前端开发爱好者38 分钟前
Vue3 超强“积木”组件!5 分钟搞定可交互 3D 机房蓝图!
前端·javascript·vue.js
前端开发爱好者42 分钟前
尤雨溪力荐!Vue3 专属!100+ 动效组件!
前端·javascript·vue.js
前端开发爱好者44 分钟前
尤雨溪力荐!Vue3 生态最强大的 14 个 UI 组件库!
前端·javascript·vue.js
lb29171 小时前
关于多个el-input的自动聚焦,每输入完一个el-input,自动聚焦到下一个
前端·javascript·vue.js
lingliang1 小时前
使用 JS 渲染页面并导出为PDF 常见问题与修复
javascript·pdf·vue
sorryhc1 小时前
【AI解读源码系列】ant design mobile——Divider分割线
前端·javascript·react.js
前端进阶者2 小时前
electron-vite_20配置依赖包运行时区外部加载commonjsExternals_vite-plugin-commonjs-externals
前端·electron
anyup2 小时前
🔥 uView Pro 全新升级来啦!一行配置,解锁 uView Pro 全局 TS 类型提示与校验
前端·vue.js·uni-app
Jimmy2 小时前
使用 Electron 在 5 分钟内创建一个桌面的 React 应用
前端·javascript·electron
玲小珑2 小时前
LangChain.js 完全开发手册(二)Prompt Engineering 与模板系统深度实践
前端·langchain·ai编程