一 : 本文中使用的性能优化策略
1 只写入DOM,因为DOM交互式相当慢的
测试:
console.time('test');
var test=document.getElementById('index-bn').title ;
console.timeEnd('test');
test: 0.091ms
var cache=['111'];
console.time('test');
var test=cache[0];
console.timeEnd('test');
test: 0.021ms
```
1.1 显然不可能完全避免从DOM中读取数据,但我们可以使用缓存数据来减少读取的频率,如果你认为会再次使用某个DOM,你可以将它储存在一个变量中。
1.2 DOM操作是阻塞的,当一个DOM操作在运行时,其他的什么都不能做。
2 给用户反馈的优先级是最高的
2.1 输出
浏览器中的javaScript是单线程,这意味着同一时间javaScript引擎在同一时间只能做一件事情,这个线程通常被称为UI线程,并行是通过将所有任务推到一个队列中来实现的,队列中的任务会在线程变得空闲时执行,如果一个缓慢的DOM操作在执行,传入的时间必须排队等候,直到DOM操作完成处理,如一个缓慢的DOM读取在执行,此时用户与页面进行交互,那么事件将不会马上被处理。
2.2 延迟执行
如果浏览器正忙着给用户返回,而你需要处理AJAX响应这种成本比较大的操作,你可以延迟执行。
例 –无限滚动
<div id="wrapper">
</div>
<script id="slide" type="text/x-handlebars">
<div class="slide">
<p class="logo"></p>
<div class="imgholder">
<a title="by {{ownername}} on Flickr" target="_blank"
href="http://www.flickr.com/photos/{{owner}}/{{id}}"><img class="img" data-src="{{url_n}}"></a>
<p>{{title}}</p>
</div>
</div>
</script>
样式
body {
margin: 0;
padding: 0;
text-align: center;
}
.slide {
width: 300px;
padding: 20px;
margin: 10px auto 10px auto;
height: 250px;
background: #f6f6ed;
position: relative;
text-align: center;
}
.slide p {
width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-family: sans-serif;
}
.slide .img {
max-width: 100%;
max-height: 210px;
text-align: center;
opacity: 1;
display: inline;
-webkit-transition: opacity 0.25s ease-in-out;
-moz-transition: opacity 0.25s ease-in-out;
-o-transition: opacity 0.25s ease-in-out;
transition: opacity 0.25s ease-in-out;
}
.imgholder {
width: 100%;
text-align: center;
}
无限滚动脚本
function handleScroll(e) {
if(window.scrollY + window.innerHeight + 1000 > document.body.offsetHeight) {
fetchBirds();//请求数据
}
handleDefer(); //判断是否可见
}
window.addEventListener('scroll', handleScroll);
获取包含很多图片的内容时,当他们被插入到页面中是,浏览器就开始获取图像,这意味着同时下载,浏览器的下载通道被占满,使用户看的图片显示更慢,同时,由于内容先于用户加载,可能下载永远无法被看到的图像,在这里可以使用延迟加载,当图像被看到时才下载加载图像,使页面更快,还可以节省用户的流量。
延迟加载图像
function isVisible(node) {//判断是否可见
//get the dimensions we need
var scrollTop = window.scrollY,
offTop = node.offsetTop,
offsetHeight = node.offsetHeight,
innerHeight = window.innerHeight,
topViewPort = scrollTop,
bottomViewPort = scrollTop + innerHeight;
//figure out if it is in the viewport or not
return offTop + offsetHeight > topViewPort && offTop < bottomViewPort;
}
function handleDefer() {//可见就加载图片
//find all the slides, I'm fetching the container
//rather than the image because I don't know the image
//heights yet
var list = document.querySelectorAll('.slide');
for (var i=0, len = list.length; i < len; i++) {
thisImg = list[i].querySelector('.img');
if(thisImg.src) {
continue;
}
//if they are visible, update the src
if(isVisible(list[i])) {
var src = thisImg.getAttribute('data-src');
if(src) {
thisImg.src = src;
thisImg.removeAttribute('data-src');
}
}
}
}
现在图像使延迟加载的,但加载时会看到图像空白,体验并不是很好,我们可以加上动画效果并优化延迟加载效果
.slide .img{
transition:opacity .25s ease-in-out;
}
function handleDefer() {
console.time('defer');
var i, list, thisImg, deferSrc, img, handler,
//the slide cache is populated by the function that
//loads the data so that the defer code doesn't need
//to keep querying the dom.
list = slideCache,
len = listLength;
for (i=0; i < len; i++) {
thisImg = list[i].img
var deferSrc = list[i].src;
if(isVisible(list[i].id)) {
//create a closure for for simple preload stuff
handler = function() {
var node, src;
node = thisImg;
src = deferSrc;
return function () {
node.src = src;
node.style.opacity = 1;
loaded[deferSrc] = true;
}
}();
//申明一个image,然后将src赋给改image的src,等待图像下载完成后再讲路径替换(此时图像已经下载到本地了,并使用动画效果显示)
var img = new Image();
img.onload = handler;
img.src = list[i].src;
}
}
console.timeEnd('defer');
}
现在加上了动画效果,在感知的性能上的提升是显著的。
可能你已经注意到了,我们是从slideCache而不是从DOM得到的幻灯片信息,这是应用了“只写入DOM”策略。DOM只有在添加新内容时才会被修改。数据会使用updateSlideCache函数来缓存。
var slideCache;
function updateSlideCache(node) { //在插入数据时调用此函数
var list = node.querySelectorAll('.slide'),
len = list.length
obj;
slideCache = [];
for (var i=0; i < len; i++) {
obj = {
node:list[i],
id:list[i].getAttribute('data-id'),
img:list[i].querySelector('.img')
}
obj.src = obj.img.getAttribute('data-src');
slideCache.push(obj);
}
}
这里的node是一个只包含新添加的数据的节点。
另一个减低速度的是isVisible()函数, isVisible()函数需要大量的DOM读取和检查来执行它的运算。
使用缓存来快速检查元素的可见性
var slideMap = {};
//to simplify look up this now takes a photo id,
//which is the same as a slide id.
function isVisible(id) {
var offTop, offsetHeight, data;
//if the slide is cached we can get the
//values from there
if(slideMap[id]){
offTop = slideMap[id].offTop;
offsetHeight = slideMap[id].offsetHeight;
//if the slide is not cached, update the cache
}else {
node = document.getElementById('s-' + id);
offsetHeight = parseInt(node.offsetHeight);
offTop = parseInt(node.offsetTop);
data = {
node:node,
offTop:offTop,
offsetHeight:offsetHeight
};
slideMap[id] = data;
}
//in the cached case this is just math, no DOM inspection at all
if(offTop + offsetHeight > topViewPort && offTop < bottomViewPort) {
return true;
} else {
return false;
}
}