Vue源码-各实现原理简单总结

1,双向数据绑定的实现

简单总结为 :通过js的层层封装,互相调用,实际就是利用js的Object.defineProperty()方法,然后实现了一个发布订阅模式。

整体逻辑是在vue初始化的时候,通过Object.defineProperty()重写数据的set、get方法。

在每个调用到变量的地方(例如vue的模板字符串{{ }}),会触发重写的get方法,该方法增加一个观察者;

在每次修改变量值的时候,会触发重写的set方法,该方法会通知所有的观察者更新视图。

详细源码解读可查看vue源码解析-响应式原理

2,$mount的实现

vue项目的main.js文件中最后一段代码总是为:

new Vue({ el: '#app', router, store, ... ... render: (h) => h(App)});

也可以这样写:new Vue({ router, store, ... ... render: (h) => h(App).$mount('#app') });

而$mount函数是Vue.property原型上定义的函数。

这个函数中主要代码简单总结为

首先会判断options.render,如果render为空则再获取options.template,如果template也为空则获取传入的el参数(即el),获取到模板内容之后通过处理将模板内容转换成Element类型,并返回render函数(获取render函数的过程见3,complier过程);

然后根据render函数生成虚拟DOM(具体过程见4,render函数生成虚拟DOM过程);

然后进行patch过程(新增节点、更新节点、删除节点),vue执行diff算法渲染页面;

最后调用mounted钩子。

详细源码解读可查看[vue源码解析-mount](https://juejin.cn/post/6932726705521950733 "vue源码解析-mount")

3,complier过程

简单总结:将template转换成render函数。

complier分为构建时complier和运行时complier,二者的区别是:

构建时complier是在本地开发中通过webpack + vue-loader来处理.vue文件,然后在打包的时候转换成render函数;

运行时complier是不使用vue-loader这样的插件,。直接编写template这样的模板代码,然后在浏览器运行的时候将template转换成render函数。

因此本质都是转换成render函数,对比看来构建时complier的性能更好一些。

然而vue的源码对于运行时complier进行了封装实现:在mount挂载时,如果没有render函数,则会先进行模板编译,转换成AST对象(Abstract Syntax Tree抽象语法树,实际是一种自定义的数据结构),然后通过AST转换成render函数并返回,挂载到vm.$options。

4,render函数生成虚拟DOM过程

上述complier过程,只是返回了render函数,但并没有执行,而执行了render函数之后,会生成一个虚拟DOM,也就是一个js对象。虚拟dom存在的意义就是提升性能和跨平台。

生成虚拟dom的过程简单总结为 :通过执行_render函数,来调用createElement函数,生成VNode虚拟DOM并返回。

虚拟DOM与真实DOM的区别在于:虚拟DOM只需要一些重要的属性(tag, data, children, text, elm, context, componentOptions)即可,因此虚拟DOM其实就是一个js对象。

详细源码解读可查看vue源码解析-组件化&虚拟DOM

5,patch过程和diff算法

上述执行render函数并返回虚拟DOM之后,vue会调用update方法去更新视图。而patch函数就是在update方法中进行调用(vm.$el = vm.patch(prevVnode, vnode))。

简单总结 :整个patch函数的执行过程就是以新的虚拟dom为基准,改造旧的虚拟dom(创建节点、更新节点、删除节点)。其中新老vnode对比的过程就是diff算法。

首次渲染页面时,不需要使用diff算法对比:

首次渲染时旧节点oldNode是真实节点(根节点),此时要将其转换成虚拟节点(因为后面节点的remove、invoke和diff对比都是基于虚拟DOM)并保存;然后调用createElm方法(最内层调用封装的原生的document.createElement)创建节点;若有多层组件嵌套,接着调用createChildren(实际是递归调用createElm方法)方法完成多层嵌套的子组件的节点创建。

非首次渲染页面,页面数据发生改变时,diff算法介入:

会触发reactiveSetter方法对比新老数据是否相等,如果相等则直接return;不相等则需要使用diff算法进行更新过程。

触发reactiveSetter方法时,实际是将每个观察者放入一个队列中(一次性更新,提升性能)循环调用了update方法,在最后调用nextTick进行一次性更新。

diff算法的过程,主要是对新老vnode标签、文本(updateProperties)、子节点(updateChildren)等依次进行判断和对比,若二者不一致,通常将新的vnode节点或内容替换到oldVnode节点或者内容,然后对二者都有子节点的情况,再递归对子节点进行diff的过程。整个过程可能有增删改查等操作。因此diff算法是为了可以合理的复用节点,提升性能。

详细源码解读可查看Vue源码解析-patch&diff算法

6,$nextTick的实现

简单总结:$nextTick是通过事件循环的机制,将所有的回调函数放到一个队列中(callbacks)存储,然后在下一次dom同步更新完成页面渲染之后,再执行队列中存储的回调函数,执行完成后再清空队列,便于下次使用。

这样实现的是因为同步更新dom会执行diff对比,将会非常损耗性能。

vue的$nextTick有效使用降解来实现兼容性问题(promise>MutaionObserver>SetImmediate>

setTimeout)

7,watch监听的实现

vue中watch的实现方式有4种

1,变量名: 函数名(字符串形式)

eg:watch: { message: "getMessage" } methods: { getMessage(val) { ... } }

2,变量名: 函数定义

eg:watch: { message: function (old, new) { ... ... } }

3,对象.属性(字符串形式): 函数定义

eg:watch: { "message.read": function (old, new) { ... ... } }

4,对象.属性(字符串形式): 回调函数数组

eg:watch: { "message.read": ["getMessage", function (old, new) { ... ... }] }

methods: { getMessage(val) { ... } }

简单总结

在vue初始化调用initState()的方法中调用了initWatch方法,对watch进行一系列的初始化操作;

该过程主要通过遍历的方式循环获取属性,根据不同watch的实现方式来分别判断并获取回调函数handler;

然后进入new Watcher()收集依赖和更新;收集的依赖之后调用get方法,并挂载到Dep.target上(其中字符串类型的对象属性键值如果是多层嵌套,例如"obj.a.b.c",每一层都会触发回调);而更新也会触发数据劫持set方法,执行dep.notify()方法进行后续的更新操作(基于双向绑定响应式的实现过程)。

8,computed计算属性的实现

简单总结:与watch监听属性类似,也是在Vue初始化的时候进行初始化,通过遍历的方式获取属性;

但不同点是计算属性会为每个属性创建计算属性watcher实例和和渲染属性watcher实例,且将值缓存到vm._computedWatchers中;同时计算属性在对数据set劫持的时候会先进行判断是否有dirty标记属性,如果有则需要通过他依赖的计算观察者watcher.evaluate()方法重新计算;没有则

computed要依赖data属性的数据变化返回一个值;而watch是观察数据变化执行回调函数

相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax