浏览器层面优化前端性能:Reader引擎线程与模块分析优化点

Posted zhoulujun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浏览器层面优化前端性能:Reader引擎线程与模块分析优化点相关的知识,希望对你有一定的参考价值。

优化JS的执行效率 降低样式选择器的复杂度 避免大规模、复杂的布局 尽可能避免触发布局 避免强制同步布局事件的发生 避免快速连续的布局 简化绘制的复杂度 减小绘制区域 优先使用渲染层合并属性 控制层数量 节流 抖动

Reader 引擎线程与模块分析

 

首先是网页内容,加载完输入到HTML解释器,解释后构成DOM树,这期间如果遇到JavaScript代码就交给JavaScript引擎去处理,如果网页中包含CSS,就交给CSS解释器;DOM树简历的时候,渲染引擎接收来自CSS解释器的样式信息,构建一个新的你日不会吐模型,该模型由布局模块计算模型内部各个元素的位置和大小信息

渲染流程有四个主要步骤:

  1. 解析HTML生成DOM树 - 渲染引擎首先解析HTML文档,生成DOM树

  2. 构建Render树 - 接下来不管是内联式,外联式还是嵌入式引入的CSS样式会被解析生成CSSOM树,根据DOM树与CSSOM树生成另外一棵用于渲染的树-渲染树(Render tree),

  3. 布局Render树 - 然后对渲染树的每个节点进行布局处理,确定其在屏幕上的显示位置

  4. 绘制Render树 - 最后遍历渲染树并用UI后端层将每一个节点绘制出来

DOM树与Render树

renderer与DOM元素是相对应的,但并不是一一对应,有些DOM元素没有对应的renderer,而有些DOM元素却对应了好几个renderer,对应多个renderer的情况是普遍存在的,就是为了解决一个renderer描述不清楚如何显示出来的问题,譬如有下拉列表的select元素,我们就需要三个renderer:一个用于显示区域,一个用于下拉列表框,还有一个用于按钮。

另外,renderer与DOM元素的位置也可能是不一样的。那些添加了float或者position:absolute的元素,因为它们脱离了正常的文档流,构造Render树的时候会针对它们实际的位置进行构造。

布局与绘制

上面确定了renderer的样式规则后,然后就是重要的显示元素布局了。当renderer构造出来并添加到Render树上之后,它并没有位置跟大小信息,为它确定这些信息的过程,接下来是布局(layout)。

浏览器进行页面布局基本过程是以浏览器可见区域为画布,左上角为(0,0)基础坐标,从左到右,从上到下从DOM的根节点开始画,首先确定显示元素的大小跟位置,此过程是通过浏览器计算出来的,用户CSS中定义的量未必就是浏览器实际采用的量。如果显示元素有子元素得先去确定子元素的显示信息

布局阶段输出的结果称为box盒模型(width,height,margin,padding,border,left,top,…),盒模型精确表示了每一个元素的位置和大小,并且所有相对度量单位此时都转化为了绝对单位。

在绘制(painting)阶段,渲染引擎会遍历Render树,并调用renderer的 paint() 方法,将renderer的内容显示在屏幕上。绘制工作是使用UI后端组件完成的。

回流与重绘

回流(reflow)当浏览器发现某个部分发生了点变化影响了布局,需要倒回去重新渲染。reflow 会从<html>这个 root frame 开始递归往下,依次计算所有的结点几何尺寸和位置。reflow 几乎是无法避免的。现在界面上流行的一些效果,比如树状目录的折叠、展开(实质上是元素的显示与隐藏)等,都将引起浏览器的 reflow。鼠标滑过、点击……只要这些行为引起了页面上某些元素的占位面积、定位方式、边距等属性的变化,都会引起它内部、周围甚至整个页面的重新渲染。通常我们都无法预估浏览器到底会 reflow 哪一部分的代码,它们都彼此相互影响着。

 

重绘(repaint):改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,但是元素的几何尺寸没有变。

 

关键渲染路径与阻塞渲染

在浏览器拿到HTML、CSS、JS等外部资源到渲染出页面的过程,有一个重要的概念关键渲染路径(Critical Rendering Path)。

例如为了保障首屏内容的最快速显示,通常会提到一个渐进式页面渲染,但是为了渐进式页面渲染,就需要做资源的拆分,那么以什么粒度拆分、要不要拆分,不同页面、不同场景策略不同。

现代浏览器总是并行加载资源,例如,当 HTML 解析器(HTML Parser)被脚本阻塞时,解析器虽然会停止构建 DOM,但仍会识别该脚本后面的资源,并进行预加载。

  • CSS 被视为渲染阻塞资源(包括JS),这意味着浏览器将不会渲染任何已处理的内容,直至 CSSOM 构建完毕,才会进行下一阶段。

    存在阻塞的 CSS 资源时,浏览器会延迟 JavaScript 的执行和 DOM 构建

    • css加载不会阻塞DOM树的解析

    • css加载会阻塞DOM树的渲染

    • css不会阻塞JS的加载

    • css加载会阻塞后面js语句的执行

  • JavaScript 被认为是解释器阻塞资源,HTML解析会被JS阻塞,它不仅可以读取和修改 DOM 属性,还可以读取和修改 CSSOM 属性。

    • 当浏览器遇到一个 script 标记时,DOM 构建将暂停,直至脚本完成执行。

    • JavaScript 可以查询和修改 DOM 与 CSSOM。

    • CSSOM 构建时,JavaScript 执行将暂停,直至 CSSOM 就绪。

    • 没有js的理想情况下,html与css会并行解析,分别生成DOM与CSSOM,然后合并成Render Tree,进入Rendering Pipeline;但如果有js,css加载会阻塞后面js语句的执行,而(同步)js脚本执行会阻塞其后的DOM解析(所以通常会把css放在头部,js放在body尾)

CSS 优先:引入顺序上,CSS 资源先于 JavaScript 资源。JavaScript 应尽量少影响 DOM 的构建。

改变脚本加载次序defer/async/document.createElement

defer

defer 属性表示延迟执行引入 JavaScript,即 JavaScript 加载时 HTML 并未停止解析,这两个过程是并行的。整个 document 解析完毕且 defer-script 也加载完成之后(这两件事情的顺序无关),会执行所有由 defer-script 加载的 JavaScript 代码,再触发DOMContentLoaded(初始的 HTML 文档被完全加载和解析完成之后触发,无需等待样式表图像和子框架的完成加载) 事件。

defer 不会改变 script 中代码的执行顺序,示例代码会按照 1、2、3 的顺序执行。所以,defer 与相比普通 script,有两点区别:载入 JavaScript 文件时不阻塞 HTML 的解析,执行阶段被放到 HTML 标签解析完成之后。

async

async 属性表示异步执行引入的 JavaScript,与 defer 的区别在于,如果已经加载好,就会开始执行,无论此刻是 HTML 解析阶段还是 DOMContentLoaded 触发(HTML解析完成事件)之后。需要注意的是,这种方式加载的 JavaScript 依然会阻塞 load 事件。换句话说,async-script 可能在 DOMContentLoaded 触发之前或之后执行,但一定在 load 触发之前执行。

从上一段也能推出,多个 async-script 的执行顺序是不确定的,谁先加载完谁执行。值得注意的是,向 document 动态添加 script 标签时,async 属性默认是 true。

document.createElement

使用 document.createElement 创建的 script 默认是异步的

通过动态添加 script 标签引入 JavaScript 文件默认是不会阻塞页面的。如果想同步执行,需要将 async 属性人为设置为 false。

优化渲染性能

chrome 官方文档:https://developers.google.com/web/fundamentals/performance/?hl=en

翻译:https://x5.tencent.com/tbs/document/doc-chrome.html

优化JS的执行效率

动画实现使用requestAnimationFrame

setTimeout(callback)和setInterval(callback)无法保证callback函数的执行时机,很可能在帧结束的时候执行,从而导致丢帧。

requestAnimationFrame(callback)可以保证callback函数在每帧动画开始的时候执行。拓展阅读《频率史—从电源频率到音频采样频率与视频帧率:29.97/44.1》、《弄懂javascript的执行机制:事件轮询|微任务和宏任务

长耗时的JS代码放到Web Workers中执行

JS代码运行在浏览器的主线程上,与此同时,浏览器的主线程还负责样式计算、布局、绘制的工作,如果JavaScript代码运行时间过长,就会阻塞其他渲染工作,很可能会导致丢帧。

前面提到每帧的渲染应该在16ms内完成,但在动画过程中,由于已经被占用了不少时间,所以JavaScript代码运行耗时应该控制在3-4毫秒。

如果真的有特别耗时且不操作DOM元素的纯计算工作,可以考虑放到Web Workers中执行。

CSS渲染与布局优化

添加或移除一个DOM元素、修改元素属性和样式类、应用动画效果等操作,都会引起DOM结构的改变,从而导致浏览器要repaint或者reflow。

降低样式选择器的复杂度

  • 尽量保持class的简短,或者使用Web Components框架(如:Omi)。

  • 降低样式选择器的复杂度;使用基于class的方式,比如BEM(Block, Element, Modifier)。 

  • 减少css嵌套,如sass使用@at-root

  • 减少需要执行样式计算的元素的个数

    对于样式计算来说,范围越小、规则越简单的话,处理效率越高。

    • 在过去,如果你修改了body元素的class属性,那么页面里所有元素都要重新计算样式。现代的浏览器中不再这样做了,浏览器不会检查所有受到样式变化影响的元素。因为会对每个DOM元素维护一个独有的样式规则小集合,如果这个集合发生改变,才重新计算该元素的样式。所以,样式计算一般是直接对那些目标元素执行。因此我们应该尽可能减少需要执行样式计算的元素的个数

    • 一般来说在最坏的情况下,样式计算量 = 元素个数 x 样式选择器个数因为对每个元素最少需要检查一次所有的样式,以确认是否

    • Web Components中的样式计算不会跨越Shadow DOM范围,仅在单个的Web Component中进行,而不是在整个页面的DOM树上进行

避免大规模、复杂的布局

布局,就是浏览器计算DOM元素的几何信息的过程:元素大小和在页面中的位置。每个元素都有一个显式或隐式的大小信息,决定于其CSS属性的设置、或是元素本身内容的大小、抑或是其父元素的大小。在Blink/WebKit内核的浏览器和IE中,这个过程称为布局。在基于Gecko的浏览器(比如Firefox)中,这个过程称为Reflow。

尽可能避免触发布局

布局的时间消耗主要在于:

  • 需要布局的DOM元素的数量

  •  布局过程的复杂程度

一份详细的能触发布局、绘制或渲染层合并的CSS属性清单:CSS Triggers

使用flexbox替代老的布局模型

新的Flexbox比旧的Flexbox和基于浮动的布局模型更高效。

在任何情况下,不管是是否使用Flexbox,你都应该努力避免同时触发所有布局,特别在页面对性能敏感的时候(比如执行动画效果或页面滚动时)。

避免强制同步布局事件的发生

将一帧画面渲染到屏幕上的处理顺序如下所示: 

  • 在JavaScript脚本运行的时候,它能获取到的元素样式属性值都是上一帧画面的,都是旧的值。

  • 如果想在这一帧开始的时候,读取一个元素属性值,就需要修改当前元素的某个属性(可能触发重绘与回流)。

  • 为了避免触发不必要的布局过程,你应该首先批量读取元素样式属性,然后再对样式属性进行写操作。

大多数情况下,都不需要先修改然后再读取元素的样式属性值,使用上一帧的值就足够了。过早地同步执行样式计算和布局是潜在的页面性能的瓶颈之一

避免快速连续的布局

比强制同步布局更糟:连续快速的多次执行它。如:

for (var i = 0; i < paragraphs.length; i++) 
     paragraphs[i].style.width = box.offsetWidth + \'px\';

FastDom是一个轻量的库,它提供一个公共接口,能让DOM的读/写操作捆绑在一起。 

https://github.com/wilsonpage/fastdom

简化绘制的复杂度、减小绘制区域

绘制并非总是在内存中的单层画面里完成的。实际上,浏览器在必要时将会把一帧画面绘制成多层画面,然后将这若干层画面合并成一张图片显示到屏幕上。

这种绘制方式的好处是,使用tranforms来实现移动效果的元素将会被正常绘制,同时不会触发对其他元素的绘制。这种处理方式和思想跟图像处理软件(比如Sketch/GIMP/Photoshop)是一致的,它们都是可以在图像中的某个单个图层上做操作,最后合并所有图层得到最终的图像。

提升移动或渐变元素的绘制层
  • 在页面中创建一个新的渲染层的最好方式就是使用CSS属性will-change,同时再与transform属性一起使用,就会创建一个新的组合层:will-change: transform;

  • 对于那些目前还不支持will-change属性、但支持创建渲染层的浏览器,以使用一个3D transform属性来强制浏览器创建一个新的渲染层:transform: translateZ(0);

减少绘制区域

有时候尽管把元素提升到了一个单独的渲染层,渲染工作依然是必须的。渲染过程中一个比较有挑战的问题是,浏览器会把两个相邻区域的渲染任务合并在一起进行,这将导致整个屏幕区域都会被绘制。比如,你的页面顶部有一个固定位置的header,而此时屏幕底部有某个区域正在发生绘制的话,整个屏幕都将会被绘制。 

注意:在DPI较高的屏幕上,固定定位的元素会自动地被提升到一个它自有的渲染层中。但在DPI较低的设备上却并非如此,因为这个渲染层的提升会使得字体渲染方式由子像素变为灰阶(详细内容请参考:Text Rendering),我们需要手动实现渲染层的提升。

减少绘制区域通常需要对动画效果进行精密设计,以保证各自的绘制区域之间不会有太多重叠,或者想办法避免对页面中某些区域执行动画效果。

简化绘制的复杂度

比如js 获取元素的offsetTop ffsetTop 比如getBoundingClientRect 消耗更少。

在css里面,重绘 backgroun 比如 box-shadow 消耗更好。

那些能性能更加耗资源,我也不知道,道友若知,请留言赐教,多谢。手工就 paint profiler 分析对比咯

优先使用渲染层合并属性、控制层数量

  • 只使用transform/opacity来实现动画效果

    应用了transforms/opacity属性的元素必须独占一个渲染层。为了对这个元素创建一个自有的渲染层,你必须提升该元素。在合成层上面的元素,也会合并到此图层中。

  • 用will-change/translateZ属性把动画元素提升到单独的渲染层中

  • 避免滥用渲染层提升:更多的渲染层需要更多的内存和更复杂的管理

    过多的渲染层来带的开销而对页面渲染性能产生的影响,甚至远远超过了它在性能改善上带来的好处。由于每个渲染层的纹理都需要上传到GPU处理,因此我们还需要考虑CPU和GPU之间的带宽问题、以及有多大内存供GPU处理这些纹理的问题。

从性能方面考虑,最理想的渲染流水线是没有布局和绘制环节的,只需要做渲染层的合并即可:

之前也参看:《关于css3之transform一些坑的总结-transform对普通元素的N多渲染

对用户输入事件的处理去抖动

  • 避免使用运行时间过长的输入事件处理函数,它们会阻塞页面的滚动

  • 避免在输入事件处理函数中修改样式属性

  • 对输入事件处理函数去抖动,存储事件对象的值,然后在requestAnimationFrame 回调函数中修改样式属性

具体参看《Debounce 和 Throttle 的原理及实现

 

参考文章:

从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理 https://www.cnblogs.com/cangqinglang/p/8963557.html

Chrome源码剖析、上--多线程模型、进程通信、进程模型https://www.cnblogs.com/v-July-v/archive/2011/04/02/2036008.html

Chrome源代码分析之进程和线程模型(三) https://blog.csdn.net/namelcx/article/details/6582730

http://dev.chromium.org/developers/design-documents/multi-process-architecture

chrome渲染机制浅析 https://www.jianshu.com/p/99e450fc04a5

浅析浏览器渲染原理 https://segmentfault.com/a/1190000012960187

javascript宏任务和微任务 https://www.cnblogs.com/fangdongdemao/p/10262209.html

浏览器与Node的事件循环(Event Loop)有何区别? https://blog.csdn.net/Fundebug/article/details/86487117

 

 


转载本站文章《浏览器层面优化前端性能(2):Reader引擎线程与模块分析优化点》,
请注明出处:https://www.zhoulujun.cn/html/webfront/browser/webkit/2020_0615_8464.html

前端优化带来的思考,浅谈前端工程化转

重复优化的思考

这段时间对项目做了一次整体的优化,全站有了20%左右的提升(本来载入速度已经1.2S左右了,优化度很低),算一算已经做了四轮的全站性能优化了,回顾几次的优化手段,基本上几个字就能说清楚:

传输层面:减少请求数,降低请求量
执行层面:减少重绘&回流

传输层面的从来都是优化的核心点,而这个层面的优化要对浏览器有一个基本的认识,比如:

① 网页自上而下的解析渲染,边解析边渲染,页面内CSS文件会阻塞渲染,异步CSS文件会导致回流

② 浏览器在document下载结束会检测静态资源,新开线程下载(有并发上限),在带宽限制的条件下,无序并发会导致主资源速度下降,从而影响首屏渲染

③ 浏览器缓存可用时会使用缓存资源,这个时候可以避免请求体的传输,对性能有极大提高

衡量性能的重要指标为首屏载入速度(指页面可以看见,不一定可交互),影响首屏的最大因素为请求,所以请求是页面真正的杀手,一般来说我们会做这些优化:

减少请求数

① 合并样式、脚本文件

② 合并背景图片

③ CSS3图标、Icon Font

降低请求量

① 开启GZip

② 优化静态资源,jQuery->Zepto、阉割IScroll、去除冗余代码

③ 图片无损压缩

④ 图片延迟加载

⑤ 减少Cookie携带

很多时候,我们也会采用类似“时间换空间、空间换时间”的做法,比如:

① 缓存为王,对更新较缓慢的资源&接口做缓存(浏览器缓存、localsorage、application cache这个坑多)

② 按需加载,先加载主要资源,其余资源延迟加载,对非首屏资源滚动加载

③ fake页技术,将页面最初需要显示Html&Css内联,在页面所需资源加载结束前至少可看,理想情况是index.html下载结束即展示(2G 5S内)

④ CDN

......

从工程的角度来看,上述优化点半数以上是重复的,一般在发布时候就直接使用项目构建工具做掉了,还有一些只是简单的服务器配置,开发时不需要关注。

可以看到,我们所做的优化都是在减少请求数,降低请求量,减小传输时的耗时,或者通过一个策略,优先加载首屏渲染所需资源,而后再加载交互所需资源(比如点击时候再加载UI组件),Hybrid APP这方面应该尽可能多的将公共静态资源放在native中,比如第三方库,框架,UI甚至城市列表这种常用业务数据。

拦路虎

有一些网站初期比较快,但是随着量的积累,BUG越来越多,速度也越来越慢,一些前端会使用上述优化手段做优化,但是收效甚微,一个比较典型的例子就是代码冗余:

① 之前的CSS全部放在了一个文件中,新一轮的UI样式优化,新老CSS难以拆分,CSS体量会增加,如果有业务团队使用了公共样式,情况更不容乐观;

② UI组件更新,但是如果有业务团队脱离接口操作了组件DOM,将导致新组件DOM更新受限,最差的情况下,用户会加载两个组件的代码;

③ 胡乱使用第三方库、组件,导致页面加载大量无用代码;

......

以上问题会不同程度的增加资源下载体量,如果听之任之会产生一系列工程问题:

① 页面关系错综复杂,需求迭代容易出BUG;

② 框架每次升级都会导致额外的请求量,常加载一些业务不需要的代码;

③ 第三方库泛滥,且难以维护,有BUG也改不了;

④ 业务代码加载大量异步模块资源,页面请求数增多;

......

为求快速占领市场,业务开发时间往往紧迫,使用框架级的HTML&CSS、绕过CSS Sprite使用背景图片、引入第三方工具库或者UI,会经常发生。当遇到性能瓶颈时,如果不从根源解决问题,用传统的优化手段做页面级别的优化,会出现很快页面又被玩坏的情况,几次优化结束后我也在思考一个问题:

前端每次性能优化的手段皆大同小异;代码的可维护性也基本是在细分职责;
既然每次优化的目的是相同的,每次实现的过程是相似的,而每次重新开发项目又基本是要重蹈覆辙的,那么工程化、自动化可能是这一切问题的最终答案

工程问题在项目积累到一定量后可能会发生,一般来说会有几个现象预示着工程问题出现了:

① 代码编写&调试困难

② 业务代码不好维护

③ 网站性能普遍不好

④ 性能问题重复出现,并且有不可修复之势

像上面所描述情况,就是一个典型的工程问题;定位问题、发现问题、解决问题是我们处理问题的手段;而如何防止同一类型的问题重复发生,便是工程化需要做的事情,简单说来,优化是解决问题,工程化是避免问题,今天我们就站在工程化的角度来解决一些前端优化问题,防止其死灰复燃。

文中是我个人的一些开发经验,希望对各位有用,也希望各位多多支持讨论,指出文中不足以及提出您的一些建议

消灭冗余

我们这里做的第一个事情便是消除优化路上第一个拦路虎:代码冗余(做代码精简),单从一个页面的加载来说,他需要以下资源:

① 框架MVC骨架模块&框架级别CSS

② UI组件(header组件、日历、弹出层、消息框......)

③ 业务HTML骨架

④ 业务CSS

⑤ 业务Javascript代码

⑥ 服务接口服务

因为产品&视觉会经常折腾全站样式加之UI的灵活性,UI最容易产生冗余的模块。

UI组件

UI组件本身包括完整的HTML&CSS&Javascript,一个复杂的组件下载量可以达到10K以上,就UI部分来说容易导致两个工程化问题:

① 升级产生代码冗余

② 对外接口变化导致业务升级需要额外开发

UI升级

最理想的升级是保持对外的接口不变甚至保持DOM结构不变,但多数情况的UI升级其实是UI重做,最坏的情况是不做老接口兼容,这个时候业务同事便需要修改代码。为了防止业务抱怨,UI制作者往往会保留两个组件(UI+UI1),如果原来那个UI是核心依赖组件(比如是UIHeader组件),便会直接打包至核心框架包中,这时便出现了新老组件共存的局面,这种情况是必须避免的,UI升级需要遵守两个原则:

① 核心依赖组件必须保持单一,相同功能的核心组件只能有一个

② 组件升级必须做接口兼容,新的特性可以做加法,绝不允许对接口做减法

UI组成

项目之初,分层较好的团队会有一个公共的CSS文件(main.css),一个业务CSS文件,main.css包含公共的CSS,并且会包含所有的UI的样式:

技术分享

半年后业务频道增,UI组件需求一多便容易膨胀,弊端马上便暴露出来了,最初main.css可能只有10K,但是不出半年就会膨胀至100K,而每个业务频道一开始便需要加载这100K的样式文件页面,但是其中多数的UI样式是首屏加载用不到的。

所以比较好的做法是,main.css只包含最核心的样式,理想情况是什么业务样式功能皆不要提供,各个UI组件的样式打包至UI中按需加载:

技术分享

如此UI拆分后,main.css总是处于最基础的样式部分,而UI使用时按需加载,就算出现两个相同组件也不会导致多下载资源。

拆分页面

一个PC业务页面,其模块是很复杂的,这个时候可以将之分为多个模块:

技术分享

一经拆分后,页面便是由业务组件组成,只需要关注各个业务组件的开发,然后在主控制器中组装业务组件,这样主控制器对页面的控制力度会增加。

业务组件一般重用性较低,会产生模块间的业务耦合,还会对业务数据产生依赖,但是主体仍然是HTML&CSS&Javascript,这部分代码也是经常导致冗余的,如果能按模块拆分,可以很好的控制这一问题发生:

技术分享

按照上述的做法现在的加载规则是:

① 公共样式文件

② 框架文件,业务入口文件

③ 入口文件,异步加载业务模块,模块内再异步加载其它资源

这样下来业务开发时便不需要引用样式文件,可以最大限度的提升首屏载入速度;需要关注的一点是,当异步拉取模块时,内部的CSS加载需要一个规则避免对其它模块的影响,因为模块都带有样式属性,页面回流、页面闪烁问题需要关注。

一个实际的例子是,这里点击出发后的城市列表便是一个完整的业务组件,城市选择的资源是在点击后才会发生请求,而业务组件内部又会细分小模块,再细分的资源控制由实际业务情况决定,过于细分也会导致理解和代码编写难度上升:

技术分享

技术分享

demo演示地址代码地址

如果哪天需求方需要用新的城市选择组件,便可以直接重新开发,让业务之间使用最新的城市列表即可,因为是独立的资源,所以老的也是可以使用的。

只要能做到UI级别的拆分与页面业务组件的拆分,便能很好的应付样式升级的需求,这方面冗余只要能避过,其它冗余问题便不是问题了,有两个规范最好遵守:

1 避免使用全局的业务类样式,就算他建议你使用
2 避免不通过接口直接操作DOM

冗余是首屏载入速度最大的拦路虎,是历史形成的包袱,只要能消除冗余,便能在后面的路走的更顺畅,这种组件化编程的方法也能让网站后续的维护更加简单。

资源加载

解决冗余便抛开了历史的包袱,是前端优化的第一步也是比较难的一步,但模块拆分也将全站分成了很多小的模块,载入的资源分散会增加请求数;如果全部合并,会导致首屏加载不需要的资源,也会导致下一个页面不能使用缓存,如何做出合理的入口资源加载规则,如何合理的善用缓存,是前端优化的第二步。

经过几次性能优化对比,得出了一个较优的首屏资源加载方案:

① 核心框架层:mvc骨架、异步模块加载器(require&seajs)、工具库(zepto、underscore、延迟加载)、数据请求模块、核心依赖UI(header组件消息类组件)

② 业务公共模块:入口文件(require配置,初始化工作、业务公共模块)

③ 独立的page.js资源(包含template、css),会按需加载独立UI资源

④ 全局css资源

技术分享

这里如果追求极致,libs.js、main.css与main.js可以选择合并,划分结束后便可以决定静态资源缓存策略了。

资源缓存

资源缓存是为二次请求加速,比较常用的缓存技术有:

① 浏览器缓存

② localstorage缓存

③ application缓存

application缓存更新一块不好把握容易出问题,所以更多的是依赖浏览器以及localstorage,首先说下浏览器级别的缓存。

时间戳更新

只要服务器配置,浏览器本身便具有缓存机制,如果要使用浏览器机制作缓存,势必关心一个何时更新资源问题,我们一般是这样做的:

原文地址: http://www.cnblogs.com/yexiaochai/p/4901341.html 

 

以上是关于浏览器层面优化前端性能:Reader引擎线程与模块分析优化点的主要内容,如果未能解决你的问题,请参考以下文章

前端性能优化——概述

前端性能优化成神之路-总结

前端性能优化总结

前端性能优化总结

前端优化带来的思考,浅谈前端工程化转

前端性能优化之重排和重绘