在不久前的面试中,一个动态加载li到ul里的题目,被问到性能优化的问题,一下把我问住了,就傻乎乎地回答:“每次循环结束的时候,把元素appendChild到ul里”。然后,面试官就点头,恩恩,好的。知道了。一看就知道,自己回到错了,回来后,就去各种查资料,现将一些总结如下,希望大家指正。
其实,这优化就是几个原则:
1. 多使用内存,缓存。
2. 减少cpu请求,减少网络。
3. 页面渲染上,尽量快的把DOM展现出来。
那围绕上面几点,就有这些优化方式:
- 静态资源的压缩合并,多用缓存。
- 多使用cdn(bootcdn之类的)--选就近的cdn可以减少网络耗时。
- 使用ssr(sever side render)后端渲染数据,数据直接显示到html上,而不是通常的加载完后,再依靠ajax来渲染数据。
在渲染呢,(把握dom操作的成本高,要快速的展示为主)
- css放前,js放后面(主要是js有权改变dom结构并阻塞渲染,所以js要在渲染要放在body的后面)
- 懒加载(图片懒加载、下拉刷新等等,后面会稍微讲一讲)
- 减少dom查询,对dom作缓存(var nodeList =$('li'),这里就是作缓存,以及后面用到for循环的时候,尽量避免$('li').length,要用nodeList.length)
- 减少dom操作,多操作的尽量合并在一起执行。(就像前面说的,多个标签一起插入的时候,要是一个一个的话,会很耗成本的。具体方法后面会有介绍。)
- 事件节流(在键盘事件keyup上会有体现,后面也会具体分析)
懒加载:当访问一个页面的时候,先把img元素或是其他元素的背景图片路径替换成一张大小为1*1px图片的路径(这样就只需请求一次,俗称占位图),只有当图片出现在浏览器的可视区域内时,才设置图片正真的路径,让图片显示出来(一般放在data-src属性里,用时直接赋值到src属性里)。这就是图片懒加载。js判断其是否在可视区域,是监听滚动条的。(这个面试也被问到了。/(ㄒoㄒ)/~~)
<script>
var imgNum=document.getElementsByTagName('img').length;
var imgObj=document.getElementsByTagName("img");
var l=0;
window.onscroll=function(){
var seeHeight = document.documentElement.clientHeight;
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
for(var i=l;i<imgNum;i++){
if(imgObj[i].offsetTop < seeHeight + scrollTop){
console.log(imgObj[i].getAttribute("src"));
console.log(imgObj[i].src );
if(imgObj[i].getAttribute("src") == ""){
imgObj[i].src = imgObj[i].getAttribute("data-src");
}
}
if(imgObj[i].offsetTop > seeHeight + scrollTop){
l=i;
break;
}
}
}
</script>
这里再记录下jquery实现
var l=0
$(window).bind("scroll", function(event){
for(var i=l;i<$("img").length;i++){
if($("img").eq(i).offset().top < parseInt($(window).height()) + parseInt($(window).scrollTop())){
if($("img").eq(i).attr("src") == ""){
var lazyloadsrc = $('img').eq(i).data("src");
$("img").eq(i).attr("src",lazyloadsrc);
}
}
if($("img").eq(i).offset().top > parseInt($(window).height()) + parseInt($(window).scrollTop())){
l=i;
break;
}
}
});
这里再插下预加载:图片预先加载到浏览器中,访问者便可顺利地在你的网站上冲浪,并享受到极快的加载速度。这对图片画廊及图片占据很大比例的网站来说十分有利,它保证了图片快速、无缝地发布,也可帮助用户在浏览你网站内容时获得更好的用户体验。
具体分析见:预加载
两者的比较:见:http://www.jianshu.com/p/4876a4fe7731
1)概念:
懒加载也叫延迟加载:JS图片延迟加载,延迟加载图片或符合某些条件时才加载某些图片。
预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
2)区别:
两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。
3)懒加载的意义及实现方式有:
意义:
懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。
实现方式:
1.第一种是纯粹的延迟加载,使用setTimeOut或setInterval进行加载延迟.
2.第二种是条件加载,符合某些条件,或触发了某些事件才开始异步下载。
3.第三种是可视区加载,即仅加载用户可以看到的区域,这个主要由监控滚动条来实现,一般会在距用户看到某图片前一定距离遍开始加载,这样能保证用户拉下时正好能看到图片。
4)预加载的意义及实现方式有:
意义:
预加载可以说是牺牲服务器前端性能,换取更好的用户体验,这样可以使用户的操作得到最快的反映。
实现方式:
实现预载的方法非常多,比如:用CSS和JavaScript实现预加载;仅使用JavaScript实现预加载;使用Ajax实现预加载。
常用的是new Image();设置其src来实现预载,再使用onload方法回调预载完成事件。只要浏览器把图片下载到本地,同样的src就会使用缓存,这是最基本也是最实用的预载方法。当Image下载完图片头后,会得到宽和高,因此可以在预载前得到图片的大小(方法是用记时器轮循宽高变化)。
这里接着我们开始的话题,接着将合并dom(减少dom操作)
var ListNode = document.getElementById('list');
var frag = document.createDocumentFragment();//DOM片段
var i,li;
for(i=0;i<10;i++){
li.document.createElement('li');
li.innerHTML = i +'<br/>';
frag.appendChild(li);//不会触发DOM操作
}
ListNode.appendChild(frag)
对于事件节流来说,就举一个keyup的例子。那就是没按一次按键就会触发一次,如果对于连续敲击键盘的事件来说,就是一个非常不流畅的事情。所以,我们就希望事件节流。再用户输入完一串后再去触发,这时加个延时就好,如果在这个延时里,还有keyup事件就不触发,直到延时结束。
那我最后再加一个页面渲染过程吧,
- 根据html结构生成dom树。
- 根据css生成cssom。
- 将dom和cssom整合成renderTree。
- 根据renderTree开始渲染和展示内容。
- 其中,遇到脚本,会执行并阻塞渲染,遇到img会异步加载。
暂时先记这些吧,后面有了解的也会补充。也希望大家多多指正,也让我加强学习。(^__^)