• vue3源码分析——实现createRenderer,增加runtime-test


    引言

    <<往期回顾>>

    1. vue3源码分析——rollup打包monorepo
    2. vue3源码分析——实现组件的挂载流程
    3. vue3源码分析——实现props,emit,事件处理等
    4. vue3源码分析——实现slots
    5. vue3源码分析——实现组件通信provide,inject

    本期来实现, vue3的自定义渲染器,增加runtime-test子包,所有的源码请查看

    正文

    createRenderer的作用是: 实现vue3的runtime-core的核心,不只是仅仅的渲染到dom上,还可以渲染到canvas,webview等指定的平台

    请思考🤔🤔🤔,createRenderer是怎么做到的呢?

    设计createRenderer函数

    createRenderer顾名思义就是创造一个render(可以直接导出一个render函数),现在咱们的是直接在render.ts中对外导render函数出提供给createApp中使用

    image.png

    对于createApp而言,需要render函数,那么咱们可以通过函数的参数穿进来,那就变成这个样子的形式

    image.png

    编码

    // 通过上面的分析,先把createApp给改造一下,需要一个新的函数来包裹,并且传入render函数
    
    export function createAppApi(render){
     return function createApp(rootComponent){
      // ……原有的逻辑不变
     }
    }
    
    // 在createAppApi里面需要render,那就在createRenderer里面调用并且给他,
    // 返回一个新的createApp
    export function createRenderer(){
         function render(vnode, container) {
            // 调用patch
            patch(vnode, container, null)
          }
     // ……省略其他所有的函数
     
     return {
      // 这样设计是不是对外导出了一个新的createAPP哇
      createApp: createAppApi(render)
     }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    渲染平台

    既然是自定义渲染平台,那肯定是需要修改元素的挂载逻辑,并且把需要挂载的平台给传入进来

    分析

    目前代码里面默认是渲染到dom,在mountElement里面使用了document.createElement, dom.setAttribute, dom.innerHtml等逻辑都是用来处理dom操作,其他的平台挂载元素的方式是不一样的,那么怎么解决这个问题呢?

    需要解决这个问题,也是非常简单的,既然咱们不知道是挂载到哪里,那直接通过createRenderer里面传入进来就ok啦😄😄😄 目前用到的主要是四个地方涉及到dom操作,把这四个地方统统封装成函数,然后通过createRenderer里面作为options里面传入即可

    编码

    在createRenderer里面加入参数options,并且结构出四个函数
    
    export function createRenderer(options) {
      const {
       // 创建元素
        createElement,
        // 绑定key
        patchProps,
        // 插入操作
        insert,
        // 设置文本
        setElementText
      } = options
      
      
        function mountElement(vnode: any, container: any, parentComponent) {
            const el = createElement(vnode.type)
        // 设置vnode的el
        vnode.el = el
        // 设置属性
        const { props } = vnode
    
        for (let key in props) {
          patchProps(el, key, props[key])
        }
        // 处理子元素
        const children = vnode.children
        if (vnode.shapeflag & ShapeFlags.ARRAY_CHILDREN) {
          // 数组
          mountChildren(children, el, parentComponent)
        } else if (vnode.shapeflag & ShapeFlags.TEXT_CHILDREN) {
          // 自定义插入文本
          setElementText(el, String(children))
        }
        // 挂载元素
        insert(el, container)
        }
      }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    这么改造,目前createRenderer的功能实现了,但是会发现所有用的createApp的测试用例都不行了,由于咱们没目前没有对外导出createApp

    runtime-test

    从目前来说,本块的内容可以说是 runtime-dom,因为runtime-test对外提供的确实是dom环境的测试,方便用于runtime-core的测试

    新建子包的过程不在这里描述哈,有兴趣的可以查看

    runtime-test需要的依赖是:

     "dependencies": {
        "shared":"workspace:shared@*",
        "runtime-core":"workspace:runtime-core@*"
      }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    分析

    runtime-test的作用是对外提供一个createApp函数,那就需要调用createRender来创建一个customRender,customRender里面有createApp函数。 调用createRender又需要传入一个options,options是我们当前对应平台的4个函数,分别是:

    • createElement: 创建dom
    • patchProps: 处理属性
    • insert: 将某个元素插入到哪里
    • setElementText: 设置文本

    编码

    function createElement(type) {
      return document.createElement(type);
    
    }
    
    function patchProps(el, key, value) {
    
      if (isOn(key)) {
        // 注册事件
        el.addEventListener(key.slice(2).toLowerCase(), value)
      }
      el.setAttribute(key, value)
    }
    
    
    function insert(el, container) {
      container.append(el)
    }
    
    function setElementText(el, text) {
      el.textContent = text;
    }
    
    const render: any = createRenderer({
      createElement,
      patchProps,
      insert,
      setElementText
    });
    
    // 对外导出createApp
    export function createApp(...args) {
      return render.createApp(...args);
    }
    // 需要使用runtime-core里面的所有内容,因为里面有的变量是在闭包中进行使用的
    export * from 'runtime-core'
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    思考🤔🤗🤔: 处理完runtime-test就需要在runtime-core中进行引用,直接在runtime-core中引用么?

    那肯定是不行的,runtime-test里面引用runtime-core,如果runtime-core在引用runtime-test的话,那就是循环引用了,𝒮ℴ, 𝒽ℴ𝓌 𝓉ℴ 𝓇ℯ𝓈ℴ𝓁𝓋ℯ 𝒾𝓉 ?

    解决方式: **在上一级的package.json上加入runtime-test这个包,那么在runtime-core中就能引用啦!**😝😝😝

    测试效果

    image.png

    总结

    本期主要实现了createRenderer函数改造createApp等函数,通过这些函数,可以看到vu3在设计方面的用心良苦,尽量让vue3满足更多的人。增加了runtime-test,方便用于测试dom环境下面的情况!

  • 相关阅读:
    Cell 重磅丨不依赖泛素蛋白酶降解途径的新型 PROTAC - MCE
    设计模式(十六)----结构型模式之代理享元模式
    [附源码]Python计算机毕业设计Django健身房预约平台
    机器学习入门教学——人工智能、机器学习、深度学习
    C++算法之旅、06 基础篇 | 第四章 动态规划 详解
    VA01/VA02/VA03 销售订单根据定价和步骤校验权限隐藏价格(二)
    阿里云服务器添加安全组和防火墙规则
    某猫投诉app逆向 【一鱼多吃app逆向】
    ahx文件转mav文件 工具分享及说明
    如何用Python将普通视频变成动漫视频
  • 原文地址:https://blog.csdn.net/qq_41499782/article/details/125429769