一、对比
学习了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>