记得面试现在这份工作的时候,一位领导语重心长地谈道——当今的世界是互联网的世界,IT企业之间的竞争是很激烈的,如果一个网页的加载和显示速度,相比别人的站点页面有那么0.1秒的提升,那也是很大的一个成就。

然后我不知道怎么写下去了,就在群里问了那群狗头军师,结果是这样的。。。

好的,是时候“语锋一转”切回主题了 —— 如何提升我们站点页面的访问速度、减少等待时间,从而最大化地提升用户访问体验呢?

针对这个问题,我们今天会从前端的角度来提出系列解决方案,它们都能有效地提升你页面的访问速度。

一. 减少对服务器的文件请求

常规的HTTP请求属于“请求”-“应答”-“断开”形式的短连接,每一个独立的资源我们都会向服务器发去一份get请求,再等服务端将我们需要的文件传回来。每一次资源的请求都实实在在地耗费了一次“连接-等待-接收”的时间(当然将http请求设为keep-alive长连接状态可以减少“连接”的次数和时间),如果我们能有效减少对服务器文件的请求次数,便意味着我们可以从这块省下一些页面等待时间,也可以顺便减少服务器的负担。

对于这个解决方案,我们可以这么做:

1. 使用css sprite技术合并多个图片为单个图片文件,实际使用时通过background-position来定位背景位置(相信大家第一个想到的也是这个吧);

2. 合并多个css样式文件为单个样式文件,合并多个脚本为单个脚本,再在页面中引用合并后的样式/脚本文件。对于这个你可以使用r.js来帮忙(看我这篇文章),但我个人倒是不怎么推荐这个方法,因为合并了文件之后,多个页面之间公共部分的样式/脚本文件就无法缓存到客户端了;

3. 使用base64编码来展示图片。就如图github 404页面那样:

你可以使用这个工具来帮你把图片转换为base64编码的文件流,但常规只推荐你把这种方式使用在用户重复访问量较少的页面,因为它们虽然无须从服务端get一遍,但也无法缓存在客户端,导致用户每次访问页面都要重新渲染一次。而且冗长的文件流代码会占用你页面很大的代码空间,维护起页面来估计也会挺心塞;

4. 将小块的css、js代码段直接写在页面上,而非在页面引入独立的样式/脚本文件。相信有的朋友看惯了“保持结构 (标记)、表现 (样式)、行为 (脚本)三者分离”的规范,对此观点可能有些意见。只能说规范不是教条,适合自己的才是硬道理。直接把小段的、复用率低的样式/脚本直接写于页面上带来的利还是大于弊的(弊可能也就是增大了页面代码量、不那么好维护了点)。反观所有主流门户网站的页面源文件,基本没有一个是把样式/脚本都全部作为外部文件引入的(无论他们是否从减少服务器请求这点出发,事实都是这样),配合gzip压缩会是很好的选择;

5. 利用http-equiv="expires"元标签,设定一个未来的某时间点作为页面文件过期时间,用户在过期时间之前所获取到的页面文件都仅从缓存中去取。最好的形式是给资源名称加md5之类的唯一标识符后缀,然后设置一段较长的过期时间,同时请后端配置好ETag。这块的内容可以参考这篇文章

二. 减少文件大小

文件太大(特别是图片)导致加载时间较长,往往都是影响页面加载体验的头号大敌,那么尽可能减少请求文件的大小便是相当重要的事情了,我们可以做的事情有:

1. 压缩样式/脚本文件,就此你可以使用gulp或者grunt来实现这点,它们均能很好地减少css/js文件的大小(对于js还能起到混淆变量、函数名的作用);

2. 针对性选择图片格式,在无透明背景需求下,对于颜色较单一、无色彩渐变的图片仅使用gif格式,对于jpg图片也可按照其清晰度要求,在导出jpg的时候选择对应的“品质”进行优化:

另外,你可以使用TinyPNG或者腾讯智图这样的工具来压缩图片(极力推荐)

如果你喜欢尝鲜,可以学淘宝那样使用webp图片格式,它能很好地优化同画质下的文件大小:

如果你对webp感兴趣,可以查阅这里

3. 使用Font Awesome来替代页面上的图标,其原理是使用@font-face让用户下载一个非常小的UI字体包,把页面上用到的图标以字符的形式来显示,从而减少了图片需求和图标文件大小。

三. 适度使用CDN

使用CDN有几个好处:如果用户在其它站点下载过这个CDN资源,那么来我们站点仅仅从缓存获取即可;减少了对自己站点服务器的文件请求(外部CDN的情况下),减少服务器负担;多个域会使浏览器允许异步下载资源的最大数量增多,比如一个站点只从一个域来请求资源,那么FireFox只允许同时刻最多异步下载2个文件,但如果使用了外部CDN来引入资源,那么FF允许在同时异步下载本域中的两个资源外,还额外允许同时异步下载另一个域(CDN)下的2个资源。

但是使用CDN有一个很大的问题——增加了dns解析的开销,如果一个页面同时引入了多个CDN的资源,可能会因为dns解析而陷入较多的等待时间,导致得不偿失。

对于这个问题,常规是建议一个站点下只使用同一个可靠、快速的CDN来引入各种所需资源即可,也就是说,建议一个页面从2个不同的域(比如站点域和CDN域)下来请求资源是最佳的选择(据说这个结论是雅虎前端工程师提出的,这里有一篇很不错的文章)。

四. 延迟请求、异步加载脚本

在各主流浏览器下,常规情况,我们的脚本文件跟随其它资源文件一样都是异步下载的,但这里存在一个问题——比如FireFox下载好脚本后的一小段时间内会有“执行阻塞”的情况发生,也就是说浏览器下载好脚本后执行它的这段时间里,浏览器的其它行为被阻塞,导致页面上的其它资源都是无法被请求和下载的:

如果你页面里存在js代码执行时间过长的情况,那么用户就会明显感觉到页面的延迟。解决这个问题有一个简单的方法——将脚本请求标签放置到</body>结束标签前,使得页面上的脚本成为最后被请求的资源,自然也不会阻塞其它页面资源的请求事件了。

另外,虽然上面提到“我们的脚本文件跟随其它资源文件一样都是异步下载的”,但异步下载不代表异步执行,为了严格保证脚本逻辑顺序和依赖关系的正确性,浏览器会按照脚本被请求的先后顺序来执行脚本。那么问题就来了——如果页面上的脚本依赖关系并不大,甚至没有任何相互间的依赖,那么浏览器的这套规则就仅仅增加了页面请求阻塞时间而已(就像你花大钱买了一笔保险,但被保险期间你平安无事啥都没发生。。。嗯,这个比喻有点反人类。。。)。

解决这个问题的办法无非就是让脚本无阻塞地异步执行,比如给script标签加上defer和async属性或者动态注入脚本(可以参考这里),但这些都不是良好的解决方案,要么存在兼容性问题,要么太麻烦还无法处理依赖。

个人是推荐使用 requireJS(AMD规范) 或 seaJS(CMD规范) 来异步加载脚本并处理模块依赖的,前者将“依赖前置”(预加载所有被依赖脚本模块,执行速度最快),后者走的“依赖就近”(懒加载被依赖脚本模块,请求脚本更科学),你可以根据项目具体需求来选择最合适的。

五. 延迟请求首屏外的文件

先解释下,“首屏”指的是页面初始化时候的页面内容显示区域,也就是页面一加载,用户就首先看到的区域。

比如像京东啊淘宝啊,对于需要滚动页面才能看到的图片内容,都做了类似lazyload的处理,这些无非都是走了代理模式的理念,但的确给用户一个错觉——这个页面更快地加载完了,因为我很快就看到了屏幕上的内容(即使我还没下拉滚动条,而页面后方的文件其实还没真正加载呢)。

我们可以这样实现此方案,不依赖任何lazyload库,拿图片来做示范,我们可以这样编写首屏外的图片(假设某张图片地址是a.jpg)的img标签:

<img src="data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" data-src="a.jpg">

如上所示,页面初步加载这张图片的时候是直接以base64的方式(当然你也可以统一使用一张占位图loading.gif来替代)来快速显示一张极小的图片的,而图片本身的真实路径是存在data-src属性内的,我们可以在页面加载结束后再向服务器请求它真实的文件并替换:

function init() {
var imgDefer = document.getElementsByTagName('img');
for (var i=0; i<imgDefer.length; i++) {
if(imgDefer[i].getAttribute('data-src')) {
imgDefer[i].setAttribute('src',imgDefer[i].getAttribute('data-src'));
}
}
}
window.onload = init;

如上是对图片的延迟加载处理,对于视频、音频文件,可以采取完全一样的原理来延迟加载,从而有效减少页面初始化等待时间。

六. 优化页面模块排放顺序

这里有一个很好的例子,比如有一个页面是这样的——左边是侧边栏,用于存放用户的头像啊、资料啊,以及网站投放的广告啊,而右侧是文章内容区域:

那么我们的代码很可能是这样的:

<body>
<sidebar>
<!-- 侧边栏内容 -->
</sidebar> <content>
<!-- 文章内容 -->
</content>
</body>

于是乎,浏览器按照它的UI单线程准则从上到下先加载了侧边栏,再加载我们的文章。。。

很明显,这样不是一个人性化的加载顺序,我们得弄清楚,页面上各个区域模块,对于用户而言,哪个才是最重要、最应当首先展示的

对于上面的例子,文章内容才应该是用户首先要看到、需要浏览器优先请求和显示的区域。所以我们得修改我们的代码为:

<body>
<content>
<!-- 文章内容 -->
</content> <sidebar>
<!-- 侧边栏内容 -->
</sidebar>
</body>

当然这里仅仅是用一个小示例来挑起各位的脑洞,懂得举一反三和实际运用才是硬道理。

七. 其它建议

1. 不要在css中使用@import,它会让一个样式文件去等待另一个样式文件的请求,无形中增加了页面等待时间(当然如果走的scss,@import就是另一回事了,呃跑题了~);

2. 避免页面或者页面文件重定向查找,这相当于你走进了一间卫生间,然后看到上面的牌子说“此处不同,请去前面左拐的卫生间”,又得重走一遍;

3. 减少无效请求——比如通过css/js来请求一个不存在的资源,可能会导致较长的等待和阻塞(直到它返回错误信息);

4. 无论你是否决定将脚本放到页尾,但一定要保障脚本放置于样式文件后方;

5. 文件在小于50K的时候,直接读取文件流会比从文件系统中去读取文件来的快些,大于50K则相反。比如有一张图片,如果它小于50K,我们可以将它转为二进制数据存储在数据库中,页面若要读取该图片则从数据库上来读取,若文件大小大于50K,那建议存放在可访问的文件夹中以文件的形式来读取即可;

6. 使用 cookie-free domains 来存放资源,减少无用cookie传输的网络开销(可以参考这篇文章);

7. 不重要的样式文件可以走无阻塞渲染的加载形式,可参考这篇文章

8. 配置.htaccess文件、走Gzip页面压缩形式、开启keep-alive连接模式等后端解决方案,这边就不细说了。

相信页面提速的方案绝对不仅仅局限于上面提到的这些,而且每个项目都有着各种需求和情况,只能按需选择适合自己的方案。

但最重要的还是——尽量把用户的体验放在第一位,无论是页面的加载或者交互,都应当多从用户角度切入去思考和设计最优选的方案,这样相信你一定能做出出色、受欢迎的作品。

今天聊的就是这些,共勉~

java web 页面提速的更多相关文章

  1. paip.powerdesign cdm pdm文件 代码生成器 java web 页面 实现

    paip.powerdesign cdm pdm文件 代码生成器 java web 页面 实现 准备从pd cdm生成java web 页面...但是,ms无直接地生成软件.... 只好自己解析cdm ...

  2. Java Web页面跳转

    Java Web的页面跳转分服务器跳转和客户端跳转: 服务器端跳转  的特点是:跳转之后浏览器的地址栏不会发生任何变化,在使用rquest属性范围时,能将request属性保存到跳转页.执行到跳转语句 ...

  3. java web页面跳转 总结

    Servlet中forward和redirect的区别 forward方式:request.getRequestDispatcher("/somePage.jsp").forwar ...

  4. 浅谈WEB页面提速(前端向)

    记得面试现在这份工作的时候,一位领导语重心长地谈道——当今的世界是互联网的世界,IT企业之间的竞争是很激烈的,如果一个网页的加载和显示速度,相比别人的站点页面有那么0.1秒的提升,那也是很大的一个成就 ...

  5. 高性能Java Web 页面静态化技术(原创)

    package com.yancms.util; import java.io.*; import org.apache.commons.httpclient.*; import org.apache ...

  6. 高性能Java Web 页面静态化技术

    package com.yancms.util; import java.io.*; import org.apache.commons.httpclient.*; import org.apache ...

  7. java web页面 base

    <base href="<%=basePath%>"> <base src="<%=basePath%>"> 主 ...

  8. Tomcat设置默认启动项目及Java Web工程设置默认启动页面

    Tomcat设置默认启动项目 Tomcat设置默认启动项目,顾名思义,就是让可以在浏览器的地址栏中输入ip:8080,就能访问到我们的项目.具体操作如下: 1.打开tomcat的安装根目录,找到Tom ...

  9. Java用webSocket实现tomcat的日志实时输出到web页面

    原文:http://blog.csdn.net/smile326/article/details/52218264 1.场景需求 后台攻城狮和前端攻城狮一起开发时,经常受到前端攻城狮的骚扰,动不动就来 ...

随机推荐

  1. ASUS主板ALC887声卡,RTL81XX网卡,黑苹果驱动安装

    折腾了一下午终于在黑苹果上成功的安装了网卡,声卡驱动: 我的配置: 主板 ASUS b75m-a 声卡 ALC887 网卡 RTL8168F 安装所需工具: MutiBest 下载OS对应的版本即可  ...

  2. Android双向seekbar

    ※效果 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/disso ...

  3. 分析USB平台设备模型框架(1)

    start_kernel rest_init(); kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); do_basic_setup ...

  4. 彻底删除Oracle 11g的方法

    1.关闭oracle所有的服务. 可以在windows的服务管理器中关闭. 2.打开注册表:regedit 打开路径:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlS ...

  5. Layui 弹出层组件——layer的模块化开发实例应用

    Layui 弹出层组件——layer的模块化开发实例应用 1.首先在package.json中引入layer组件依赖 2.在源码中应用这个依赖 3.在源码中编写代码应用此组件 4.效果验证:点击日历上 ...

  6. asp.net 抓取新闻

    前台页面: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w ...

  7. 【转】打造自己的视频会议系统 GGMeeting(附送源码)

    原文地址:http://www.cnblogs.com/justnow/p/4487201.html 自从在博客园发布开源即时通信系统GG(QQ高仿版)以来,结识了很多做IM的朋友,然后我和我的伙伴们 ...

  8. unity3d移动及键鼠状态

    gameObject的transform属性可以进行位置.旋转.大小的设置 位置:position,Translate(),位置的移动 旋转:rotate 大小:localScale Vector3内 ...

  9. etymology-F

    forsake [fə'seɪk] vt.放弃:断念. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400 ...

  10. Top 10 Project Management Software