Web前端性能优化实践
天下武功,唯快不破。
对于网站来说,怎么能给用户最快的响应、让用户有飞一般的体验是摆在每一名开发人员面前的难题。据研究表明,用户访问网站的时间体验尺度是以10为基数变化的,如果网站响应时间在0.1秒或更快,用户会感觉到他们的操作得到了“即时”的响应;如果响应时间超过0.1秒但不到1秒,虽然用户会感觉到短暂的延迟,但这感觉就像网站在给出反馈、与自己进行交互,是合理的时间延迟;如果响应时间超过1秒,用户就需要等待网站的回应,等待的时间越长,用户就越焦躁,大约10秒后,焦躁到极点,如果不是什么至关重要的网站,用户一般会选择放弃并愤怒地关掉网页。崔遥
因此无数开发人员前仆后继、夜以继日就为了能让网站提点速。然而这很难,因为网站性能的优化是一个非常庞大而复杂的课题,从前端到后端,各个层面都有很多技术难题要攻克。
本文将从前端角度,结合《高性能网站建设指南》一书中介绍的14条优化规则,在这里跟大家共同讨论一些前端优化的简单实践。
让我们先来思考并确定优化的方向,在很多书和文章中都提到了最为重要的两点:一是减少HTTP请求数量,二是减少响应内容的大小(即减少请求带宽)。如果能把这两点做好,问题也就解决一大半了。
减少HTTP请求数量
1合并js、css等文件
浏览器同一时间针对同一域名下的请求有数量限制,超过限制数目的请求会被阻塞,不同浏览器限制数目不同,具体见下图。
因此在项目构建时,需要将js、css等文件按需进行合并,从而减少http请求数。在项目中,我们采用了RequreJS模块化工具,异步加载js模块,RequreJS官方配套的优化构建工具为r.js ,用于合并压缩脚本。
假设有两个页面Page1和Page2,Page1依赖模块A,B两个模块,Page2依赖B,C,D三个模块,而模块A和C又依赖模块E。
为r.js创建一个配置文件,进行一些最基本的配置。
r.js会自动追溯所有该模块的依赖模块,将其放进入口文件中。以上述配置为例,打出的dist包中,Page1.js中会包含模块A,B,E,Page2.js中会包含模块B,C,D,E。
我们还可以按照其他方式组织模块。
以上述配置为例,打出的dist包中会多出一个Common.js文件,包含A,B,C,D,E五个模块,而Page1.js和Page2.js中则不会包含任何依赖模块。
上面两种打包方法各有利弊,第一种方法:每个页面只需请求一个js文件即可加载出所有模块,但由于各个页面可能会依赖相同的模块,同一模块会被重复打入多个文件中,增加了总体dist包的大小,而且无法利用到浏览器的缓存。第二种方法:没有重复的模块,但第一次加载页面时需要加载两个js文件,虽然有些模块是第一个页面用不到的;但这种方式可以利用浏览器的缓存,将公共依赖文件Common.js缓存下来。
现在比较火的模块加载兼打包工具是webpack,功能更为强大,笔者也在学习中。
2将图片制作成字体图标
网页上有很多零碎的图片,将它们整合到一起,可以极大减少图片的请求数。
字体图标(Icon Font),就是把图标做成字体,具有浏览器兼容性好、任意放大缩小、使用方便等特点。网上有很多漂亮的字体图标库,比如
Font Awesome (http://fontawesome.io/)
Foundation Icon Fonts (http://zurb.com/playground/foundation-icons)
MFG Labs Web Icon Set (http://mfglabs.github.io/mfglabs-iconset/)
IcoMoon (https://icomoon.io)
当然也可以定制自己的字体图标,在这里推荐IcoMoon网站,可以在线制作字体图标。具体方法为:首先通过AI或者PS制作矢量图标,输出成SVG格式。然后访问https://icomoon.io/app/#/select ,导入SVG格式的图标,即可生成对应的字体图标,包括style.css,eot压缩字库,整合后的svg图标,ttf字体,woff字体格式。
减少响应内容大小
1压缩js、css文件和图片
压缩是去除代码中多余的空行和注释,减小文件大小。还可以配合代码混淆,将代码中的变量、函数、类的名字改写成无意义的名字,在保证功能的情况下,进一步精简。
我们采用Gulp作为打包构建工具,Gulp采用流式构建模式,没有中间文件生成,数据的读取和操作更快。Gulp有非常丰富的插件库,可以支持复杂的构建逻辑。下面以Gulp为例,配置各类文件压缩混淆任务。
开启服务器压缩
Nginx、Apache、IIS等Web服务器都支持Gzip压缩功能,可以将网页内容压缩后再传输到客户端,压缩率可达50%以上。以Nginx为例,可以进行如下配置:
gzip on;
#开启,默认为off
gzip_min_length 10k;
#小于此值的数据包不会被压缩
gzip_buffers 16 8k;
#指定gzip压缩文件时向系统申请的缓存大小为16*8K
gzip_comp_level 2;
#gzip压缩级别,从1到9,级别越高压缩率越高但CPU占用率越高。
#实测发现,级别为2与9压缩后的文件大小基本相差无几,但CPU利用率相差却很大。
gzip_types text/plainapplication/javascript text/javascript application/x-javascriptapplication/json text/css text/xml application/xml;
#只有类型匹配的文件才会被压缩
gzip_vary on;
#在响应头中添加Vary: Accept-Encoding,告知代理服务器(如CDN服务器)客户端数据支持何种压缩方式
gzip_disable"MSIE[1-6]\.";
#IE6不支持压缩,不进行压缩
3采用CDN
CDN全称为Content Delivery Network或者Content Distribute Network,即内容分发网络或内容交付网络,通过将服务内容分发至全网加速节点,用户访问站点时能够从就近节点获取所需内容,有效降低访问延迟,提升服务可用性。关于CDN的介绍网上有很多资料,在这里就不做搬运工了。
如果想用CDN,前端需要在打包构建时将html、css文件中引用静态资源的路径从相对路径修改为CDN域名下的绝对路径。
假如CDN域名为https://static.test.cn,那么在打包构建时,我们需要将上述文件中的相对路径修改为CDN域名下的绝对路径。使用Gulp 的cdnify插件,进行如下配置。
输出结果为:
参考资料:
https://www.nngroup.com/articles/powers-of-10-time-scales-in-ux/
https://developer.yahoo.com/performance/rules.html
http://www.stevesouders.com/blog/2008/03/20/roundup-on-parallel-connections/