vue开发runtime core中的虚拟节点示例详解

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

vue开发runtime core中的虚拟节点示例详解

ClyingDeng   2022-11-30 我要评论

引言

我们知道runtime-dom内部功能其实是 将渲染时所需要节点操作的 API (即rendererOptions) 传入到runtime-core中。

之前文章我们可以大概知道 runtime-dom 中节点操作属性API有哪些,并试着实现部分API功能。runtime-core 是实现与平台无关的运行时功能(即runtime-core中的节点渲染)。

本文讲述的内容是:实现 runtime-core 中的createAppAPI,完成虚拟节点的创建,以及render中的挂载所需参数的获取。

将API传入到runtime-core中

我们再来看下上篇文章的例子:

<div id="app"></div>
<script src="./runtime-dom.global.js"></script>
<script>
        let { createApp, h, ref } = VueRuntimeDOM
        function useCounter() {
            const count = ref(0)
            const add = () => {
                count.value++
            }
            return { count, add }
        }
        let App = {
            props: {
                title: {}
            },
            setup() {
                let { count, add } = useCounter()
                return { count, add }
            },
            // 每次更新重新调用render方法
            render(proxy) {
                return h('h2', { onClick: this.add, title: proxy.title }, 'hello dy' + this.count)
            }
        }
        let app = createApp(App, { title: 'dy' }) // 组件 组件参数
        app.mount('#app')
</script>

用户通过解析runtime-dom中的 createApp,在createApp中接收组件和组件传入的属性参数,对根节点进行初始化渲染。

那这样的话,我们在runtime-dom需要对外暴露一个createApp方法,内部实现根据相应参数实现真实节点的初始化工作。

/**
 * 
 * @param component 组件
 * @param rootProps 传入的属性
 */
export const createApp = (component, rootProps = null) => { }
// 将runtime-core中所有API导出
export * from '@vue/runtime-core'

createRenderer 初始化

此时,我们就需要创建一个渲染器createRenderer。在渲染器中实现一个mount挂载的方法。看下面的结构:

export function createRenderer(rendererOptions) {
    // 虚拟节点转化成真实节点 渲染到容器中
    const render = (vnode, container) => {
    }
    // 创建节点相关的api mount use mixin
    const createAppAPI = (render) => {
        return (rootComponent, rootProps) => {
            let app = {
                mount(container) { },
                use() { },
                mixin() { },
                component() { }
            }
            return app
        }
    }
    return {
        render,
        createApp: createAppAPI(render)
    }
}

createAppAPI中是组件相关的API,比如mount、use、mixin等,在createAppAPI内部我们肯定还需要元素挂载实现的render方法。

我们可以看到官网中的createRenderer API,中对外暴露了 render 和 createApp两种方法,其实runtime-core中内部也是这样实现的。

当然我们只是先将空架子搭建起来,后面还需要一步步填充功能。

createApp 内部实现

把上面例子来出来:

let app = createApp(App, { title: 'dy' }) // 组件 组件参数
app.mount('#app')

继续回到createApp这个方法:

createRenderer实现一个渲染器,我们通过 createRenderer 可以解构其中对外暴露的方法,在 createApp 内部进行初始化,最后将其返回,用户再通过mount对元素进行挂载。

const { createApp } = createRenderer(rendererOptions)
let app = createApp(component, rootProps) // 渲染器完成 将其返回

在初始化的时候我们通过用户传入的#app,进行初始化。在执行用户mount前,我们在createApp中存在实际元素挂载功能的mount,我们就需要先将其保存下来,在执行用户mount时,在其内部进行实际的元素挂载(即app.mount内部执行我们实际mount功能)。

createApp内部:

let { mount } = app // 自己的
app.mount = (containerOrSelector) => { // 用户传入的 #app
        const container = nodeOps.querySelector(containerOrSelector)
        if (!container) return 
        container.innerHTML = ''
        mount(container)
    }

在挂载的时候,我们需要清空当前节点的内部元素,再将其挂载。

完整的createApp:

export const createApp = (component, rootProps = null) => {
    const { createApp } = createRenderer(rendererOptions) // 将core中的createApp解构出来
    // 创建一个渲染器 把功能传递给runtime-core
    let app = createApp(component, rootProps)
    let { mount } = app // 调用的是core中的mount
    // app.mount 用户初始化的mount ==> app.mount("#app");
    // 在用户的app.mount中再去执行core中的mount ==> core中就会存在domAPI  需要渲染的组件 目标属性,最后再去挂载到容器中
    /**
     * containerOrSelector 用户传入的容器
     */
    app.mount = (containerOrSelector) => { // 用户传入的 #app
        const container = nodeOps.querySelector(containerOrSelector)
        if (!container) return // 不为空直接返回
        // 在挂载之前清空
        container.innerHTML = ''
        // 调用的是core中的mount  处理节点后将container传入mount
        mount(container)
    }
    return app
}

这样我们 createRenderer 就可以拿到节点操作的DOM API(rendererOptions)、需要渲染的组件(component)、目标属性(rootProps)和需要挂载的位置-容器(container)。createRenderer也就可以独立出来,放到runtime-core中,在core中我们单独去实现元素的渲染。

runtime-core

视角转到我们core中,在core中我们需要实现一个渲染器。目录结构如下:

我们主要实现createRenderer这个功能对外暴露的两个API,可以先来看看createApp中的 createAppAPI 。

createAppAPI

在 createAppAPI 中,我们目前需要实现真实元素的挂载。

说的通俗一点:挂载的核心就是根据传入的组件对象,创造一个组件的虚拟节点,再将这个虚拟节点渲染到容器中。

具体分两步走:

mount(container) {
    // 1:创建组件虚拟节点
    const vnode = cerateVNode(rootComponent, rootProps)// h函数
    // 2:虚拟节点渲染到容器中
    render(vnode, container)
}

cerateVNode 创建组件虚拟节点

我们首先要知道,虚拟节点是一个数据对象,用一个数据结构来对元素进行描述,当前元素是组件还是元素、它的字节点、携带的参数等有哪些。

类型表示

我们需要判断传过来的type是组件还是元素,改如何表示呢?

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们