Vue3 setup render

2,473 阅读1分钟

文中的reactive和effectWatch出自上一篇文章juejin.cn/post/704773…

入口

import App from './core/init.js';
import { createApp } from './core/createApp.js';

// 根据vue的初始化顺序实现createApp和mount函数
createApp(App).mount(document.querySelector('#app'));

App

import { reactive } from './reactive/index.js';
import { h } from './parse/vNode.js';

export default {
    // template --> render 此过程省略,直接模拟一个h函数
    render(context) {
        return h('div', 
            {
                class: "demo"
            },
            `${context.state.count}`
            // [
            //     h('p', null, '哈哈哈'),
            //     h('p', null, '嘿嘿嘿'),
            // ]
        );
    },
    setup() {
        // 响应式数据
        const state = reactive({
            count: 1
        });
        window.state = state;
        return {
            state
        }
    }
}

createApp

import { effectWatch } from './reactive/index.js';
import { mountElement } from './renderer/index.js';

export function createApp(rootComponent) {
    // rootComponent App对象,有render和setup函数
    return {
        // 根节点容器,比如默认vue项目的index.html中的id='app'的div
        mount(rootContainer) {
            // 获取setup返回的对象
            const context = rootComponent.setup();
            // 做依赖收集
            effectWatch(() => {
                // 清空页面
                rootContainer.innerHTML = ``;
                const subTree = rootComponent.render(context);
                // 将vNode转为真实dom
                mountElement(subTree, rootContainer);
            })
        }
    }
}

创建虚拟DOM

// 创建虚拟dom
export function h(tag, props, children) {
    return {
        tag,
        props,
        children
    }
}

render

export function mountElement(vNode, container) {
    const { tag, props, children } = vNode;
    // tag
    const el = document.createElement(tag);
    // props
    if (props) {
        Object.entries(props).forEach(([key, value]) => {
            el.setAttribute(key, value);
        });
    }
    // children <Array|String>
    if (typeof children === 'string') {
        const node = document.createTextNode(children);
        el.append(node);
    }
    else if (Array.isArray(children)) {
        // 递归渲染
        children.forEach(vNode => {
            // 切记传入el作为下一次的渲染容器
            mountElement(vNode, el);
        });
    }
    // 插入
    container.append(el);
}