DOM diff 作为工程问题,需要具有一定算法思维,因此经常出现在面试场景中,毕竟这是难得出现在工程领域的算法问题。
无论出于面试目的,还是深入学习目的,都有必要将这个问题搞懂,因此前端精读我们就专门用一个章节说清楚此问题。
Dom diff 是所有现在框架必须做的事情,这背后的原因是,由 Jquery 时代的面向操作过程转变为数据驱动视图导致的。
为什么 Jquery 时代不需要 Dom diff?因为 Dom diff 交给业务处理了,我们调用 .append
或者 .move
之类 Dom 操作函数,就是显式申明了如何做 Dom diff,这种方案是最高效的,因为怎么移动 Dom 只有业务最清楚。
但这样的问题也很明显,就是业务心智负担太重,对于复杂系统,需要做 Dom diff 的地方太多,不仅写起来繁琐,当状态存在交错时,面向过程的手动 Dom diff 容易出现状态遗漏,导致边界错误,就算你没有写出 bug,代码的可维护性也绝对算不上好。
解决方案就是数据驱动,我们只需要关注数据如何映射到 UI,这样无论业务逻辑再复杂,我们永远只需要解决局部状态的映射,这极大降低了复杂系统的维护复杂度,以前需要一个老手写的逻辑,现在新手就能做了,这是非常了不起的变化。
但有利也有弊,这背后 Dom diff 就要交给框架来做了,所以是否能高效的做 Dom diff,是一个数据驱动框架能否应用于生产环境的重要指标,接下来,我们来看看 Dom diff 是如何做的吧。
如图所示,理想的 Dom diff 自然是滴水不漏的复用所有能复用的,实在遇到新增或删除时,才执行插入或删除。这样的操作最贴近 Jquery 时代我们手写的 Dom diff 性能。
可惜程序无法猜到你的想法,想要精确复用就必须付出高昂的代价:时间复杂度 O(n³) 的 diff 算法,这显然是无法接受的,因此理想的 Dom diff 算法无法被使用。
关于 O(n³) 的由来。由于左树中任意节点都可能出现在右树,所以必须在对左树深度遍历的同时,对右树进行深度遍历,找到每个节点的对应关系,这里的时间复杂度是 O(n²),之后需要对树的各节点进行增删移的操作,这个过程简单可以理解为加了一层遍历循环,因此再乘一个 n。
如图所示,只按层比较,就可以将时间复杂度降低为 O(n)。按层比较也不是广度遍历,其实就是判断某个节点的子元素间 diff,跨父节点的兄弟节点也不必比较。
这样做确实非常高效,但代价就是,判断的有点傻,比如 ac 明明是一个移动操作,却被误识别为删除 + 新增。
好在跨 DOM 复用在实际业务场景中很少出现,因此这种笨拙出现的频率实际上非常低,这时候我们就不要太追求学术思维上的严谨了,毕竟框架是给实际项目用的,实际项目中很少出现的场景,算法是可以不考虑的。
下面是同层 diff 可能出现的三种情况,非常简单,看图即可:
那么同层比较是怎么达到 O(n) 时间复杂度的呢?我们来看具体框架的思路。
Vue 的 Dom diff 一共 5 步,我们结合下图先看前三步:
如图所示,第一和第二步分别从首尾两头向中间逼近,尽可能跳过首位相同的元素,因为我们的目的是 尽量保证不要发生 dom 位移。
这种算法一般采用双指针。如果前两步做完后,发现旧树指针重合了,新树还未重合,说明什么?说明新树剩下来的都是要新增的节点,批量插入即可。很简单吧?那如果反过来呢?如下图所示:
第一和第二步完成后,发现新树指针重合了,但旧树还未重合,说明什么?说明旧树剩下来的在新树都不存在了,批量删除即可。
当然,如果 1、2、3、4 步走完之后,指针还未处理完,那么就进入一个小小算法时间了,我们需要在 O(n) 时间复杂度内把剩下节点处理完。熟悉算法的同学应该很快能反映出,一个数组做一些检测操作,还得把时间复杂度控制在 O(n),得用一个 Map 空间换一下时间,实际上也是如此,我们看下图具体做法:
如图所示,1、2、3、4 步走完后,Old 和 New 都有剩余,因此走到第五步,第五步分为三小步:
e:4 d:3 c:2 h:0
这样一个数组,下标 0 是新增,非 0 就是移过来的,批量转化为插入操作即可。最后一步的优化也很关键,我们不要看见不同就随便移动,为了性能最优,要保证移动次数尽可能的少,那么怎么才能尽可能的少移动呢?假设我们随意移动,如下图所示:
但其实最优的移动方式是下面这样:
为什么呢?因为移动的时候,其他元素的位置也在相对变化,可能做了 A 效果同时,也把 B 效果给满足了,也就是说,找到那些相对位置有序的元素保持不变,让那些位置明显错误的元素挪动即是最优的。
什么是相对有序?a c e
这三个字母在 Old 原始顺序 a b c d e
中是相对有序的,我们只要把 b d
移走,这三个字母的位置自然就正确了。因此我们只需要找到 New 数组中的 最长连续子串。具体的找法可以当作一个小算法题了,由于知道每个元素的实际下标,比如这个例子中,下标是这样的:
[b:1, d:3, a:0, c:2, e:4]
肉眼看上去,连续自增的子串有 b d
和 a c e
,由于 a c e
更长,所以选择后者。
换成程序去做,可以采用动态规划,设 dp(i) 为以第 i 个字符串结尾的最长连续子串长度,一次 O(n) 循环即可。
// dp(i) = num[i] > num[i - 1] ? dp(i - 1) + 1 : 1
假设这么一种情况,我们将 a 移到了 c 后,那么框架从最终状态倒推,如何最快的找到这个动机呢?React 采用了 仅右移策略,即对元素发生的位置变化,只会将其移动到右边,那么右边移完了,其他位置也就有序了。
我们看图说明:
遍历 Old 存储 Map 和 Vue 是一样的,然后就到了第二步遍历 New,b
下标从原来的 1
变成了 0
,需要左移才行,但我们不左移,我们只右移,因为所有右移做完后,左移就等于自动做掉了(前面的元素右移后,自己自然被顶到前面去了,实现了左移的效果)。
同理,c 下标从 2
变成了 1
,需要左移才行,但我们继续不动。
a 的下标从 0
变成 2
,终于可以右移了!
后面的 d、e 下标没变,就不用动。我们纵观整体可以发现,b 和 c 因为前面的 a 被抽走了,自然发生了左移。这就是用一个右移代替两个左移的高效操作。
同时我们发现,这也确实找到了我们开始提到的最佳位移策略。
那这个算法真的有这么聪明吗?显然不是,这个算法只是歪打误撞碰对了而已,有用右移替代左移的算法,就有用左移替代右移的算法,既然选择了右移替代左移,那么一定丢失了左移代替右移的效率。
什么时候用左移代替右移效率最高?就是把数组最后一位移到第一位的场景:
显然左移只要一步,那么右移就是 n-1 步,在这个例子就是 4 步,我们看右移算法图解:
首先找到 e,位置从 4
变成了 0
,但我们不能左移!所以只能保持不动,悲剧从此开始。
虽然算法已经不是最优了,但该做的还是要做,其实之前有一个 lastIndex 概念没有说,因为 e 已经在 4
的位置了,所以再把 a 从 0
挪到 1
已经不够了,此时 a 应该从 0
挪到 5
。
方法就是记录 lastIndex = max(oldIndex, newIndex)
=> lastIndex = max(4, 0)
,下一次移动到 lastIndex + 1
也就是 5
:
发现 a 从 0
变成了 5
(注意,此时考虑到 lastIndex 因素),所以右移。
同理,b、c、d 也一样。我们最后发现,发生了 4 次右移,e 也因为自然左移了 4 次到达了首位,符合预期。
所以这是一个有利有弊的算法。新增和删除比较简单,和 Vue 差不多。
PS:最新版 React Dom diff 算法如有更新,欢迎在评论区指出,因为这种算法看来不如 Vue 的高效。
Dom diff 总结有这么几点考虑:
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/t78RAWGYG-sbWw_VL_nswQ
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。