更快速将你的页面展示给用户[前端优化篇]

更快速将你的页面展示给用户[前端优化篇]
<div id="cnblogs_post_body">现在许多公司往往注重后端优化,而忽略了前端优化想想如果辛苦优化了服务器,后台,排查了sql
却在最后页面加载展示的时候很慢,也得不偿失其实,前后台优化都是相辅相成的
后台优化好了,响应请求速度快,前台展示的更迅速
前台优化了不必要的请求,后台压力也会更小大纲:
<div class="q_menu" style="padding: 5px 5px 5px 15px; border: solid 1px #ccc; width: 250px; font-size: 12px; line-height: 12px;">

<strong>请求优化</strong>

<a href="#1">图片延迟加载</a>

<a href="#2">ajax局部加载</a>

<a href="#2">数据</a><a href="#3">预加载</a>

<strong>资源优化</strong>

<a href="#4">资源压缩(uglify-js,clean-css)</a>

<a href="#5">资源合并</a>

<a href="#6">图片合并csssprite</a>

<a href="#7">iconfont</a>

<a href="#8">引用优化</a>

<a href="#9">单独域名存放资源</a>

<strong>缓存</strong>

<a href="#10">Cache-Control缓存策略</a>

<a href="#11">离线存储</a>

<a href="#12">本地存储localStorage</a>

<strong>其它的</strong>

<a href="#13">css3替换js动画</a>

<a href="#14">替换flash</a>

<a href="#15">结语</a>

</div>

<strong>请求优化</strong>

首先我们来优化HTTP请求数

由于用户浏览的,往往只是局部网页,
所以只加载用户可视范围内的资源,就会减少一些不必要的请求,也会减少浏览器加载资源的消耗
考虑到移动端可视范围,网络流量,性能,延迟加载作用尤为明显
<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>图片延迟加载</strong><a name="1"></a></div>
适合延迟加载的东西很多,最需要的当然是图片,这里推荐lazyload

<a href="https://github.com/tuupola/jquery_lazyload" target="_blank">https://github.com/tuupola/jquery_lazyload</a>

图片延迟加载的原理就首先将要延迟加载的图片src替换为空白图片或者参数指定的loading图
然后根据当前元素的位置(offset)来判断是否在页面可视范围(页面宽/高度+滚动宽/高度)

如果在,就将真实图片资源路径替换回src让浏览器加载

防止浏览器解析到HTML中<img>标签的src属性就开始下载资源,最好将原<img>的src属性去掉
统一配置lazyload的参数去加载loading图吧,如我们项目中这样:

<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;"> $(".main_content img").lazyload({
placeholder: "/images/loading.gif",
threshold:200
});
</pre>
</div>
再来看到lazyload的源代码,可视范围判断上下左右,写的十分完善
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;collapse:true;;gutter:true;"> $.belowthefold = function(element, settings) {
var fold;

    if (settings.container === undefined || settings.container === window) {
        fold = (window.innerHeight ? window.innerHeight : $window.height()) + $window.scrollTop();
    } else {
        fold = $(settings.container).offset().top + $(settings.container).height();
    }

    return fold <= $(element).offset().top - settings.threshold;
};

$.rightoffold = function(element, settings) {
    var fold;

    if (settings.container === undefined || settings.container === window) {
        fold = $window.width() + $window.scrollLeft();
    } else {
        fold = $(settings.container).offset().left + $(settings.container).width();
    }

    return fold <= $(element).offset().left - settings.threshold;
};

$.abovethetop = function(element, settings) {
    var fold;

    if (settings.container === undefined || settings.container === window) {
        fold = $window.scrollTop();
    } else {
        fold = $(settings.container).offset().top;
    }

    return fold >= $(element).offset().top + settings.threshold  + $(element).height();
};

$.leftofbegin = function(element, settings) {
    var fold;

    if (settings.container === undefined || settings.container === window) {
        fold = $window.scrollLeft();
    } else {
        fold = $(settings.container).offset().left;
    }

    return fold >= $(element).offset().left + settings.threshold + $(element).width();
};

$.inviewport = function(element, settings) {
     return !$.rightoffold(element, settings) && !$.leftofbegin(element, settings) &&
            !$.belowthefold(element, settings) && !$.abovethetop(element, settings);
 };</pre>

</div>

关于图片这里,除了延迟加载外,用户上传的图片以及我们所用的资源图片都应该进行压缩处理
如需要进一步提高压缩率,可以使用例如:google开发的webp图片格式等..
不过不是所有浏览器都支持webp格式,需要针对浏览器响应
<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>ajax局部加载数据</strong><a name="2"></a></div>
根据上面这段代码,其实我们就可以延迟加载其他内容了,
总之呢,这里我们的目的就是尽量减少不必要的请求

比如现在用的很多的下拉式翻页,就是判断到页面底部之后再ajax获取下页内容

如果考虑到网页只是局部更新的话,那使用ajax是很合适的
好处显而易见,无需重新请求整页,小巧快速,网页展示也友好一些

善用ajax对前端性能,体验都是有改善的

但是也要考虑到对搜索引擎的友好,
如果页面整体功能改变了,或者页面改动量大就要进行取舍了。

<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>预加载</strong><a name="3"></a></div>
延迟加载的目的就是减少不必要的请求,在用户有需求时才请求资源
所以对于用户来说,其实是有一点点“等待”的过程的

一般会用loading图,等待文字来改善这里的用户体验

但是有一些需求是希望尽量少出现这种“等待”过程的

这里我们就可以预加载资源,如下,我们先在js中加载图片

<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">var img = new Image();
img.src="test.png";
</pre>
</div>
提前加载好了图片,用户进行下一步时,图片则是从浏览器缓存中获取

多页数据也可以类似处理,页面初始可以默认加载两页数据
翻到第二页时,就ajax去获取第三页内容
翻到第三页时,就去获取第四页内容......
总是提前预加载一页数据

如此可一定程度上减少一些等待的过程

总的来说延迟加载是尽量少加载资源,预加载则是判断可能要的资源,尽量去提前多加载,
都是为了优化用户的体验,适用于不同场景

<strong>资源优化</strong>

网页中有些资源可以通过延迟加载等方式减少不必要的请求
而许多的javascript脚本,css样式等资源却是网页中必须要加载的

既然不可避免需要加载,那接着,我们就可以去优化这些资源

<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>资源压缩(uglify-js,clean-css)</strong><a name="4"></a></div>
首先我们可以优化资源文件的大小,资源文件变小了,传输速度当然也快了
这里我们压缩分为如下两种:

<ul>
<li>HTTP压缩,我们可以在web服务器内设置压缩输出的内容</li>
</ul>

如下,在IIS7内启用Gzip压缩

可以看到,设置了之后资源的HTTP响应多了Content-Encoding:gzip

<ul>
<li>对javascript代码,css样式代码进行语法压缩,减少文件大小</li>
</ul>
我们写js代码,css,都命名尽量取得有意义,多换行,tab把格式弄的优美
都是为了方便维护,再阅读,而机器解析我们的代码的时候可不管这些。

这里压缩我推荐 uglify-js和 clean-css,都在nodejs环境下

<span style="line-height: 1.5;">

</span>

<span style="line-height: 1.5;">这里可见压缩效果还是很显著的,按我目前代码习惯,平均可以压缩一半左右</span>

但是,不可避免,我们在日常开发,发布的时候,会增加额外的工作量

这里我分享一下我的用法:

为了方便开发,我在项目中还是引用的原文件
只是在生成发布文件后,执行下面bat批处理脚本

把发布文件夹内的js代码进行压缩,同名覆盖原文件,这样就不用修改项目内js引用地址

<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">@echo off
:: 设置压缩JS文件的根目录,脚本会自动按树层次查找和压缩所有的JS
SET JSFOLDER=D:\project\scripts
echo 正在查找JS文件
chdir /d %JSFOLDER%
for /r . %%a in (.js) do (
@echo 正在压缩 %%~a ...
uglifyjs %%~fa -mangle -o %%~fa
)
echo 完成!
pause & exit
</pre>
</div>
(
这个bat批处理脚本不是我写的,是以前在网上搜来的)
<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>资源合并</strong><a name="5"></a></div>
我们项目内往往会引用多个javascript脚本,和多个css样式文件
所以可以把多个脚本合并到一个js文件内,然后统一引用它就能减少http请求

这里uglify-js和 clean-css 都支持多个文件合并压缩输出
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">>uglifyjs js1.js js2.js -m -o merge.js
</pre>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">>cleancss -o megar.css style1.css style2.css
</pre>
</div>
也可以在服务器内合并输出,比如我们看淘宝的合并:
<div class="cnblogs_code">
<pre><span style="color: #0000ff;"><</span><span style="color: #800000;">script </span><span style="color: #ff0000;">src</span><span style="color: #0000ff;">="//g.alicdn.com/kissy/k/6.2.4/??node-min.js,node-base-min.js,dom-base-min.js,query-selector-base-min.js,dom-extra-min.js,node-event-min.js,event-dom-base-min.js,event-base-min.js,event-dom-extra-min.js,event-gesture-min.js,event-touch-min.js,node-anim-min.js,anim-transition-min.js,anim-base-min.js,promise-min.js,base-min.js,attribute-min.js,event-custom-min.js,json-base-min.js,event-min.js,io-min.js,io-extra-min.js,io-base-min.js,io-form-min.js,cookie-min.js"</span><span style="color: #0000ff;">></</span><span style="color: #800000;">script</span><span style="color: #0000ff;">></span></pre>
</div>

他们则是在web服务器内做了处理,请求多个文件,会自动合并
有条件的同学也可以这样进行合并
<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>图片合并csssprite</strong><a name="6"></a></div>
图片合并目的也是减少http请求,将页面中需要的相关图片合并到一张大图片里
然后用css的background-position定位到大图的具体局部位置

比如这样:

但是近年来好像用的越来越少了
想想原因,应该是。。。。维护起来[太麻烦]了
只要稍微改动一张图片,尤其是改变了大小的话,整个大图片都要改
并且还要把css样式也改个遍
否则就是继续往大图片里拼,无效的老图保留,导致图片越来越臃肿

再有,如果只是一些这样的小图标的话,用fonticon要方便得多
<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>iconfont</strong><a name="7"></a></div>
iconfont就是图标字体,将图标输出为矢量的字体

使用起来非常方便,对应字体的编码,就和网页中的普通文字一样
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;"><i class="icon iconfont">U</i>
</pre>
</div>
它对比合并的图片来说
体积会更小,并且能用css控制大小,颜色
然后它还是矢量的,放大也不失真

虽然iconfont制作,维护成本也不低,引用起来为兼容浏览器得引用多种格式
但好在现在用的人多,网上也有许多免费图标库供使用

比如这里阿里巴巴图标库

<a href="http://iconfont.cn/" target="_blank">http://iconfont.cn/</a>

<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>引用优化</strong><a name="8"></a></div>
引用css放在<head>内,引用js放在</body>结束标签前,现在很多朋友都会这么做了

css加载是异步的,更早的加载出样式就能更早呈现出页面
js放在尾部,防止浏览器加载js而阻塞页面,造成页面“白屏”现象
<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>单独域名存放资源</strong><a name="9"></a></div>
如果有条件的话,我们还可以启用额外的服务器,域名来存放资源

这样能减少主域名的HTTP请求数,让主服务器更快响应请求
还能减少主域名的cookie请求

<strong>缓存</strong>

说到缓存,首先想到的肯定是服务器后台的缓存
其实,打开我们的浏览器工具看一下,就会发现,网页前台也存在着许多缓存
它无声无息,悄悄优化着每一次访问

<span style="line-height: 1.5;">
</span>
<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>Cache-Control缓存策略</strong><a name="10"></a></div>
直接看响应头,我们会发现这些字段:

<span style="line-height: 1.5;">

</span>

<div class="cnblogs_code">
<pre><span style="color: #000000;">Expires:Thu, 20 Oct 2016 06:43:43 GMT //告诉浏览器此日期以前可以使用缓存文件

Cache-Control:public, max-age=3600 //表示资源在3600毫秒之内可以使用缓存文件,如果和Expires同时存在则覆盖Expires

Last-Modified:Wed, 13 Jul 2015 08:52:12 GMT//配合Cache-Controls使用,标示资源的最后修改日期

ETag:5384183131862232576//配合Cache-Controls使用,标示资源由服务器生成的唯一标识(某些资源最后修改日期不可靠,或者不希望展示最后修改日期,就可以使用ETag)</span></pre>
</div>

上面这些都是控制前端缓存的字段,然后再来看看下面的HTTP状态码

<div class="cnblogs_code">
<pre><span style="color: #000000;">//200 OK 请求已成功,请求所希望的响应头或数据体将随此响应返回。

//304 Not Modified 请求资源没有改变,可以使用缓存资源</span></pre>
</div>

<span style="line-height: 1.5;">所以这里当我们第一次访问的时候,得到响应资源及缓存策略,</span>

之后如果缓存有效,资源就会来自缓存
如果缓存无效了,就会判断是否有Last-Modified或者ETag,有则发送If-Modified-Since或If-None-Match请求头
服务器如果判断资源无修改,就会返回304,有修改就会返回200以及新的缓存策略

当然,这里只是指用户的普通操作,如:地址栏回车,页面链接跳转,新窗口,前进后退
如果用户是进行 刷新 操作,则不会读取缓存资源了,但是如果有Last-Modified或者ETag,依然会发送If-Modified-Since或If-None-Match请求头
如果用户是进行 强制刷新(ctrl+f5) 操作,那所有缓存策略都失效,会重新请求

可见,我们往往只有第一次请求的时候,才会有较多的资源加载
所以我们上面做了这么多优化,延迟加载,资源压缩,合并等等,
都是为了用户第一次访问的时候尽量友好。

那么Cache-Control要如何在服务器设置呢,其实针对静态资源来说
大部分服务器都会默认就将Cache-Control设置好了的,我们可以视情况修改一些时效参数等等

我们这里要注意的是用url去控制缓存的有效性

如下:

虽然它们请求的都是服务器上的同一个js文件,但是浏览器不会把它们当作同一个资源

可以看到v=1.01.js的请求是来源于cache
其他几个请求都是新请求

所以,我们每次发布都可以修改资源url强制让用户页面上的缓存失效
这样用户不需要刷新也能得到最新的资源
<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>离线存储</strong><a name="11"></a></div>
离线存储在我之前一篇文章里也提到过,在移动端应用的比较多
它和缓存不同,它设置好了之后,连离线也能访问,无论用户刷新或者新窗口,链接等等

使用manifest
<html manifest="/mobile.manifest">
在html上添加manifest,其中文件格式内容如:

<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">CACHE MANIFEST

需要离线的内容

CACHE:

Script/jquery.js
Script/gameconfig.js

Image/home.png
Image/logo.png

总是访问网络的内容

NETWORK:

访问A失败时访问B

FALLBACK
</pre>
</div>
浏览器将缓存chache内所有的内容,并且可以离线访问,只要文件发生任何改变都将会重新读取并刷新全部缓存,所以更改注释是个更新缓存的好方法

这里要注意的是
1,添加了manifest的当前网页也会被缓存 所以推荐的方式是页面缓存,页面动态内容全部用ajax获取,所以在移动网站项目设计开始就要注意这个问题
2,页面中添加iframe 然后子页面引用manifest想达到缓存资源而不缓存当前页面内容,是无效的。

<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>本地存储localStorage</strong><a name="12"></a></div>
本地存储数据一直是网页端的弱项,在没有HTML5的localStorage前,用cookie可以保存一点数据
但付出的代价很大,cookie能保存的数据很少,并且它会伴随着每一次请求一起发送

localStorage就好多了,默认5MB的大小,除非用户手动清除,否则一直不过期,就连IE8浏览器都支持

这里要注意,localStorage和cookie一样受到跨域的限制
可以使用domain控制
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">document.domain="";
</pre>
</div>
<strong>其它的</strong>

<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>css3替换js动画</strong><a name="13"></a></div>
在js中,我们实现动画,就是利用定时器循环改变dom元素的属性来达到动画效果
但是许多属性更改之后会造成浏览器重绘,增加性能消耗

当然浏览器更新换代也做了许多优化,我们优化js,css减少重绘,也能改进动画性能
但是想一想,究竟应不应该让js去实现页面动画呢?

css3就是往这方面发展,让js更纯粹的去实现业务逻辑
页面效果之类的事情就让css去做吧

并且css3在动画效率上面也有增强,浏览器会单独处理css3动画,不占用js主线程,还可以硬件加速
将来还有提升的可能,所以快把我们的js动画替换为css3吧!
<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>替换flash</strong><a name="14"></a></div>
同样更迭的还有flash,当初flash是为了弥补网页展现的不足而出现的“插件”
而现在网页标准一次次升级,html5的出现,再加上flash自身也有各种漏洞,性能问题
尤其是现在flash在移动端的支持很少,都加快了我们替换flash的步伐
<div style="padding: 8px; display: inline-block; border-left: solid 3px #ccc; line-height: 20px; color: #327691;"><strong>结语</strong><a name="15"></a></div>
写到这里快写完了,突然发现好像自己在啰啰嗦嗦了一整页。。

却又感觉哪里漏都了一些,好吧,希望大家不要嫌烦,如果都了解了,就当温习吧

如有纰漏,欢迎提醒

哦,还有些想说的

优化一定要选择适合自己项目,硬件条件的优化方式,千万不要盲目硬搬
还有要持续不断的关注新标准,方法,一直保持活力!

以上内容转自 http://www.cnblogs.com/wingkun/p/6014259.html 感谢作者分享

</div>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,451评论 25 707
  • <a name='html'>HTML</a> Doctype作用?标准模式与兼容模式各有什么区别? (1)、<...
    clark124阅读 3,455评论 1 19
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,019评论 4 62
  • 今天是中秋,可惜台风吹走了月亮 (●'◡'●)ノ♥ 。 窗外的树影绰绰,很安静,环绕着的别人的家里似乎都挺热闹,应...
    修女Maria阅读 206评论 0 0
  • 明月长空,玉笺薄凉飘飞雪。人闲小院,忆往昔笑谑。 可恨如今,到底谁难却。江湖夜,收拾残酒,梦幻与昨绝。
    李大柘阅读 179评论 0 2