初试Vue3-手动实现render挂载

初试Vue3-手动实现render挂载
只实现了最简单的render新增节点挂载到指定dom上,数据响应式没实现,一点一点的研究吧

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="app"></div>
    <script>
        // const {createApp} = Vue;
        const createAppAPI = (render) => {
            return function createApp(rootComponent) {
                // 返回真正的应用程序实例,执行mount方法
                const app = {

                    // 将传入的容器选择器 把vnode转变成dom 并挂载到容器
                    mount(rootContainer) {
                        // rootContainer 就是#app
                        // rootComponent 是传进来的 {data(){return{}},render(){tag:"h2",children:this.foo}}
                        // v3里面虚拟dom的变化
                        const vnode = {
                            tag: rootComponent
                        }

                        // 这一步非常重要哦,执行渲染
                        render(vnode, rootContainer)
                    }
                }


                return app
            }
        }

        // 2.实现renderer工厂函数
        const createRenderer = options => {
            const patch = (n1, n2, container) => {
                /**
                 * n1 旧的虚拟节点
                 * n2 新的虚拟节点 根组件的配置在这里
                 * container 容器挂载
                 * */

                const rootComponent = n2.tag; //为啥是n2.tag 可以往上找,createAppAPI里面定义的,当然也可以不叫tag

                // 数据的上下文,要指向data的执行结果
                const ctx = {...rootComponent.data()
                    }
                    /*
                     *ctx 的值是{foot:'hellov3'}
                     **/

                // 执行render获取vnode
                const vnode = rootComponent.render.call(ctx);
                /**
                    执行实例里面render方法,拿到data里面的数据
                **/

                // 转换vnode dom 拿到要挂载的节点dom
                const parent = options.querySelector(container);

                // render里要添加的tagname,创建新节点
                const child = options.createElement(vnode.tag);

                //这里只判断了最简单的情况,其他类型还得再去源码中好好看
                if (typeof vnode.children === 'string') {
                    child.textContent = vnode.children;
                    // textContent 文本内容
                }
                // 最后一步将虚拟dom转完的真实dom挂载到指定元素上
                options.insert(child, parent)
            }
            const render = (vnode, container) => {
                // 把虚拟dom变成真实dom并添加到container里

                // 判断container上有没有虚拟dom
                patch(container._vode || null, vnode, container);

                container._vode = vnode;
            }

            // 该对象就是renderer
            return {
                render,
                createApp: createAppAPI(render)
            }
        }


        // 1.createApp  runtimedom->index.js createApp ensureRender  createRenderer
        const Vue = {
            createApp(options) {
                // 执行的实际是renderer.createApp()
                return renderer.createApp(options)
            }
        }
        const renderer = createRenderer({
            querySelector(sel) {
                return document.querySelector(sel)
            },
            createElement(tag) {
                return document.createElement(tag)
            },
            insert(child, parent) {
                parent.appendChild(child)
            }
        })
        const {
            createApp
        } = Vue;
        createApp({
            data() {
                return {
                    foo: "hello,vue3!"
                }
            },
            render() {
                return {
                    tag: "h2",
                    children: this.foo
                }
            }
        }).mount('#app');
    </script>
</body>

</html>
posted @ 2020-10-22 15:52  ✔️zhangfl_go  阅读(1166)  评论(0编辑  收藏  举报