前端性能优化指南
Posted viana37
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端性能优化指南相关的知识,希望对你有一定的参考价值。
发现关于前端性能优化的博文,比XX军规好太多,详细很多。
原文地址:http://cnodejs.org/topic/55e31bd6898f6bdc7e5551ac
###AJAX
优化
- 缓存
AJAX
:
- -
异步
并不等于即时
。- 请求使用
GET
:
- - 当使用
XMLHttpRequest
时,而URL长度不到2K
,可以使用GET
请求数据,GET
相比POST
更快速。
- -
POST
类型请求要发送两个TCP
数据包。
- - 先发送文件头。
- - 再发送数据。
- -
GET
类型请求只需要发送一个TCP
数据包。
- - 取决于你的
cookie
数量。
###COOKIE
专题
- 减少
COOKIE
的大小。- 使用无
COOKIE
的域。
- - 比如图片
CSS
等静态文件放在静态资源服务器上并配置单独域名,客户端请求静态文件的时候,减少COOKIE
反复传输时对主域名的影响。
###DOM
优化
- 优化节点修改。
- 使用
cloneNode
在外部更新节点然后再通过replace
与原始节点互换。var orig = document.getElementById('container'); var clone = orig.cloneNode(true); var list = ['foo', 'bar', 'baz']; var content; for (var i = 0; i < list.length; i++) content = document.createTextNode(list[i]); clone.appendChild(content); orig.parentNode.replaceChild(clone, orig);
- 优化节点添加
多个节点插入操作,即使在外面设置节点的元素和风格再插入,由于多个节点还是会引发多次reflow。
- - 优化的方法是创建
DocumentFragment
,在其中插入节点后再添加到页面。
- 如
JQuery
中所有的添加节点的操作如append
,都是最终调用DocumentFragment
来实现的,createSafeFragment(document) var list = nodeNames.split( "|" ), safeFrag = document.createDocumentFragment(); if (safeFrag.createElement) while (list.length) safeFrag.createElement( list.pop(); ); ; ; return safeFrag; ;
- 优化
CSS
样式转换。如果需要动态更改CSS样式,尽量采用触发reflow次数较少的方式。
- 如以下代码逐条更改元素的几何属性,理论上会触发多次
reflow
。element.style.fontWeight = 'bold' ; element.style.marginLeft= '30px' ; element.style.marginRight = '30px' ;
- 可以通过直接设置元素的
className
直接设置,只会触发一次reflow
。element.className = 'selectedAnchor' ;
- 减少
DOM
元素数量
- 在
console
中执行命令查看DOM
元素数量。`document.getElementsByTagName( '*' ).length`
- 正常页面的
DOM
元素数量一般不应该超过1000
。-
DOM
元素过多会使DOM
元素查询效率,样式表匹配效率降低,是页面性能最主要的瓶颈之一。DOM
操作优化。
- -
DOM
操作性能问题主要有以下原因。
- -
DOM
元素过多导致元素定位缓慢。- - 大量的
DOM
接口调用。
- -
javascript
和DOM
之间的交互需要通过函数API
接口来完成,造成延时,尤其是在循环语句中。- -
DOM
操作触发频繁的reflow(layout)
和repaint
。- -
layout
发生在repaint
之前,所以layout相对来说会造成更多性能损耗。
- -
reflow(layout)
就是计算页面元素的几何信息。- -
repaint
就是绘制页面元素。- - 对
DOM
进行操作会导致浏览器执行回流reflow
。- - 解决方案。
- - 纯
JAVASCRIPT
执行时间是很短的。- - 最小化
DOM
访问次数,尽可能在js端执行。- - 如果需要多次访问某个
DOM
节点,请使用局部变量存储对它的引用。- - 谨慎处理
html
集合(HTML
集合实时连系底层文档),把集合的长度缓存到一个变量中,并在迭代中使用它,如果需要经常操作集合,建议把它拷贝到一个数组中。- - 如果可能的话,使用速度更快的API,比如
querySelectorAll
和firstElementChild
。- - 要留意重绘和重排。
- - 批量修改样式时,
离线
操作DOM
树。- - 使用缓存,并减少访问布局的次数。
- - 动画中使用绝对定位,使用拖放代理。
- - 使用事件委托来减少事件处理器的数量。
- 优化
DOM
交互在
JAVASCRIPT
中,DOM
操作和交互要消耗大量时间,因为它们往往需要重新渲染整个页面或者某一个部分。
- - 最小化
现场更新
。
- - 当需要访问的
DOM
部分已经已经被渲染为页面中的一部分,那么DOM
操作和交互的过程就是再进行一次现场更新
。- -
现场更新
是需要针对现场
(相关显示页面的部分结构)立即进行更新,每一个更改(不管是插入单个字符还是移除整个片段),都有一个性能损耗。- - 现场更新进行的越多,代码完成执行所花的时间也越长。
- - 多使用
innerHTML
。
- - 有两种在页面上创建
DOM
节点的方法:
- - 使用诸如
createElement()
和appendChild()
之类的DOM
方法。- - 使用
innerHTML
。
- - 当使用
innerHTML
设置为某个值时,后台会创建一个HTML
解释器,然后使用内部的DOM
调用来创建DOM
结构,而非基于JAVASCRIPT
的DOM
调用。由于内部方法是编译好的而非解释执行,故执行的更快。对于小的
DOM
更改,两者效率差不多,但对于大的DOM
更改,innerHTML
要比标准的DOM
方法创建同样的DOM
结构快得多。- 回流
reflow
。
- - 发生场景。
- - 改变窗体大小。
- - 更改字体。
- - 添加移除stylesheet块。
- - 内容改变哪怕是输入框输入文字。
- - CSS虚类被触发如 :hover。
- - 更改元素的className。
- - 当对DOM节点执行新增或者删除操作或内容更改时。
- - 动态设置一个style样式时(比如element.style.width=“10px”)。
- - 当获取一个必须经过计算的尺寸值时,比如访问offsetWidth、clientHeight或者其他需要经过计算的CSS值。
- - 解决问题的关键,就是限制通过DOM操作所引发回流的次数。
- 在对当前DOM进行操作之前,尽可能多的做一些准备工作,保证N次创建,1次写入。
- 在对DOM操作之前,把要操作的元素,先从当前DOM结构中删除:
- - 通过removeChild()或者replaceChild()实现真正意义上的删除。
- - 设置该元素的display样式为“none”。
- 每次修改元素的style属性都会触发回流操作。
element.style.backgroundColor = "blue";
- - 使用更改
className
的方式替换style.xxx=xxx
的方式。- - 使用
style.cssText = '';
一次写入样式。- - 避免设置过多的行内样式。
- - 添加的结构外元素尽量设置它们的位置为
fixed
或absolute
。- - 避免使用表格来布局。
- - 避免在
CSS
中使用JavaScript expressions(IE only)
。- 将获取的
DOM
数据缓存起来。这种方法,对获取那些会触发回流操作的属性(比如offsetWidth
等)尤为重要。- 当对HTMLCollection对象进行操作时,应该将访问的次数尽可能的降至最低,最简单的,你可以将length属性缓存在一个本地变量中,这样就能大幅度的提高循环的效率。
###eval优化
- 避免
eval
:
- -
eval
会在时间方面带来一些效率,但也有很多缺点。
- -
eval
会导致代码看起来更脏。- -
eval
会需要消耗大量时间。- -
eval
会逃过大多数压缩工具的压缩。
###HTML
优化
- 插入
HTML
。
- -
JavaScript
中使用document.write
生成页面内容会效率较低,可以找一个容器元素,比如指定一个div
,并使用innerHTML
来将HTML
代码插入到页面中。- 避免空的
src
和href
。
- - 当
link
标签的href
属性为空、script
标签的src
属性为空的时候,浏览器渲染的时候会把当前页面的URL
作为它们的属性值,从而把页面的内容加载进来作为它们的值。- 为文件头指定
Expires
。
- - 使内容具有缓存性,避免了接下来的页面访问中不必要的HTTP请求。
- 重构HTML,把重要内容的优先级提高。
- Post-load(次要加载)不是必须的资源。
- 利用预加载优化资源。
- 合理架构,使DOM结构尽量简单。
- 利用
LocalStorage
合理缓存资源。- 尽量避免CSS表达式和滤镜。
- 尝试使用defer方式加载Js脚本。
- 新特性:will-change,把即将发生的改变预先告诉浏览器。
- 新特性Beacon,不堵塞队列的异步数据发送。
- 不同之处:网络缓慢,缓存更小,不令人满意的浏览器处理机制。
- 尽量多地缓存文件。
- 使用HTML5 Web Workers来允许多线程工作。
- 为不同的Viewports设置不同大小的Content。
- 正确设置可Tap的目标的大小。
- 使用响应式图片。
- 支持新接口协议(如HTTP2)。
- 未来的缓存离线机制:Service Workers。
- 未来的资源优化Resource Hints(preconnect, preload, 和prerender)。
- 使用Server-sent Events。
- 设置一个Meta Viewport。
###JIT
与GC
优化
untyped
(无类型)。
-
JAVASCRIPT
是个无类型的语言,这导致了如x=y+z
这种表达式可以有很多含义。
- -
y
,z
是数字,则+
表示加法。- -
y
,z
是字符串,则+
表示字符串连接。而JS引擎内部则使用“
细粒度
”的类型,比如:
- - 32-bit* integer。
- - 64-bit* floating-point。
这就要求js类型-js引擎类型,需要做“boxed/unboxed(装箱/解箱)”,在处理一次
x=y+z
这种计算,需要经过的步骤如下。
- . 从内存,读取
x=y+z
的操作符。- . 从内存,读取
y
,z
。- . 检查y,z类型,确定操作的行为。
- .
unbox y,z
。- . 执行操作符的行为。
- .
box x
。- . 把
x
写入内存。只有第
5
步骤是真正有效的操作,其他步骤都是为第5
步骤做准备/收尾,JAVASCRIPT
的untyped
特性很好用,但也为此付出了很大的性能代价。
前端工程化实战:React 模块化开发性能优化和组件化实践