盒子
盒子
文章目录
  1. 性能优化核心关注点
  2. 图片优化
    1. CSS代替图片
    2. 使用矢量图替代位图
    3. 使用data url
    4. 按照HTTP协议设置合理的缓存
    5. 资源的lazyload或postpone
    6. 资源的prefetch
    7. responsive设计
    8. 使用恰当的图片格式
    9. 使用“渐进”图片或“交错”图片
  3. CSS优化
    1. CSS 优化主要是四个方面:
    2. Css Lint提到的规则
    3. CSS方法论
      1. 总结的写CSS代码的一些关键点。  
  4. JS优化
    1. 针对DOM问题,Javascript的应对方案
  5. 前端综合
    1. 使用tree-shaking以及code-splitting机制来减轻负载(Use tree-shaking and code-splitting to reduce payloads)
    2. 优化网络环境能够加快网络传输的速度(Warm up the connection to speed up delivery)
    3. 用service worker来处理缓存问题或者将service worker作为网络(出现问题时)的应急方案,合适吗?(Are service workers used for caching and network fallbacks?)
    4. HTML5的离线储存怎么使用,工作原理能不能解释一下?

前端性能优化

前端性能优化笔记

性能优化核心关注点

对普通的网站有一个统一的思路,就是尽量向前端优化、减少数据库操作、减少磁盘IO
向前端优化指的是,在不影响功能和体验的情况下,能在浏览器执行的不要在服务端执行,能在缓存服务器上直接返回的不要到应用服务器,程序能直接取得的结果不要到外部取得,本机内能取得的数据不要到远程取,内存能取到的不要到磁盘取,缓存中有的不要去数据库查询。
减少数据库操作指减少更新次数、缓存结果减少查询次数、将数据库执行的操作尽可能的让你的程序完成(例如join查询),减少磁盘IO指尽量不使用文件系统作为缓存、减少读写文件次数等。程序优化永远要优化慢的部分,换语言是无法“优化”的。

图片优化

CSS代替图片

直接使用CSS替代图片来实现修饰效果吧!如半透明、边框、圆角、阴影、渐变等,在当前主流浏览器中都可以用CSS达成。将来CSS滤镜得到广泛支持后,还可以做到alpha混合、正片叠底等各种效果。

使用矢量图替代位图

对于绝大多数图案、图标等,矢量图更小,且可缩放而无需生成多套图。现在主流浏览器都支持SVG了,所以可放心使用!

使用data url

资源内嵌于CSS或HTML中,而不必单独请求。注意,多个地方都要使用的资源不一定适合用此优化方式,因为图片数据重复多了,增加流量。另外许多浏览器对data url有长度限制,注意资源的大小。

按照HTTP协议设置合理的缓存

具体的缓存策略(如永久缓存+重命名)、部署策略(如反向代理、CDN等)这里就不展开了。

资源的lazyload或postpone

(lazyload:延迟到其他资源下载完成后再加载,postpone:延迟到元素可见再加载。)目前基本上都要用脚本控制。未来HTML和CSS会增加相关的控制属性

资源的prefetch

可用,见http://www.whatwg.org/specs/web-apps/current-work/#link-type-prefetch。注意prefetch只是hint,Firefox会预取资源(如果网络空闲的话),而IE 9则是对该资源的hostname进行DNS预解析。如果你真的需要更强的控制,则得用脚本。注意:Chrome支持与prefetch相近但更进一步的,另外SPDY加入了与prefetch相近但语义不同的subresource link支持,这两个新特性我也没用过,有兴趣的可以尝试。

图片的其他优化技巧如字体图标、CSS Sprites等,不过我不推荐。用字体图标不如用SVG。使用了SPDY和data url后,CSS Sprites完全没有必要用了。

responsive设计

可能要产生多套不同大小和分辨率的图片,配合media query、以及srcset属性、picture元素、src-N等标准提案。

使用恰当的图片格式

我们常见的图片格式有JPEG、GIF、PNG。基本上,内容图片多为照片之类的,适用于JPEG。而修饰图片通常更适合用无损压缩的PNG。而GIF基本上除了GIF动画外不要使用。且动画的话,也更建议用video元素和视频格式,或用SVG动画取代。除了这些格式之外,Chrome、新版Opera、Android 4+支持WebP格式,IE 9+、IE mobile 10+支持JPEG XR。这两个新格式都支持无损和有损压缩,都具有更良好的压缩比。当然这需要为不同的浏览器返回不同的图片,增加了开发成本,也增加存储成本。不过你省了流量或者相同流量下改善了图片质量,提升了用户体验。你会如何取舍呢?对了,别忘了使用优秀的图片编码器及合适的参数。好的图片编码器,尤其是有损图片格式的编码器,能通过算法或手动调整,获得更高的压缩比。

作者:贺师俊
链接:https://www.zhihu.com/question/21815101/answer/19410993
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

使用“渐进”图片或“交错”图片

JPEG、GIF和PNG这三种图像格式都提供了一种功能,让图像能够更快地显示。图像可以以一种特殊方式存储,显示时先大概显示图像的草图,当文件全部下载后再填充细节(百度图片,QQ空间,点点网等显示大图时都使用的这种方式)。这起到一种很有意义的心理效果,因为这样使人们有东西可看,而不必坐着干等大型图像慢慢显示在屏幕上。但这种效果并不是所有浏览器都支持。

在测试各个浏览器(未说明都为最新版本)时发现:

  1、Chrome对“渐进”和“交错”都支持良好

  2、Safari(PC/MAC)对“渐进”不支持,“交错支持良好”

  3、Fiefox对“渐进”和“交错”都支持良好

  4、Opera对“渐进”和“交错”都支持良好

  5、IE9对“渐进”和“交错”都不支持

图一:基本显示效果演示

图二:渐进\交错显示效果演示

上图一:

  图一中我们可以发现图片是从上倒下一条一条逐渐加载的,显示效果如图一的这种JPG压缩模式叫做顺序式编码(Sequential Encoding),一次将图像由左到右、由上到下顺序处理。也是一种常见的JPG编码模式。

上图二:

  图二中我们可以发现同样是一样图片,因为图片较大图,要逐渐加载完我们才知道这张图片的大致轮廓,但是在图二上,由于使用了渐进式JPG格式,在图片加载的时候我们已经可以看到了图片的大致轮廓,这种就是渐进式JPG,使用了递增式编码(Progressive Encoding)。

所以

  所谓的渐进式JPG格式就是采用了递增式编码的JPG,你可以通过谷歌搜索关键字JPG Progressive Encoding查的更多英文的资料,因为我发现国内这方面的介绍不是很多。

这种JPG格式是当图像传输的时间较长时,可将图像分数次处理,以从模糊到清晰的方式来传送图像(效果类似GIF在网络上的传输)。

二、渐进式JPEG创建

  1. PHP转换
    2.photoshop中有个“存储为web所用格式”,连续勾选就是渐进式JPEG图片

CSS优化

CSS 优化主要是四个方面:

加载性能
这个方面相关的 best practice 太多了,网上随便找一找就是一堆资料,比如不要用 import 、多用继承熟悉啊,压缩啊等等,主要是从减少文件体积、减少阻塞加载、提高并发方面入手的,任何 hint 都逃不出这几个大方向。
选择器性能
可以参考 GitHub 的这个分享 https://speakerdeck.com/jonrohan/githubs-css-performance,但 selector 的对整体性能的影响可以忽略不计了,selector 的考察更多是规范化和可维护性、健壮性方面,很少有人在实际工作当中会把选择器性能作为重点关注对象的,但也像 GitHub 这个分享里面说的一样——知道总比不知道好。
渲染性能
渲染性能是 CSS 优化最重要的关注对象。页面渲染 junky 过多?看看是不是大量使用了 text-shadow?是不是开了字体抗锯齿?CSS 动画怎么实现的?合理利用 GPU 加速了吗?什么你用了 Flexible Box Model?有没有测试换个 layout 策略对 render performance 的影响?这个方面搜索一下 CSS render performance 或者 CSS animation performance 也会有一堆一堆的资料可供参考。
可维护性、健壮性
命名合理吗?结构层次设计是否足够健壮?对样式进行抽象复用了吗?优雅的 CSS 不仅仅会影响后期的维护成本,也会对加载性能等方面产生影响。这方面可以多找一些 OOCSS(不是说就要用 OOCSS,而是说多了解一下)等等不同 CSS Strategy 的信息,取长补短。

Css Lint提到的规则

修复解析错误(Parsing errors should be fixed)
避免使用多类选择符(Don’t use adjoining classes)
IE6以及更古老的浏览器对类似.foo.bar的多类选择符解析不正确,参考IE6下的多类选择符一文。

移除空的css规则(Remove empty rules)
这个规则不包含任何属性,类似:.foo { }空规则的产生原因一般来说是为了预留样式。去除这些空规则无疑能减少css文档体积。

正确使用display的属性(Use correct properties for a display)
由于display的作用,某些样式组合会无效,徒增样式体积的同时也影响解析性能。CSS Lint会检查一下几点:
display:inline后不应该再使用width、height、margin、padding以及float。
display:inline-block后不应该再使用float。
display:block后不应该再使用vertical-align。
display:table-*后不应该再使用margin或者float。

不滥用浮动(Don’t use too many floats)
虽然浮动不可避免,但不可否认很多css bug是由于浮动而引起。CSS Lint一旦检测出样式文件中有超过10次的浮动便会提示警告。

不滥用web字体(Don’t use too many web fonts)
对于中文网站来说Web Fonts可能很陌生,国外却很流行。web fonts通常体积庞大,而且一些浏览器在下载web fonts时会阻塞页面渲染损伤性能。

不声明过多的font-size(Don’t use too may font-size declarations)
这是设计层面的问题,设计精良的页面不会有过多的font-size声明。

不在选择符中使用ID标识符(Don’t use IDs in selectors)
主要考虑到样式重用性以及与页面的耦合性。

不给h1~h6元素定义过多的样式(Don’t qualify headings)
全站统一定义一遍heading元素即可,若需额外定制样式,可使用其他选择符作为代替。

不重复定义h1~h6元素(Heading styles should only be defined once)

值为0时不需要任何单位(Zero values don’t need units)

标准化各种浏览器前缀(Vendor prefixed properties should also have the standard)
通常将浏览器前缀置于前面,将标准样式属性置于最后,类似:
.foo {-moz-border-radius: 5px;border-radius: 5px; }

使用CSS渐变等高级特性,需指定所有浏览器的前缀(CSS gradients require all browser prefixes)

避免让选择符看起来像正则表达式(Avoid selectors that look like regular expressions)
CSS3添加了一些类似~=等复杂属性,也不是所有浏览器都支持,需谨慎使用。

遵守盒模型规则(Beware of broken box models)

CSS方法论

什么是CSS方法论呢?简单地说就是一些同行为了提高CSS可维护性、提出的一些编写CSS代码的规范和方法。他们提出了一些概念,这些概念可能听起来很高大上,但是实际你平时可能不知不觉也会用到这些所谓的CSS方法论。下面我简单地介绍下几个比较常见的CSS方法论。

OOCSS  
OOCSS是(Object Oriented CSS),顾名思义就是面向对象的CSS。  
OOCSS主要有两个原则:  
1、结构和样式分离  
我们平时一定遇到过这种情况,比如一个页面存在着多个不同功能的按钮,这些按钮的形状大小都差不多,但是根据不同的功能会有不同的颜色或背景来加以区分。如果不进行结构和样式分离,我们的CSS代码可能是这样的

1
2
.btn-primary{  width:100px;  height:50px;  padding:5px 3px;  background:#ccc;  color:#000;  }  
.btn-delete{  width:100px;  height:50px;  padding:5px 3px;  background:red;  color:#fff;  }

这两个或者可能更多的按钮拥有一些不同的样式,但是它们同时拥有相同的大小样式等,我们将其抽象的部分提取出来,结果如下:

1
2
3
.btn{  width:100px;  height:50px;  padding:5px 3px;  }  
.primary{  background:red;  color:#fff;  }  
.delete{  background:red;  color:#fff;  }

这样提取公用的样式出来,然后按钮同时引用btn和primary等。这种做法除了减少重复的代码精简CSS之外,还有一个好处是复用性,如果需要增加其他额外的按钮,只需要编写不同的样式,和btn配合使用即可。

2、容器和内容分离

SMACSS

SMACSS是什么呢,它的全称是Scalable and Modular Architecture for
CSS。简单说就是可扩展和模块化的CSS架构。

SMACSS将样式分成5种类型:Base,Layout,Module,State,Theme,我们简单来说说每一种类型分别指什么。

1、Base  
基础样式表,定义了基本的样式,我们平时写CSS比如reset.css就是属于基础样式表,另外我认为清除浮动,一些动画也可以归类为基础样式。  
2、Layout  
布局样式,用于实现网页的基本布局,搭起整个网页的基本骨架。  
3、Module  
网页中不同的区域有这个不同的功能,这些功能是相对独立的,我们可以称其为模块。模块是独立的,可重用的组件,它们不依赖于布局组件,可以安全的删除修改而不影响其他模块。  
4、State  
状态样式,通常和js一起配合使用,表示某个组件或功能不同的状态,比如菜单选中状态,按钮不可用状态等。  
关于状态样式,我个人觉得要分情况进行讨论:  
(1)不同组件的同一状态的样式是一样的,比如头部的导航菜单的选中状态样式和侧栏的菜单选中状态样式是一样的,我认为这部分状态样式可以归类为State  
(2)不同组件的统一状态的样式是不一样的,即两个地方的菜单虽然都是选中状态,但是他们却又不同的选中样式,这部分样式不应该被认为是State类型,而是应该放在其组件对应的Module中。  
5、Theme  
皮肤样式,对于可更换皮肤的站点来说,这个是很有必要的,分离了结构和皮肤,根据不同的皮肤应用不同的样式文件。

BEM  
BEM是Block,Element,Modifier的缩写。下面分别来介绍一下这三个概念:  
(1)Block:在BEM的理论中,一个网页是由block组成的,比如头部是个block,内容是block,logo也是block,一个block可能由几个子block组成。  
(2)Element:element是block的一部分,具有某种功能,element依赖于block,比如在logo中,img是logo的一个element,在菜单中,菜单项是菜单的一个element  
(3)Modifier:modifier是用来修饰block或者element的,它表示block或者element在外观或行为上的改变  
我们通过BEM命名法写样式如下:  
.block{}  
.block-element{}  
.block-modifier{}  
.block-element-modifier{}  
BEM将页面解析为block和element,然后根据不同的状态使用modifier来设置样式。  
我对BEM的思想理解可能不到位,对BEM的看法主要是由两点:  
(1)页面CSS模块化,每个block就是一个模块,模块间相互独立  
(2)多级的class命名,避免选择器的嵌套结构

总结的写CSS代码的一些关键点。  

1、写代码之前:从PSD文件出发  
当我们拿到设计师给的PSD时,首先不要急于写CSS代码,首先对整个页面进行分析,主要关注点是下面几个:  
(1)页面分成了几个模块,哪些模块是公用的,常见的比如头部和底部,还有一些菜单栏等等  
(2)分析每一个模块都有什么样式,提取出公用的样式,注意公用样式是全局公用(整个页面公用)还是局部公用(模块内公用),公用样式包括公用的状态样式,比如公用的选中状态,禁用状态等等。  
2、开始写代码  
根据对PSD文件的分析,我们就可以开始着手写代码,我比较推荐SMACSS将样式分成不同类型的做法:  
(1)第一步是搭好页面的骨架,也就是base样式,layout样式。  
(2)第二步就是依次实现不同的模块,在这里我推荐BEM的命名思想,但是可以嵌套一到两层的选择器结构  
3、优化代码  
我相信当我们完成基本的页面效果后,还是会存在着一些重复的或者不够简洁的代码,这时候就是要去优化这些代码,主要是在提取重复代码,尽可能地精简代码。

JS优化

相关 :Nicholas 《Speed up your JavaScript》

  • 定义局部变量
  • 不要使用 with() 语句
  • 小心使用闭包
  • 对象属性和数组元素的速度都比变量慢
  • 不要在数组中挖得太深
  • 避免 for-in 循环(和基于函数的迭代)
    这是另一条非常教条的建议:不要使用for-in循环。

这背后的逻辑非常直接:要遍历一个集合内的元素,你可以使用诸如for循环、或者do-while循环来替代for-in循环,for-in循环不仅仅可能需要遍历额外的数组项,还需要更多的时间。

为了遍历这些元素,JavaScript需要为每一个元素建立一个函数,这种基于函数的迭代带来了一系列性能问题:额外的函数引入了函数对象被创建和销毁的上下文,将会在作用域链的顶端增加额外的元素。

  • 在循环时将控制条件和控制变量合并起来

针对DOM问题,Javascript的应对方案

核心问题

当解析的html文件很大时,生成DOM树占用内存较大,同时遍历(不更新)元素耗时也更长。但这都不是重点,DOM的核心问题是:DOM修改导致的页面重绘、重新排版!重新排版是用户阻塞的操作,同时,如果频繁重排,CPU使用率也会猛涨!

DOM操作会导致一系列的重绘(repaint)、重新排版(reflow)操作。为了确保执行结果的准确性,所有的修改操作是按顺序同步执行的。大部分浏览器都不会在JavaScript的执行过程中更新DOM。相应的,这些浏览器将对对 DOM的操作放进一个队列,并在JavaScript脚本执行完毕以后按顺序一次执行完毕。也就是说,在JavaScript执行的过程,直到发生重新排版,用户一直被阻塞。

一般的浏览器中(不含IE),repaint的速度远快于reflow,所以避免reflow更重要。

导致repaint、reflow的操作:

  • DOM元素的添加、修改(内容)、删除( Reflow + Repaint)

  • 仅修改DOM元素的字体颜色(只有Repaint,因为不需要调整布局)

  • 应用新的样式或者修改任何影响元素外观的属性

  • Resize浏览器窗口、滚动页面

  • 读取元素的某些属性(offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE))

其他
某些Javascript框架中,CSS选择器,如:var el = $(‘.hyddd’);由于IE6、7不支持,所以Javascript框架必须通过遍历整个DOM树来寻找对象。

解决问题的关键是:减少因DOM操作,引起的reflow。Nicholas总结了一些方法:

在DOM外,执行尽量多的变更操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 不好的做法
for (var i=0; i < items.length; i++){
var item = document.createElement("li");
item.appendChild(document.createTextNode("Option " + i);
list.appendChild(item);
}
// 更好的做法
// 使用容器存放临时变更, 最后再一次性更新DOM
var fragment = document.createDocumentFragment();
for (var i=0; i < items.length; i++){
var item = document.createElement("li");
item.appendChild(document.createTextNode("Option " + i);
fragment.appendChild(item);
}
list.appendChild(fragment);

操作DOM前,先把DOM节点删除或隐藏,因为隐藏的节点不会触发重排。Demo如下:

1
2
3
4
5
6
7
list.style.display = "none";
for (var i=0; i < items.length; i++){
var item = document.createElement("li");
item.appendChild(document.createTextNode("Option " + i);
list.appendChild(item);
}
list.style.display = "";

一次性,修改样式属性。Demo如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 不好的做法
// 这种做法会触发多次重排
element.style.backgroundColor = "blue";
element.style.color = "red";
element.style.fontSize = "12em";
// 更好的做法是,把样式都放在一个class下
.newStyle {
background-color: blue;
color: red;
font-size: 12em;
}
element.className = "newStyle";

使用缓存,缓存临时节点。

1
2
3
4
5
6
7
// 不好的做法
document.getElementById("myDiv").style.left = document.getElementById("myDiv").offsetLeft +
document.getElementById("myDiv").offsetWidth + "px";
// 更好的做法
var myDiv = document.getElementById("myDiv");
myDiv.style.left = myDiv.offsetLeft + myDiv.offsetWidth + "px";

前端综合

使用tree-shaking以及code-splitting机制来减轻负载(Use tree-shaking and code-splitting to reduce payloads)

在你进行项目构建的过程中,Tree-shaking机制能够帮你清理生产环境中的冗余代码。例如,你可以利用Webpack2中的Tree-Shaking机制来清理冗余的exports代码或者使用UnCSS、Helium工具来清理冗余的CSS代码。或许你也想知道究竟如何才能写出高效的CSS选择器以及究竟如何才能避免自己写出冗余、低性能的CSS代码。

code splitting机制实际上是Webpack的一个特性,该特性能够将你的代码块分成多个“chunk”,并且能够做到对“chunk”按需载入。另外,一旦你在代码块中定义好分割点(split point),那么Webpack就会帮你处理好分割点之间的依赖关系,而且还会输出相对应的文件。在项目中使用Webpack的split point特性之后,(我们发现)该特性不仅能够实现对文件的瘦身,而且还能对(所需要的)代码做到按需载入。

值得注意的是,通过对比Browserify之后,我们发现:“用Rollup来export代码会取得更加不错的效果”。还有就是,你可能需要用到Rollupify(Browserify进行transform时需要用到的工具,能够将ES6模块转成CommonJS模块),这是因为小型规模的模块(module)能够起到意想不到的优化效果,(至于究竟可以优化到哪种程度),这取决于你选择的打包工具以及模块系统(AMD、CommonJS、ES6模块)。

优化网络环境能够加快网络传输的速度(Warm up the connection to speed up delivery)

使用skeleton screen或者使用懒加载的方式载入字体或者HTTP开销很大的组件,例如,视频、iframe、轮播图
为节省时间,可以针对下面列出的操作使用资源优化的小技巧
dns-prefetch(能够让浏览器在后台进程执行一次DNS查询),<link rel="dns-prefetch" href="//fonts.googleapis.com"
preconnect(能够让浏览器在后台进程发起一次握手(DNS,TCP,TLS)),
prefetch(能够让浏览器发起对资源的请求),<link rel="prefetch" href="http://daker.me/2013/05/hello-world.html"/>
prerender(能够让浏览器在后台进程渲染出特定的页面),<link rel="prerender" href="http://daker.me/2013/05/hello-world.html"/>
preload(在不执行资源的前提下,预先拿到该资源),<link rel="preload" as="script" href="map.js" media="(min-width: 601px)">
值得注意的是:在实践过程中,由于浏览器对这些东西支持不一,因此对于preconnect来说,你要更倾向于使用dns-prefetch,另外在使用prefetchprerender的过程中,你需要特别小心,这是因为使用prerender的场景(指的是你要很清楚地知道用户下一步所做的决策,例如购买渠道)很特殊。

用service worker来处理缓存问题或者将service worker作为网络(出现问题时)的应急方案,合适吗?(Are service workers used for caching and network fallbacks?)

你要知道,(在有网以及服务器已被优化的情况下),用户从服务端拿数据是不可能比用户从本地拿缓存来的快。如果你的网站已经切到HTTPS,推荐你使用pragmatist-service-worker(不但可以(让你)通过service worker cache来缓存静态资源、离线资源(比如离线的页面),而且可以让你从缓存中拿数据)。当然,你也可以去看Jake的Offline Cookbook或者去看Udacity开设的免费公开课“离线web应用”。浏览器对serviceworker支持度怎么样?你要的答案在这里,不管浏览器对service worker支持度怎么样,性能优化的备用方案仍然是基于网络的。

Service Worker初体验

HTML5的离线储存怎么使用,工作原理能不能解释一下?

在用户没有与因特网连接时,可以正常访问站点或应用,在用户与因特网连接时,更新用户机器上的缓存文件。
原理:HTML5的离线存储是基于一个新建的.appcache文件的缓存机制(不是存储技术),通过这个文件上的解析清单离线存储资源,这些资源就会像cookie一样被存储了下来。之后当网络在处于离线状态下时,浏览器会通过被离线存储的数据进行页面展示。

如何使用:
1、页面头部像下面一样加入一个manifest的属性;
2、在cache.manifest文件的编写离线存储的资源;
CACHE MANIFEST

#v0.11
CACHE:
js/app.js
css/style.css
NETWORK:
resourse/logo.png
FALLBACK:
/ /offline.html

3、在离线状态时,操作window.applicationCache进行需求实现。

HTML5 离线缓存-manifest简介