浅谈前端性能优化

    在第一家公司做前端的时候,领导跟萌新的我讲前端开发最重要的事情就是搞定页面的性能优化以及表格控件。对于表格控件,当初用的是bootstrap的dataTable,因业务需要当初也是没日没夜的翻源码改源码。

    不过今天我要跟大家聊的并不是bootstrap的dataTable,而是前端性能优化。工作几年对性能优化这块也有了一些浅薄的认知,现在就开始我们今天的主题:浅谈前端性能优化。

一:减少对服务器的http请求

    常规的http请求属于“请求-响应-断开”形式的短连接,项目中每一个独立的资源我们都会向服务器发送一个get请求,然后等待服务器将资源传回我们。这里的每一次“请求-响应-断开"都会消耗一定的时间,如果能尽可能的减少http请求数量,意味着对于自己,减轻了服务器的负担,对于用户,我们加快了页面加载时间,提高了用户体验。

如何减少http请求?我们有如下办法:

    1、使用css sprite(雪碧图)技术合并多个图片为单个图片文件,使用时通过background-position来定位背景位置。

这种方法一般都是对一些页面中使用的小图片进行处理,不过随着base64编码展示图片以及字体图标的广泛使用,很多同行或一些文档中都会发表一些雪碧图已过时的观点,在此我不做评价。适合自己的就是最好的。

    2、合并多个css样式文件为单个样式文件,合并多个脚本为单个脚本,再在页面中引用合并后的样式/脚本文件。

这种思路跟css sprite的思路是一致的,目的都是将原先的多个http请求变成一个http请求。具体的实现方法可以使用现在流行的构建工具webpack、gulp等,我们甚至可以通过构建工具将css合并到js文件中。需要注意的是如果将样式/脚本全部进行合并,那么多个页面公共的头部或底部等公共样式/脚本就无法在客户端进行缓存了,自己得权衡利弊,做出相应改变。

    3、使用base64编码展示图片

在网上可以找到各种工具将图片转成base64编码的字符串,也可以通过构建工具帮你自动转换。唯一的缺点就是它们虽然无须从服务端get一遍,但也无法缓存在客户端,导致用户每次访问页面都要重新渲染一次。而且冗长的base64编码串也会占用你页面很大的代码空间,代码维护起来也挺扎心。只推荐你把这种方式使用在用户重复访问量较少的页面。

    4、把小段的、复用率低的样式/脚本直接写于页面上。

我们不需要把每段样式/脚本都放在外部文件里。像小段的、复用率低的直接写在html页面即可,减少引用外部文件的http请求。这是在之前看到的一篇博文中发现的方法,博主提出将这类的样式/脚本写在html之后再配合gzip压缩会是很好的选择。看到这里我有一些疑问,压缩css与js我们是知道的,但很少见过有把html压缩的。抱着谨慎的态度我去搜索了一波,还是发现了一些不错的观点,这里跟大家分享一下。

CSS 和 JavaScript 的压缩已经很成熟,各大网站都在使用。HTML 的压缩(特指去除空白字符和注释),除了 Google 等搜索页面,在其它网页上基本见不到踪迹。

不压缩html的原因很简单: 
HTML 文档中,多个空白字符等价为一个空白字符。也就是说换行等空白字符的删除是不安全的,有可能导致部分元素的样式产生差异。 
HTML 元素中,有一个pre, 表示 preformatted text. 里面的任何空白,都不能被删除。 
HTML 中有可能有 IE 条件注释。这些条件注释是文档逻辑的一部分,不能被删除。 
对于动态页面来说,HTML 的压缩有可能还会增加服务器的 CPU 负担,得不偿失。 

    5、给资源文件添加一个标识符后缀,我们可以通过构建工具给资源文件添加一个哈希码后缀。在这些资源文件未被更新的情况下,客户端会直接读取缓存中的文件,只有当服务器中资源文件的哈希码后缀跟客户端缓存中的后缀不一致时,客户端才会向服务器发出请求重新下载该文件。

二、减小文件大小

    影响页面性能的因素除了http请求的数量,还跟请求的资源文件大小有关。一个最直观的例子:下载10M跟下载10k的图片速度能一样吗?虽然有些夸张,但尽可能减少请求文件的大小便是相当重要的事情了,我们可以做的事情有:

1、压缩样式/脚本文件,我们同样可以使用webpack或gulp等构建工具来实现这点,它们均能很好地减少css/js文件的大小;

2、针对性选择图片格式,在无透明背景需求下,对于颜色较单一、无色彩渐变的图片仅使用gif格式,对于jpg图片也可按照其清晰度要求,在导出jpg的时候选择对应的“品质”进行优。或者在网上找一些工具对图片进行相应的压缩,适当的减少它的体积。

3、使用字体ui图标来代替一些小图片。一般前端框架像bootstrap、amazeUi、mui等都有提供字体图标库,如果框架提供的图标无法满足项目需求,请移步阿里巴巴矢量图标库。

三、使用CDN

使用CDN有几个好处:

1、如果用户在其它站点下载过这个CDN资源,那么来我们站点仅仅从客户端缓存获取即可;减少了对自己站点服务器的文件请求(外部CDN的情况下),减少服务器负担;

2、多个域会使浏览器允许异步下载资源的最大数量增多,比如一个站点只从一个域来请求资源,那么FireFox只允许同时刻最多异步下载2个文件,但如果使用了外部CDN来引入资源,那么FF允许在同时异步下载本域中的两个资源外,还额外允许同时异步下载另一个域(CDN)下的2个资源。

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

dns解析:人们习惯记忆域名,但机器间互相只认IP地址,域名与IP地址之间是多对一的关系,一个ip地址不一定只对应一个域名,且一个域名只可以对应一个ip地址,它们之间的转换工作称为域名解析,域名解析需要由专门的域名解析服务器来完成,整个过程是自动进行的。

对于这个问题,常规是建议一个站点下只使用同一个可靠、快速的CDN来引入各种所需资源即可,也就是说,建议一个页面从2个不同的域(比如站点域和CDN域)请求资源是最佳的选择。

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

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

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

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

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

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

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

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

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

举个栗子,有一个页面是这样的:左边是侧边栏,用于存放用户的头像啊、资料啊,以及网站投放的广告啊,而右侧是文章内容区域,那么我们的代码结构很可能是这样的:

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

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

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

七. 其它建议

1. 不要在css中使用@import,它会让一个样式文件去等待另一个样式文件的请求,无形中增加了页面等待时间;

2. 避免页面或者页面文件重定向查找;

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

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

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

6. 当浏览器向服务器请求一个静态资源时,会先发送同域名下的 cookie,服务器对于这些 cookie 不会做任何处理。因此它们只是在毫无意义的消耗带宽。所以你应该确保对于静态内容的请求是无coockie的请求。所以要用单独的域名去访问图片资源,以减少cookie传输,提高网页性能。

这次的性能优化暂时聊到这里,这篇文章引用了一些博主的博文,也包含了一些我的个人想法。我想前端性能优化应该远远不止如此,若有其他的思路,请告知小弟一二,不胜感谢。

猜你喜欢

转载自my.oschina.net/cc4zj/blog/1626028