当前位置:首页 » 操作系统 » diff源码

diff源码

发布时间: 2023-06-05 19:58:17

㈠ 面试中的网红Vue源码解析之虚拟DOM,你知多少呢深入解读diff算法

众所周知,在前端的面试中,面试官非常爱考dom和diff算法。比如,可能会出现在以下场景

滴滴滴,面试官发来一个面试邀请。接受邀请📞

我们都知道, key 的作用在前端的面试是一道很普遍的题目,但是呢,很多时候我们都只浮于知识的表面,而没有去深挖其原理所在,这个时候我们的竞争力就在这被拉下了。所以呢,深入学习原理对于提升自身的核心竞争力是一个必不可少的过程。

在接下来的这篇文章中,我们将讲解面试中很爱考的虚拟DOM以及其背后的diff算法。 请认真阅读本文~文末有学习资源免费共享!!!

虚拟DOM是用JavaScript对象描述DOM的层次结构。DOM中的一切属性都在虚拟DOM中有对应的属性。本质上是JS 和 DOM 之间的一个映射缓存

要点:虚拟 DOM 是 JS 对象;虚拟 DOM 是对真实 DOM 的描述。

diff发生在虚拟DOM上。diff算法是在新虚拟DOM和老虚拟DOM进行diff(精细化比对),实现最小量更新,最后反映到真正的DOM上。

我们前面知道diff算法发生在虚拟DOM上,而虚拟DOM是如何实现的呢?实际上虚拟DOM是有一个个虚拟节点组成。

h函数用来产生虚拟节点(vnode)。虚拟节点有如下的属性:
1)sel: 标签类型,例如 p、div;
2)data: 标签上的数据,例如 style、class、data-*;
3)children :子节点;
4) text: 文本内容;
5)elm:虚拟节点绑定的真实 DOM 节点;

通过h函数的嵌套,从而得到虚拟DOM树。

我们编写了一个低配版的h函数,必须传入3个参数,重载较弱。

形态1:h('div', {}, '文字')
形态2:h('div', {}, [])
形态3:h('div', {}, h())

首先定义vnode节点,实际上就是把传入的参数合成对象返回。

[图片上传失败...(image-7a9966-1624019394657)]
然后编写h函数,根据第三个参数的不同进行不同的响应。

当我们进行比较的过程中,我们采用的4种命中查找策略:
1)新前与旧前:命中则指针同时往后移动。
2)新后与旧后:命中则指针同时往前移动。
3)新后与旧前:命中则涉及节点移动,那么新后指向的节点,移到 旧后之后
4)新前与旧后:命中则涉及节点移动,那么新前指向的节点,移到 旧前之前

命中上述4种一种就不在命中判断了,如果没有命中,就需要循环来寻找,移动到旧前之前。直到while(新前<=新后&&旧前<=就后)不成立则完成。

如果是新节点先循环完毕,如果老节点中还有剩余节点(旧前和旧后指针中间的节点),说明他们是要被删除的节点。

如果是旧节点先循环完毕,说明新节点中有要插入的节点。

1.什么是Virtual DOM 和Snabbdom
2.手写底层源码h函数
3.感受Vue核心算法之diff算法
4.snabbdom之核心h函数的工作原理

1、零基础入门或者有一定基础的同学、大中院校学生
2、在职从事相关工作1-2年以及打算转行前端的朋友
3、对前端开发有兴趣人群

㈡ Linux里 .diff 是什么文件在gnu下了个gcc源码包,看到有个.diff文件,这是什么文件

就是 difference 的意思。
这个东西是 diff 命令生成的“区别”数据,也就是两个源代码目录他会识别出有什么不同点并且输出为 diff 文件特有的格式。这个输出的文件可以用 patch 命令打在旧版本的源代码上来实现变成新版本的源代码。
这种升级源代码版本的办法,可以很好的解决每次下载源代码都要重头全部下载导致数据量很大的问题。

如果你下载源代码包只有 .diff ,那么证明你下载错了,因为只有旧的特定版本才能用 diff 升级为特定的新版本。diff 文件是前后两个版本关联的,不是随便可以用的。

㈢ web前端diff 算法深入一下

有同学问:能否详细说一下 diff 算法。

详细的说,请阅读这篇文章,有疑问的地方欢迎留言一起讨论。

因为 diff 算法是 vue2.x , vue3.x 以及 react 中关键核心点,理解 diff 算法,更有助于理解各个框架本质。

说到“diff 算法”,不得不说“虚拟 Dom”,因为这两个息息相关。

比如:

等等

我们先来说说虚拟 Dom,就是通过 JS 模拟实现 DOM ,接下来难点就是如何判断旧对象和新对象之间的差异。

Dom 是多叉树结构,如果需要完整的对比两棵树的差异,那么算法的时间复杂度 O(n ^ 3),这个复杂度很难让人接收,尤其在 n 很大的情况下,于是 React 团队优化了算法,实现了 O(n) 的复杂度来对比差异。

实现 O(n) 复杂度的关键就是只对比同层的节点,而不是跨层对比,这也是考虑到在实际业务中很少会去跨层的移动 DOM 元素。

虚拟 DOM 差异算法的步骤分为 2 步:

实际 diff 算法比较中,节点比较主要有 5 种规则的比较

部分源码 https://github.com/vuejs/vue/blob//src/core/vdom/patch.js#L501 如下:

在 reconcileChildren 函数的入参中

diff 的两个主体是:oldFiber(current.child)和 newChildren(nextChildren,新的 ReactElement),它们是两个不一样的数据结构。

部分源码

很多时候手工优化 dom 确实会比 virtual dom 效率高,对于比较简单的 dom 结构用手工优化没有问题,但当页面结构很庞大,结构很复杂时,手工优化会花去大量时间,而且可维护性也不高,不能保证每个人都有手工优化的能力。至此,virtual dom 的解决方案应运而生。

virtual dom 是“解决过多的操作 dom 影响性能”的一种解决方案。

virtual dom 很多时候都不是最优的操作,但它具有普适性,在效率、可维护性之间达到平衡。

virutal dom 的意义:

vue2.x 的 diff 位于 patch.js 文件中,该算法来源于 snabbdom,复杂度为 O(n)。了解 diff 过程可以让我们更高效的使用框架。react 的 diff 其实和 vue 的 diff 大同小异。

最大特点:比较只会在同层级进行, 不会跨层级比较。

对比之前和之后:可能期望将 直接移动到

的后边,这是最优的操作。

但是实际的 diff 操作是:

vue 中也使用 diff 算法,有必要了解一下 Vue 是如何工作的。通过这个问题,我们可以很好的掌握,diff 算法在整个编译过程中,哪个环节,做了哪些操作,然后使用 diff 算法后输出什么?

解释:

mount 函数主要是获取 template,然后进入 compileToFunctions 函数。

compileToFunction 函数主要是将 template 编译成 render 函数。首先读取缓存,没有缓存就调用 compile 方法拿到 render 函数的字符串形式,在通过 new Function 的方式生成 render 函数。

compile 函数将 template 编译成 render 函数的字符串形式。后面我们主要讲解 render

完成 render 方法生成后,会进入到 mount 进行 DOM 更新。该方法核心逻辑如下:

上面提到的 compile 就是将 template 编译成 render 函数的字符串形式。核心代码如下:

compile 这个函数主要有三个步骤组成:

分别输出一个包含

parse 函数:主要功能是 将 template 字符串解析成 AST(抽象语法树) 。前面定义的 ASTElement 的数据结构,parse 函数就是将 template 里的结构(指令,属性,标签) 转换为 AST 形式存进 ASTElement 中,最后解析生成 AST。

optimize 函数(src/compiler/optomizer.js):主要功能是 标记静态节点 。后面 patch 过程中对比新旧 VNode 树形结构做优化。被标记为 static 的节点在后面的 diff 算法中会被直接忽略,不做详细比较。

generate 函数(src/compiler/codegen/index.js):主要功能 根据 AST 结构拼接生成 render 函数的字符串

其中 genElement 函数(src/compiler/codgen/index.js)是根据 AST 的属性调用不同的方法生成字符串返回。

总之:

就是 compile 函数中三个核心步骤介绍,

patch 函数 就是新旧 VNode 对比的 diff 函数,主要是为了优化 dom,通过算法使操作 dom 的行为降低到最低, diff 算法来源于 snabbdom,是 VDOM 思想的核心。snabbdom 的算法是为了 DOM 操作跨级增删节点较少的这一目标进行优化, 它只会在同层级进行,不会跨层级比较。

总的来说:

在创建 VNode 就确定类型,以及在 mount/patch 的过程中采用位运算来判断一个 VNode 的类型,在这个优化的基础上再配合 Diff 算法,性能得到提升。

可以看一下 vue3.x 的源码:https://github.com/vuejs/vue/blob//src/core/vdom/patch.js

对 oldFiber 和新的 ReactElement 节点的比对,将会生成新的 fiber 节点,同时标记上 effectTag,这些 fiber 会被连到 workInProgress 树中,作为新的 WIP 节点。树的结构因此被一点点地确定,而新的 workInProgress 节点也基本定型。在 diff 过后,workInProgress 节点的 beginWork 节点就完成了,接下来会进入 completeWork 阶段。

snabbdom 算法:https://github.com/snabbdom/snabbdom

定位:一个专注于简单性、模块化、强大功能和性能的虚拟 DOM 库。

snabbdom 中定义 Vnode 的类型(https://github.com/snabbdom/snabbdom/blob//src/vnode.ts#L12)

init 函数的地址:

https://github.com/snabbdom/snabbdom/blob//src/init.ts#L63

init() 函数接收一个模块数组 moles 和可选的 domApi 对象作为参数,返回一个函数,即 patch() 函数。

domApi 对象的接口包含了很多 DOM 操作的方法。

源码:

https://github.com/snabbdom/snabbdom/blob//src/init.ts#L367

源码:

https://github.com/snabbdom/snabbdom/blob//src/h.ts#L33

h() 函数接收多种参数,其中必须有一个 sel 参数,作用是将节点内容挂载到该容器中,并返回一个新 VNode。

在 vue2.x 不是完全 snabbdom 算法,而是基于 vue 的场景进行了一些修改和优化,主要体现在判断 key 和 diff 部分。

1、在 snabbdom 中 通过 key 和 sel 就判断是否为同一节点,那么在 vue 中,增加了一些判断 在满足 key 相等的同时会判断,tag 名称是否一致,是否为注释节点,是否为异步节点,或者为 input 时候类型是否相同等。

https://github.com/vuejs/vue/blob//src/core/vdom/patch.js#L35

2、diff 差异,patchVnode 是对比模版变化的函数,可能会用到 diff 也可能直接更新。

https://github.com/vuejs/vue/blob//src/core/vdom/patch.js#L404

㈣ 如何使用源代码构建一个可用的 Ubuntu 软件包

用dpkg-source -x
foo.dsc从foo.orig.gz和foo.diff.gz创建工作目录foo:一份发行版中立的源码目录,加上一个debian目录以及目录下的meta文件,就构成了一份可以生成二进制deb包的源码工作目录。其实从apt-get
source抓下来的目录,已经是通过dpkg-source -x解压过的了。dpkg-source
-x所做的主要事情就是1.解压;2.把foo.diff.gz里的patch打到原始文件上。生成的foo目录下的源文件,都已经是打过deb源码包里的patch了的。
在foo目录下,执行dpkg-buildpackage -us
-uc构建包。-us和-uc参数是不做签名,适合于本地构建本地使用的情况。这个命令的输出有两个,一个是二进制deb包,另一个是源码包,为什么这里还要生成源码包?因为你可能改动某些文件,那么会生成新的diff.gz来记录所有你针对原始源码的改动,不管发布还是保存更改都更方便,下一次你只需要在生成的新的.dsc文件上执行dpkg-source -x就可以产生一个一模一样的源码了。如果你什么都没改动,那么新产生的源码包同你构建所来源的源码包是一样的。你也可以用参数-b和-S来控制这次构建只产生二进制包或者只产生源码包。
两个最重要的meta文件,debian/control和debian/rules。control文件决定了哪些二进制包将从这份源码目录中构建,一个源码目录往往是好几个二进制包的输入源。你不想生成哪个屏蔽它就行。二进制包的运行时依赖关系也在包的声明中可见,并且control文件也声明了构建过程中的依赖,不过可以给dpkg-buildpackage传-d参数来忽略构建依赖。
debian/rules文件其实就是个Makefile,你可以执行make -f debian/rules target来单独执行某个目标。rules文件里基本上都是对debhelper脚本函数的调用,像是dh_*这样的函数,它们负责大部分的构建过程。常用的clean, install目标在rules文件中也有,有些基于源码包的Makefile上所做的事情如make clean需要通过make -f debian/rules clean来代替。
和传统意义的Make过程有点不一样的就是,默认状态下,每次dpkg-buildpackage,其实都是把从configure.ac生成configure脚本,到生成Makefile,到构建source,到安装binary都做一遍,哪怕你并没有改过configure.ac,或者改过源代码.c文件,假如构建失败了,就需要尝试改动源代码重新构建,有时候需要反复尝试这个过程直到构建成功,如果包很大的话那需要花费的时间就很长,这时传入-nc参数可以让dpkg-buildpackage保留当前的构建结果,就像传统的make一样只会从出错的地方重新开始。当然,当对源代码的改动终止后,最后还是需要再执行一遍不带-nc参数的命令”dpkg-buildpackage -us -uc”来重新完全构建一遍,否则在生成源码包时可能会出错。
dpkg-buildpackage不用担心它会自动改变你的源文件(即通过dpkg-source
-x产生的文件),当然前提是你确实改动的是”源”文件,比如是configura.ac而不是configure,是dkms.conf.in而不是dkms.conf。
构建软件时做得最多的事就是根据自己系统的需求调整./configure参数了吧,比如–enable–xxx或者–disable-xxx,在rules文件中,通过带override前缀的target可以起到为默认的target定制参数的目的,如override_dh_auto_xconfigure:
对源码包有修改最好通过dch -i来生成一个新的changelog文件,每个change item的title部分都是表示这次change的最新版本号,dpkg-buildpackage的输出二进制包的版本号其实就是从changelog里提取的(不是写在control文件里的)。

热点内容
scratch少儿编程课程 发布:2025-04-16 17:11:44 浏览:628
荣耀x10从哪里设置密码 发布:2025-04-16 17:11:43 浏览:357
java从入门到精通视频 发布:2025-04-16 17:11:43 浏览:75
php微信接口教程 发布:2025-04-16 17:07:30 浏览:300
android实现阴影 发布:2025-04-16 16:50:08 浏览:788
粉笔直播课缓存 发布:2025-04-16 16:31:21 浏览:338
机顶盒都有什么配置 发布:2025-04-16 16:24:37 浏览:204
编写手游反编译都需要学习什么 发布:2025-04-16 16:19:36 浏览:801
proteus编译文件位置 发布:2025-04-16 16:18:44 浏览:357
土压缩的本质 发布:2025-04-16 16:13:21 浏览:583