最后
在面试前我花了三个月时间刷了很多大厂面试题,最近做了一个整理并分类,主要内容包括html,css,JavaScript,ES6,计算机网络,浏览器,工程化,模块化,Node.js,框架,数据结构,性能优化,项目等等。
包含了腾讯、字节跳动、小米、阿里、滴滴、美团、58、拼多多、360、新浪、搜狐等一线互联网公司面试被问到的题目,涵盖了初中级前端技术点。
-
HTML5新特性,语义化
-
浏览器的标准模式和怪异模式
-
xhtml和html的区别
-
使用data-的好处
-
meta标签
-
canvas
-
HTML废弃的标签
-
IE6 bug,和一些定位写法
-
css js放置位置和原因
-
什么是渐进式渲染
-
html模板语言
-
meta viewport原理
- C就是业务逻辑、交互逻辑,比如当用户输入完数据后,如何将数据处理并填充渲染到页面上去的过程,反之亦然;
在这个模式中C也就是Controller这一层其实非常薄弱,职能不强,View这一层倒反正非常厚,所有对DOM处理的操作都在这一层,一旦数据变动了就要去重新操作DOM,用设计模式的话说就是 耦合度非常高,如果业务有变动往往代码就要重写,并且还写不好…所以也就有了MVVM这种模式,那么这两种模式最大的区别是什么?
在回答这个问题之前,我先问个问题,DOM渲染页面之后,什么操作会对浏览器的性能产生较大影响?在我看来GUI 渲染线程中的重绘(Repaint)和回流(reflow)会对浏览器的性能产生较大影响,可能会有小伙伴不大清楚什么是重绘、什么是回流,这里简单说一下吧
-
重绘: 当一些元素需要更新属性,而这些属性的更新仅仅影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
-
回流: 当页面布局发生变化,比如我们修改一个元素的宽高,这时候DOM树结构随之也会发生变化,而DOM树与渲染树是紧密相连的,DOM树构建完,渲染树也会随之对页面进行再次渲染,这个过程就叫回流。
了解重绘和回流之后,那么我们就可以来说说为什么会有MVVM这种模式了,先说我的个人结论,我认为MVC和MVVM这两者本质上其实没多大区别,MVVM是脱胎于MVC的一种开发模式,传统MVC设计模式中针对DOM的操作几乎都是手动的,也就是需要开发人员去手动开发相关的控制代码,比如我们更新数据需要先获取到DOM,然后对DOM中的值进行覆盖,这也就导致了开发人员会频繁的去操作DOM,页面会频繁的在重绘和回流,MVVM这种模式就优化了这一步,它将Controller的职能完全放大,并且数据与页面DOM之间原则上不再有直接关联,通通交给Controller,包括对DOM的操作也都是框架自动去渲染,如下示例:
这么改的好处就是,开发人员可以专注于数据与业务的处理,C这一层就进行了一次进化,将DOM操作这些全部归纳到VM(ViewModel)里,VM的本质也会去操作DOM,但框架有最优秀的一批开发大佬写算法计算如何最优的去动态处理DOM,什么样的操作DOM消耗的性能最小,体验最好,那么 VM里面是如何做优化的呢,那就要说到这一小节的主角了,Virtual-dom;
Virtual-dom,又叫虚拟DOM,本质上就是用JS对象在描述DOM结构, 为什么要这么干,因为操作DOM就会重绘与回流,就会增加浏览器压力,但是操作DOM却非常简单,举一个简单的例子
// html代码
换成虚拟DOM后,差不多就是类似于这种
{
tagName:“div”,
children:[
{
tagName:“a”,
href:“oliver.blog.csdn.net”
}
]
}
每次如果要渲染页面,只需要根据虚拟DOM树就可以去渲染了,因此,在MVVM中每一次变更数据后,对应的虚拟DOM树节点上的属性也会跟着变,最后根据这个树再统一去渲染页面,这样就可以大幅提高性能,可能有小伙伴还是不大明白,使用虚拟DOM后怎么就提高性能了,再举一个实际一点例子吧:
比如在一次操作中,我门需要更新20个DOM节点,浏览器在收到第一个DOM更新的请求后并不知道还有19次更新的操作,因此浏览器会马上执行流程,最终执行20次。例如,第一次计算完,紧接着下一个DOM更新请求,而在第二次DOM更新中这个节点的值就变了,这也就导致了第一次计算结果白白浪费了。即使计算机硬件一直在迭代更新,操作DOM的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户体验;
使用虚拟DOM后就会变成这样:
在一次操作中有20次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这20次更新的diff内容保存到本地一个对象中,最终将这个JS对象一次性更新到DOM树上,再进行后续操作,避免大量无谓的计算量。所以,虚拟DOM节点的好处很明显,页面的更新可以先全部反映在JS对象(虚拟DOM)上,操作内存中的JS对象的速度显然要更快,等更新完成后,再将最终的JS对象映射成真实的DOM,交由浏览器去绘制。
(注意:这里强调了是一次操作)
到这里,相信小伙伴应该能明白Virtual-dom是什么以及它的优点了吧,在Virtual-dom中如何比对新节点和老节点的算法,就是我们的主角Diff算法;
其实Virtual-dom的优势远远不止性能上的提升,甚至正因为它的存在,使得JavaScript的跨端再次提升了一个档次,因为通过虚拟DOM我们完全可以识别到页面元素的组成以及样式
通过将传统的HTML,CSS,JavaScript转成Virtual-dom,而Virtual-dom就像是一种规范、规则化的标准数据,这种数据描述了整个页面上所有的元素属性,之后在对应的端上反译,达到跨端的目的;
在MVVM的开发模式中引入了Virtual-dom这个概念,它用JS对象描述了整个DOM树,在更新DOM之前先统一由Virtual-dom进行处理,最终一次性更新到DOM上,这样便极大的提升了浏览器性能,这Diff算法便是Virtual-dom中比对新老节点的算法;
================================================================
总算介绍到Diff算法了,上文我们说到,Diff算法其实是用在Virtual-dom里的,在MVVM的开发模式中新增了一层抽象层用来模拟DOM结构,而Diff算法就是用来比对计算,比对新旧两个Virtual-dom里哪些节点发生了变化,仅针对这些发生变化的DOM节点做出更新即可;
按层级diff比对
先说说Diff算法在MVVM框架中的比对策略吧,毕竟新旧两个Virtual-dom的比对不是瞎比对,看个图
在Vue或者说是React中都是遵循的同层级比对的策略,也就是说,旧Virtual-dom第一层蓝色只与新Virtual-dom的第一层做比对,旧Virtual-dom第二层紫色只与新Virtual-dom的第二层做比对,通过统计发现只有很少一部分情况会出现因为操作整个DOM结构都发生了更新,绝大多数情况都是DOM的层级不会变更;
按类型diff比对
这个怎么理解呢,举个例子,在Vue或者React中都有组件,diff算法在比对的时候如果发现组件的类型不一样了,那么这个组件包括其子组件都会被销毁,替换成新组件,而不会费时费力的去继续比对其子组件是否发生变化
比如这个例子中第一层的组件类型是三角形,它引用了两个子组件,经过变化后第一层的组件类型变成了五边形,但是子组件没有变化,这是diff不会去说因为只要第一层变化了,子组件都没有变因此只改变第一层的组件,保留第二次的三角,它会销毁整个蓝色三角以及其所有子组件,整体替换成五边形,并且新建了两个子组件;
现在还不看源码,还是先弄明白Diff算法的执行过程,源码可以放到下一篇博文进行逐行分析,先看一下图例吧,图例里就是假设需要比对的新旧节点
先说一下含义:上面一排,代表的是旧的节点,下面一排,代表的是新的节点,大圆圈代表的Virtual-dom也就是虚拟DOM,小圆圈代表的是虚拟DOM对应的真实DOM;
当比对开始后,Diff算法会分别给这两个节点序列标注oldStartIdx、oldEndIdx、newStartIdx、newEndIdx的指针,标记完大致如下:
换句话说就是分别标记了起始位置和结束位置,标记结束就是正式开始比对逻辑;
第一步:比对oldStartIdx和newStartIdx
比对是会比对旧Virtual-dom的oldStartIdx和新Virtual-dom的newStartIdx的节点是否是同一个**,**也是如下图的比对
结果发现旧Virtual-dom的oldStartIdx和新Virtual-dom的newStartIdx的节点是同一个,那么**oldStartIdx和newStartIdx会都向后移动一位,变成如下所示**
继续比对第二个节点,也就是节点B和节点E,这是Diff发现节点B和节点E并不是同一个节点,那么此时就会去比对endIdx
第二步:比对oldEndIdx和newEndIdx
和startIdx一样,比对是会比对旧Virtual-dom的oldEndIdx和新Virtual-dom的newEndIdx的节点是否是同一个** ,**也是如下图的比对
比对结果发现旧Virtual-dom的oldEndIdx和新Virtual-dom的newEndIdx的节点是同一个,那么此时endIdx也会有startIdx一样往前移动1位
此时会进入第三个循环,继续比对旧Virtual-dom的oldStartIdx和新Virtual-dom的newStartIdx的节点是否是同一个,此时oldStartIdx和newStartIdx不是同一个,那么就继续去比对旧Virtual-dom的oldEndIdx和新Virtual-dom的newEndIdx,发现也不是同一个,那么此时会进行第三种比对,比对oldSatrtIdx和newEndIdx
第三步:比对oldSatrtIdx和newEndIdx
这一步比对也就是会比对oldSatrtIdx和newEndIdx,如图
比对结果发现oldSatrtIdx和newEndIdx是同一个节点,但是位置变化了,那么此时diff就会将B的位置进行转移重新排序,调整位置如下
并且,在转移的同时oldSatrtIdx和newEndIdx的位置也会分别移动一位,变成如下所示
之后继续进行新的一轮比对:
-
比对oldSatrtIdx和newStartIdx是否是同一个节点,发现不是,继续比对;
-
比对oldEndIdx和newEndIdx是否是同一个节点,发现不是,继续比对;
-
比对oldStartIdx和newEndIdx是否是同一个节点,发现不是,继续比对;
-
此时会出现一种新的比对方式,比对oldEndIdx和newStartIdx是否是同一个节点
第四步:比对oldEndIdx和newStartIdx
最后
本人分享一下这次字节跳动、美团、头条等大厂的面试真题涉及到的知识点,以及我个人的学习方法、学习路线等,当然也整理了一些学习文档资料出来是给大家的。知识点涉及比较全面,包括但不限于前端基础,HTML,CSS,JavaScript,Vue,ES6,HTTP,浏览器,算法等等
前端视频资料:
字节跳动、美团、头条等大厂的面试真题涉及到的知识点,以及我个人的学习方法、学习路线等,当然也整理了一些学习文档资料出来是给大家的。知识点涉及比较全面,包括但不限于前端基础,HTML,CSS,JavaScript,Vue,ES6,HTTP,浏览器,算法等等
[外链图片转存中…(img-qrhBpijG-1715473689086)]
前端视频资料:
[外链图片转存中…(img-xHVwJoca-1715473689087)]