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

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是观察数据变化执行回调函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/127299.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(二)FreeRTOS任务控制(1)

1.任务延时vTaskDelay //task. hvoid vTaskDelay( const TickType_t xTicksToDelay ); 必须将 INCLUDE_vTaskDelay 定义为 1,此函数才可用。按给定的 tick 数延迟任务。任务保持阻塞的实际时间取决于 tick 频率。 常量 portTICK_PERIOD_MS 配合 tick 周期分辨率可用…

问题 D: 免费馅饼(类数塔问题)

免费馅饼 都说天上不会掉馅饼,但有一天gameboy正走在回家的小径上,忽然天上掉下大把大把的馅饼。说来gameboy的人品实在是太好了,这馅饼别处都不掉,就掉落在他身旁的10米范围内。馅饼如果掉在了地上当然就不能吃了,所以…

【DriveGPT学习笔记】自动驾驶汽车Autonomous Vehicle Planning

原文地址:DriveGPT - Lei Maos Log Book 自动驾驶汽车的核心软件组件是感知、规划和控制。规划是指在给定场景或一系列场景的情况下为自动驾驶汽车制定行动计划的过程,以实现安全和理想的自动驾驶。 用于规划的场景是从感知软件组件获得的。计划的行动将…

深度学习——炼丹

学习率调整策略 自定义学习率调整策略 简单版 net MyNet()optimoptim.Adam(net.parameters(),lr0.05) for param_group in optim.param_groups: param_group["lr"] param_group["lr"]*0.5print(param_group["lr"]) #0.25复杂版&#…

(新手)vue git下载后无法在本地启动项目

npm run serve> xxxxxx0.1.0 serve > vue-cli-service serve vue-cli-service 不是内部或外部命令,也不是可运行的程序 或批处理文件。 原因:缺少依赖 npm i 是 npm install 的简写形式,是用于安装 JavaScript 包和模块的 npm 命令。 npm …

centos 7 kafka2.6单机安装及动态认证SASL SCRAM配置

目录 1.kfaka安装篇 1.1 安装jdk 1.2安装kafka 2.安全篇 2.1 kafka安全涉及3部份: 2.2 Kafka权限控制认证方式 2.3 SASL/SCRAM-SHA-256 配置实例 2.3.1 创建用户 2.3.2 创建 JAAS 文件及配置 3.测试 3.1 创建测试用户 3.2 配置JAAS 文件 3.2.1 生产者配…

arcgis删除细长图斑的方法

1、有一张图斑数据如下: 如上图,有很多细长的面要素,需要保留的仅是图中的块状要素。 2、首先要将被合并的要素进行拆分,具体拆分步骤如下: 将所有要素选中,点击高级编辑中的拆分按钮。 3、拆分后图斑就…

信号、进程、线程、I/O介绍

文章目录 信号进程进程通信线程可/不可重入函数线程同步互斥锁条件变量自旋锁读写锁 I/O操作阻塞/非阻塞I/OI/O多路复用存储映射I/O 信号 信号是事件发生时对进程的通知机制,可以看做软件中断。信号与硬件中断的相似之处在于其能够打断程序当前执行的正常流程。大多…

C语言每日一题(23)兔子的序列

牛客网 BC159 兔子的序列 题目描述 描述 兔子发现了一个数字序列,于是开始研究这个序列。兔子觉得一个序列应该需要有一个命名,命名应该要与这个序列有关。由于兔子十分讨厌完全平方数,所以兔子开创了一个新的命名方式:这个序列…

深度学习数据集大合集—疾病、植物、汽车等

最近又收集了一大批深度学习数据集,今天分享给大家!废话不多说,直接上数据! 1、招聘欺诈数据集 招聘欺诈数据集:共收集了 200,000 条数据,来自三个网站。 该数据集共收集了 200.000 条数据,分别…

算法-小红的ABC(最短回文子串)- [简单]

直通牛客-小红的ABC 题目描述 小红拿到了一个只包含 a , b , c 三种字符的字符串。 小红想知道,这个字符串最短的、长度超过 1 的回文子串的长度是多少? 子串定义:字符串取一段连续的区间。例如"abcca"的子串有"ab"、&…

Java程序设计2023-第五次上机测试

8-1使用按钮选择绘制不同图形 编写一个程序,实现如下的界面,当点击不同按钮时绘制相应的图形。点击“椭圆”,绘制一个椭圆形;点击“矩形”,绘制一个矩形;点击“直线”,绘制一条直线。实现图形绘…

短视频账号矩阵系统saas源码搭建/技术

一、短视频矩阵系统建模----技术api接口--获取用户授权 技术文档分享: 本系统采用MySQL数据库进行存储,数据库设计如下: 1.用户表(user): - 用户ID(user_id) - 用户名&#xff08…

纯前端实现图片验证码

前言 之前业务系统中验证码一直是由后端返回base64与一个验证码的字符串来实现的,想了下,前端其实可以直接canvas实现,减轻服务器压力。 实现 子组件,允许自定义图片尺寸(默认尺寸为100 * 40)与验证码刷新时间(默认时间为60秒)…

MySQL - 系统库之 information_schema

information_schema 用于存储数据库元数据(metadata)。包含了有关数据库服务器、数据库、表、列、索引、权限、存储引擎和其他数据库对象的信息。information_schema 允许用户查询和检索数据库结构和对象的信息,而不需要直接访问系统表或文件…

天体学爱好者基础知识-太阳系//未完待续,业余者的学习

难过的时候,仰望天空吧,人类有时候,做的事情真的太愚昧且无聊了,渺小的尘埃,也可以飘际宇宙。 太阳系-八大行星 卫星围绕着恒星公转。行星必须围绕着恒星公转。 什么是行星?行星和恒星、卫星有什么区别&am…

Fourier分析导论——第2章——Fourier级数的基本属性(E.M. Stein R. Shakarchi)

第 2 章 Fourier级数的基本属性(Basic Properties of Fourier Series) Nearly fifty years had passed without any progress on the question of analytic representation of an arbitrary function, when an assertion of Fourier threw new light on the subject. Thus…

yolov7模型轻量化改进之MobileOne骨干替换

本文在之前文章yolov7分割训练的基础上进行改进: https://blog.csdn.net/qq_41920323/article/details/129464115?spm=1001.2014.3001.5502 具体GitHub工程下载,环境安装配置,数据准备等,请借鉴之前的文章,此处只介绍如何进行改进。 MobileOne地址:https://github.com…

ArrayList集合1

1.ArrayList集合的特点: 1.ArrayList是集合的一个实现类 2.ArrayList内部封装了一个Object类型的对象,初始长度为10,且长度可变 3.ArrayList集合查询快,但是增删慢 4.线程不安全 2.ArrayList集合的一些方法 ①add(Object…

Android ActivityLifecycleCallback使用

在 Android 开发中,ActivityLifecycleCallbacks 是一个接口,用于监听和管理应用程序中 Activity 的生命周期事件。通过实现 ActivityLifecycleCallbacks 接口,可以在 Activity 的创建、启动、暂停、恢复、停止和销毁等各个阶段执行相应的操作…