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

打印 上一主题 下一主题

主题 670|帖子 670|积分 2010

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节点的操纵
  1. const renderOptions = Object.assign({ patchProp }, nodeOps);
  2. export const render = (vnode,container)=>{
  3.     return createRenderer(renderOptions).render(vnode,container)
  4. }
复制代码
2.init

2.1 package init

runtime-core/package.json
  1. {
  2.   "name": "@vue/runtime-core",
  3.   "version": "1.0.0",
  4.   "main": "index.js",
  5.   "module": "dist/runtime-core.esm-bundler.js",
  6.   "unpkg": "dist/runtime-core.global.js",
  7.   "buildOptions": {
  8.     "name": "RuntimeCore",
  9.     "formats": [
  10.       "esm-bundler",
  11.       "esm-browser",
  12.       "cjs",
  13.       "global"
  14.     ]
  15.   },
  16.   "dependencies": {
  17.     "@vue/reactivity": "^3.4.30",
  18.     "@vue/shared": "*"
  19.   }
  20. }
复制代码
2.2 调整runtime-dom/index依赖

  1. import { nodeOps } from "./nodeOps";
  2. import patchProp from "./patchProp";
  3. import {createRenderer} from '@vue/runtime-core'
  4. const renderOptions = Object.assign({ patchProp }, nodeOps);
  5. export { renderOptions };
  6. // 如果我们采用的是runtime-dom中的render方法,我们不需要传递renderOptions,因为会把runtime-dom 这一层的dom处理方法传递进去,主要为浏览器而生的
  7. // 如果我们用的是runtime-core 中的createRenderer,需要用户自己传递renderOptions   并不关心采用什么api
  8. // runtime-dom 是内置的dom api 会去调用createRenderer,传入渲染选项,返回的渲染器有一个render方法
  9. // 采用dom api 进行渲染
  10. export const render = (vnode,container)=>{
  11.     return createRenderer(renderOptions).render(vnode,container)
  12. }
  13. export  * from "@vue/runtime-core"
复制代码
3.实现

3.1 init

createRenderer接受一个参数dom渲染相关配置,提供一个render方法,参数为虚拟节点和真实的dom元素
  1. export function createRenderer(renderOptions) {
  2.   const {
  3.     insert: hostInsert,
  4.     remove: hostRemove,
  5.     patchProp: hostPatchProp,
  6.     createElement: hostCreateElement,
  7.     createText: hostCreateText,
  8.     setText: hostSetText,
  9.     setElementText: hostSetElementText,
  10.     parentNode: hostParentNode,
  11.     nextSibling: hostNextSibling,
  12.   } = renderOptions;
  13.   const render = (vnode, container) => {
  14.     // 将虚拟节点变成真实节点进行渲染
  15.   };
  16.   return {
  17.     render,
  18.   };
  19. }
复制代码
3.2 render实现

  1. const mountElement = (vnode, container) => {
  2.     console.log(vnode);
  3.     const { type, children, props } = vnode;
  4.     let el = hostCreateElement(type);
  5.     if (props) {
  6.       for (let key in props) {
  7.         hostPatchProp(el, key, null, props[key]);
  8.       }
  9.     }
  10.     hostSetElementText(el, children);
  11.     hostInsert(el, container);
  12.   };
  13.   const patch = (n1, n2, container) => {
  14.     if (n1 == n2) {
  15.       return;
  16.     }
  17.     if (n1 == null) {
  18.       mountElement(n2, container);
  19.     }
  20.   };
  21.   // core 中不关心如何渲染
  22.   const render = (vnode, container) => {
  23.     // 将虚拟节点变成真实节点进行渲染
  24.     patch(container._vnode || null, vnode, container);
  25.     container._vnode = vnode;
  26.   };
复制代码
vnode如图:

  1. const ele1 = h(
  2.         "h1",
  3.         { style: { color: "red" }},
  4.         "hello world"
  5.       );
  6.       const ele2 = h(
  7.         "h1",
  8.         { style: { color: "green" } },
  9.         "hello world"
  10.       );
  11.       render(ele1, document.getElementById("app"));
  12.       setTimeout(()=>{
  13.         render(ele2, document.getElementById("app"));
  14.       },3000)
复制代码
此时可以实现底子渲染,由于我们知道节点children是文本,可以直接使用文本进行渲染,那如果dom内里又嵌套一个dom呢?
3.3 shapeFlag

为了能够判断子节点的类型,界说一个枚举
  1. export const enum ShapeFlags { // vue3提供的形状标识
  2.     ELEMENT = 1,
  3.     FUNCTIONAL_COMPONENT = 1 << 1,
  4.     STATEFUL_COMPONENT = 1 << 2,
  5.     TEXT_CHILDREN = 1 << 3,
  6.     ARRAY_CHILDREN = 1 << 4,
  7.     SLOTS_CHILDREN = 1 << 5,
  8.     TELEPORT = 1 << 6,
  9.     SUSPENSE = 1 << 7,
  10.     COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8,
  11.     COMPONENT_KEPT_ALIVE = 1 << 9,
  12.     COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
  13. }
复制代码
比如const ele1 = h("h1", { style: { color: "red" } }, "hello world");是节点和文本的组合,节点为1,文本为8,接纳或运算,得出节点类型数据9,可以看到上图中节点的shapeFlag为9,接纳&运算得出节点具体类型 8&9=1000&1001=1000>0 则证明包含这个类型
  1. const mountChildren = (children, container) => {
  2.     for(let i=0;i<children.length;i++) {
  3.     // 数组可能为字符串而不是节点
  4.         patch(null, children[i], container)
  5.     }
  6.   };
  7.   
  8. const { type, children, props, shapeFlag } = vnode;
  9. if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
  10.       hostSetElementText(el, children);
  11.     } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
  12.       mountChildren(children, el);
  13.     }
复制代码
此处判断了TEXT_CHILDREN是文本,ARRAY_CHILDREN是数组
  1. const ele3 = h("h1", { style: { color: "red" } }, [
  2.         h("p", "hello"),
  3.         h("p", "world"),
  4.       ]);
复制代码
可以精确渲染

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

杀鸡焉用牛刀

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表