Vue3源码学习-心得

199 阅读2分钟

一、对比

学习了vue2的源码和vue3的源码知识,收获很多,如果从对比的角度去分析理解,加深记忆的效果会更好。 下面是个人梳理的 vue2 和vue3的相同点和不同点。

一、相同点:

1、都是对虚拟dom的操作,最终一次挂载到真实的dom节点。

1.1、父子vnode的创建挂载遵循洋葱圈模式,父级先创建,子级创建子级挂载,父级后挂载。

1.2、vnode更新对比是同层比较,深度优先。

2、响应式设计模式都是遵循发布订阅模式(观察者模式)。编译时候:注册(订阅或跟踪)变量映射关系,同时劫持变量属性的更新,等待属性更新,发布(notify 或 effect)执行对应的dom节点更新函数path。

3、都是有template 到编译成ast ,在此过程中建立映射更新关系。

4、都是diff算法:先首尾比较找相同,再中间依次对比。

二、不同点:

vue3为了在性能,友好性,容易性,上提升,改动了很多,我们可以对比理解~

1、vue3更快!

1.1、虚拟DOM重写:生成的vnode更细,更精确,节点的信息更详细了。可能在编译阶段报错提醒更多,但是在执行代码的时候,处理节点速度更精确,更快!

1.2、slots优化生成:可以单独重新渲染父级和子级。

1.3、静态树提升,静态属性提升:能不执行重新渲染patch,(diff对比)就不执行。内存换时间,提升效率

1.4、基于Proxy的响应式系统: 比vue2的 defineProperty() 好处:外层加壳操作,不用遍历每一个属性;数组的操作也能触发响应,不用单独写;不好处:兼容性问题,IE不能用Proxy的还得用defineProperty方式处理。

2、vue3更小!vue3 进行了treeshaking优化,核心库体积更小。

3、vue3TS+模块化:编写代码时候,提示更有效,编写代码更规范,维护性强。

4、更友好,易扩展!

4.1、vue2:把配置或者vue语法变成vnode,vnode编译成真实的dom树时,编译是使用了js的语法,vnode转真实dom的核心编译器没有提取出来。

4.1、vue3:支持通过(web/android/ios)的语法编写核心编译器,可以让vnode生成对应平台的dom执行使用。

4.2、Composition Api:类似react的 Hooks,用于逻辑的复用

4.3、独立的响应化模块:响应式编码不用和vue集成依赖编写,可以打散拆分重用的部分更多了!

二、vue3 init过程

<div id="app" title="lxq-1019-vue3"></div>
<script>
    // 3.createAppAPI 此方法接收一个render
    const createAppAPI = (render) => {
        // 返回一个真正的应用实例
        return function createApp(rootComponent) {
            const app = {
                mount(rootContainer) {
                    // 挂载 vnodo=>dom
                    const vnode = {
                        tag: rootComponent
                    }
                    // 根渲染
                    render(vnode, rootContainer);
                }
            }
            return app;
        }
    }
    // 2.renderer 工厂函数
    const createRenderer = options => {
        // 源码里面有一个baseCreateRenderer 2000+  核心
        const patch = (n1, n2, container) => {
            // 根组件配置
            const rootComponent = n2.tag;
            const ctx = { ...rootComponent.data() };
            // 转换vnode 到dom
            const parent = options.querySelector(container); // 根
            // 页面render获取vnode
            const vnode = rootComponent.render.call(ctx); // vnode
            const child = options.createElement(vnode.tag);
            if (typeof vnode.children === 'string') {
                child.textContent = vnode.children;
            } else {
                // array
            }
            options.insert(child, parent);
        }
        // 内部实现一个render: vdom变成dom,添加到container
        const render = (vnode, container) => {
            patch(container._vnode || null, vnode, container);
            container._vnode = vnode;
        }
        // 返回的对象就是renderer
        return {
            render,
            createApp: createAppAPI(render) // 柯里化:入第一个参数,等待第二个入参
        }
    }

    // 1.1 创建renderer
    const renderer = createRenderer({
        querySelector(sel) {
            return document.querySelector(sel);
        },
        createElement(tag) {
            return document.createElement(tag);
        },
        insert(child, parent) {
            parent.appendChild(child);
        }
    });
    // 1.createApp 方法
    const Vue = {
        createApp(options) {
            // 是谁创建的真正的实例?执行的实际是renderer.createApp();
            return renderer.createApp(options);
        }
    }
    Vue.createApp({
        data() {
            return {
                name: 'lxq:vue3',
                foo: '暗号 :mua',
            }
        },
        render() {
            return {
                tag: 'h2',
                children: (this.name + this.foo)
            }
        }
    }).mount("#app");

// 为什么vue3 扩展性变得更好了?
// 答:可以支持我们自定义传入编译方法。
</script>