面试总结(1)---7.28

Ajax关于readyState(状态值)和status(状态码)的研究

var getXmlHttpRequest = function () {

    try{

        //主流浏览器提供了XMLHttpRequest对象

        return new XMLHttpRequest();

    }catch(e){

        //低版本的IE浏览器没有提供XMLHttpRequest对象,IE6以下

        //所以必须使用IE浏览器的特定实现ActiveXObject

        return new ActiveXObject("Microsoft.XMLHTTP");

    }

};

var xhr = getXmlHttpRequest();

// readyState 0=>初始化 1=>载入 2=>载入完成 3=>解析 4=>完成

// console.log(xhr.readyState);  0

xhr.open("TYPE", "URL", true);

// console.log(xhr.readyState);  1

xhr.send();

// console.log(xhr.readyState);  1

xhr.onreadystatechange = function () {

    // console.log(xhr.status); //HTTP状态吗

    // console.log(xhr.readyState);  2 3 4

    if(xhr.readyState === 4 && xhr.status === 200){

        alert(xhr.responseText);

    }

};

 

1.AjaxreadyState(状态值)和status(状态码)的区别
readyState,是指运行AJAX所经历过的几种状态,无论访问是否成功都将响应的步骤,可以理解成为AJAX运行步骤,使用“ajax.readyState”获得
status,是指无论AJAX访问是否成功,由HTTP协议根据所提交的信息,服务器所返回的HTTP头信息代码,使用“ajax.status”获得
总体理解:可以简单的理解为state代表一个整体的状态。而status是这个大的state下面具体的小的状态。

2.什么是readyState
readyState
XMLHttpRequest对象的一个属性,用来标识当前XMLHttpRequest对象处于什么状态。
readyState总共有5个状态值,分别为0~4,每个值代表了不同的含义

1

2

3

4

5

0:初始化,XMLHttpRequest对象还没有完成初始化

1:载入,XMLHttpRequest对象开始发送请求

2:载入完成,XMLHttpRequest对象的请求发送完成

3:解析,XMLHttpRequest对象开始读取服务器的响应

4:完成,XMLHttpRequest对象读取服务器响应结束

3.什么是status
status
XMLHttpRequest对象的一个属性,表示响应的HTTP状态码
HTTP1.1协议下,HTTP状态码总共可分为5大类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

1xx:信息响应类,表示接收到请求并且继续处理

2xx:处理成功响应类,表示动作被成功接收、理解和接受

3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理

4xx:客户端错误,客户请求包含语法错误或者是不能正确执行

5xx:服务端错误,服务器不能正确执行一个正确的请求

 

100——客户必须继续发出请求

101——客户要求服务器根据请求转换HTTP协议版本

200——交易成功

201——提示知道新文件的URL

202——接受和处理、但处理未完成

203——返回信息不确定或不完整

204——请求收到,但返回信息为空

205——服务器完成了请求,用户代理必须复位当前已经浏览过的文件

206——服务器已经完成了部分用户的GET请求

300——请求的资源可在多处得到

301——删除请求数据

302——在其他地址发现了请求数据

303——建议客户访问其他URL或访问方式

304——客户端已经执行了GET,但文件未变化

305——请求的资源必须从服务器指定的地址得到

306——前一版本HTTP中使用的代码,现行版本中不再使用

307——申明请求的资源临时性删除

400——错误请求,如语法错误

401——请求授权失败

402——保留有效ChargeTo头响应

403——请求不允许

404——没有发现文件、查询或URl

405——用户在Request-Line字段定义的方法不允许

406——根据用户发送的Accept拖,请求资源不可访问

407——类似401,用户必须首先在代理服务器上得到授权

408——客户端没有在用户指定的饿时间内完成请求

409——对当前资源状态,请求不能完成

410——服务器上不再有此资源且无进一步的参考地址

411——服务器拒绝用户定义的Content-Length属性请求

412——一个或多个请求头字段在当前请求中错误

413——请求的资源大于服务器允许的大小

414——请求的资源URL长于服务器允许的长度

415——请求资源不支持请求项目格式

416——请求中包含Range请求头字段,在当前请求资源范围内没有range指示值,请求也不包含If-Range请求头字段

417——服务器不满足请求Expect头字段指定的期望值,如果是代理服务器,可能是下一级服务器不能满足请求

500——服务器产生内部错误

501——服务器不支持请求的函数

502——服务器暂时不可用,有时是为了防止发生系统过载

503——服务器过载或暂停维修

504——关口过载,服务器使用另一个关口或服务来响应用户,等待时间设定值较长

505——服务器不支持或拒绝支请求头中指定的HTTP版本

 4.思考问题:为什么onreadystatechange的函数实现要同时判断readyStatestatus呢?

第一种思考方式:只使用readyState

var getXmlHttpRequest = function () {

  if (window.XMLHttpRequest) {

    return new XMLHttpRequest();

  }

  else if (window.ActiveXObject) {

    return new ActiveXObject("Microsoft.XMLHTTP");

  }

};

var xhr = getXmlHttpRequest();

xhr.open("get", "1.txt", true);

xhr.send();

xhr.onreadystatechange = function () {

  if (xhr.readyState === 4) {

    alert(xhr.responseText);

  }

};

服务响应出错了,但还是返回了信息,这并不是我们想要的结果
如果返回不是200,而是404或者500,由于只使用readystate做判断,它不理会放回的结果是200404还是500,只要响应成功返回了,就执行接下来的javascript代码,结果将造成各种不可预料的错误。所以只使用readyState判断是行不通的。

第二种思考方式:只使用status判断

var getXmlHttpRequest = function () {

  try{

    return new XMLHttpRequest();

  }catch(e){

    return new ActiveXObject("Microsoft.XMLHTTP");

  }

};

var xhr = getXmlHttpRequest();

xhr.open("get", "1.txt", true);

xhr.send();

xhr.onreadystatechange = function () {

    if (xhr.status === 200) {

        alert("readyState=" + xhr.readyState + xhr.responseText);

    }

};

事实上,结果却不像预期那样。响应码确实是返回了200,但是总共弹出了3次窗口!第一次是“readyState=2”的窗口,第二次是“readyState=3”的窗口,第三次是“readyState=4”的窗口。由此,可见onreadystatechange函数的执行不是只在readyState变为4的时候触发的,而是readyState234)的每次变化都会触发,所以就出现了前面说的那种情况。可见,单独使用status判断也是行不通的。

5.由上面的试验,我们可以知道判断的时候readyStatestatus缺一不可。那么readyStatestatus的先后判断顺序会不会有影响呢?我们可以将status调到前面先判断,代码如 xhr.status === 200 && xhr.readyState === 4
事实上,这对于最终的结果是没有影响的,但是中间的性能就不同了。由试验我们知道,readyState的每次变化都会触发onreadystatechange函数,假如先判断status,那么每次都会多判断一次status的状态。虽然性能上影响甚微,不过还是应该抱着追求极致代码的想法,把readyState的判断放在前面。
xhr.readyState === 4 && xhr.status === 200

 

 

 

CSS3的 transform属性,怎么才能让他同时执行多个不同动画(属性)效果

div{width: 100px; height: 100px; transition: all 1s; background: red;}


 

div:hover{transform: rotate(360deg) scale(2,2) skew(10deg,5deg);} 中间用空格隔开 旋转 缩放 扭曲 等同时执行多个效果!

定义和用法

transform 属性向元素应用 2D 3D 转换。该属性允许我们对元素进行旋转、缩放、移动或倾斜。

为了更好地理解 transform 属性,请查看这个演示

默认值:

none

继承性:

no

版本:

CSS3

JavaScript 语法:

object.style.transform="rotate(7deg)"

语法

transform: none|transform-functions;

描述

测试

none

定义不进行转换。

测试

matrix(n,n,n,n,n,n)

定义 2D 转换,使用六个值的矩阵。

测试

matrix3d(n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n)

定义 3D 转换,使用 16 个值的 4x4 矩阵。

 

translate(x,y)

定义 2D 转换。

测试

translate3d(x,y,z)

定义 3D 转换。

 

translateX(x)

定义转换,只是用 X 轴的值。

测试

translateY(y)

定义转换,只是用 Y 轴的值。

测试

translateZ(z)

定义 3D 转换,只是用 Z 轴的值。

 

scale(x,y)

定义 2D 缩放转换。

测试

scale3d(x,y,z)

定义 3D 缩放转换。

 

scaleX(x)

通过设置 X 轴的值来定义缩放转换。

测试

scaleY(y)

通过设置 Y 轴的值来定义缩放转换。

测试

scaleZ(z)

通过设置 Z 轴的值来定义 3D 缩放转换。

 

rotate(angle)

定义 2D 旋转,在参数中规定角度。

测试

rotate3d(x,y,z,angle)

定义 3D 旋转。

 

rotateX(angle)

定义沿着 X 轴的 3D 旋转。

测试

rotateY(angle)

定义沿着 Y 轴的 3D 旋转。

测试

rotateZ(angle)

定义沿着 Z 轴的 3D 旋转。

测试

skew(x-angle,y-angle)

定义沿着 X 和 Y 轴的 2D 倾斜转换。

测试

skewX(angle)

定义沿着 X 轴的 2D 倾斜转换。

测试

skewY(angle)

定义沿着 Y 轴的 2D 倾斜转换。

测试

perspective(n)

为 3D 转换元素定义透视视图。

测试

 

 

 

CSS3 @keyframes 规则

如需在 CSS3 中创建动画,您需要学习 @keyframes 规则。

@keyframes 规则用于创建动画。在 @keyframes 中规定某项 CSS 样式,就能创建由当前样式逐渐改为新样式的动画效果。

浏览器支持

属性

浏览器支持

@keyframes

         

animation

         

Internet Explorer 10Firefox 以及 Opera 支持 @keyframes 规则和 animation 属性。

Chrome Safari 需要前缀 -webkit-

注释:Internet Explorer 9,以及更早的版本,不支持 @keyframe 规则或 animation 属性。

实例

@keyframes myfirst
{
from {background: red;}
to {background: yellow;}
}
 
@-moz-keyframes myfirst /* Firefox */
{
from {background: red;}
to {background: yellow;}
}
 
@-webkit-keyframes myfirst /* Safari  Chrome */
{
from {background: red;}
to {background: yellow;}
}
 
@-o-keyframes myfirst /* Opera */
{
from {background: red;}
to {background: yellow;}
}

CSS3 动画

当您在 @keyframes 中创建动画时,请把它捆绑到某个选择器,否则不会产生动画效果。

通过规定至少以下两项 CSS3 动画属性,即可将动画绑定到选择器:

  • 规定动画的名称
  • 规定动画的时长

实例

"myfirst" 动画捆绑到 div 元素,时长:5 秒:

div
{
animation: myfirst 5s;
-moz-animation: myfirst 5s; /* Firefox */
-webkit-animation: myfirst 5s;     /* Safari  Chrome */
-o-animation: myfirst 5s;   /* Opera */
}

亲自试一试

注释:您必须定义动画的名称和时长。如果忽略时长,则动画不会允许,因为默认值是 0

什么是 CSS3 中的动画?

动画是使元素从一种样式逐渐变化为另一种样式的效果。

您可以改变任意多的样式任意多的次数。

请用百分比来规定变化发生的时间,或用关键词 "from" "to",等同于 0% 100%

0% 是动画的开始,100% 是动画的完成。

为了得到最佳的浏览器支持,您应该始终定义 0% 100% 选择器。

实例

当动画为 25% 50% 时改变背景色,然后当动画 100% 完成时再次改变:

@keyframes myfirst
{
0%   {background: red;}
25%  {background: yellow;}
50%  {background: blue;}
100% {background: green;}
}
 
@-moz-keyframes myfirst /* Firefox */
{
0%   {background: red;}
25%  {background: yellow;}
50%  {background: blue;}
100% {background: green;}
}
 
@-webkit-keyframes myfirst /* Safari  Chrome */
{
0%   {background: red;}
25%  {background: yellow;}
50%  {background: blue;}
100% {background: green;}
}
 
@-o-keyframes myfirst /* Opera */
{
0%   {background: red;}
25%  {background: yellow;}
50%  {background: blue;}
100% {background: green;}
}

亲自试一试

实例

改变背景色和位置:

@keyframes myfirst
{
0%   {background: red; left:0px; top:0px;}
25%  {background: yellow; left:200px; top:0px;}
50%  {background: blue; left:200px; top:200px;}
75%  {background: green; left:0px; top:200px;}
100% {background: red; left:0px; top:0px;}
}
 
@-moz-keyframes myfirst /* Firefox */
{
0%   {background: red; left:0px; top:0px;}
25%  {background: yellow; left:200px; top:0px;}
50%  {background: blue; left:200px; top:200px;}
75%  {background: green; left:0px; top:200px;}
100% {background: red; left:0px; top:0px;}
}
 
@-webkit-keyframes myfirst /* Safari  Chrome */
{
0%   {background: red; left:0px; top:0px;}
25%  {background: yellow; left:200px; top:0px;}
50%  {background: blue; left:200px; top:200px;}
75%  {background: green; left:0px; top:200px;}
100% {background: red; left:0px; top:0px;}
}
 
@-o-keyframes myfirst /* Opera */
{
0%   {background: red; left:0px; top:0px;}
25%  {background: yellow; left:200px; top:0px;}
50%  {background: blue; left:200px; top:200px;}
75%  {background: green; left:0px; top:200px;}
100% {background: red; left:0px; top:0px;}
}

亲自试一试

CSS3 动画属性

下面的表格列出了 @keyframes 规则和所有动画属性:

属性

描述

CSS

@keyframes

规定动画。

3

animation

所有动画属性的简写属性,除了 animation-play-state 属性。

3

animation-name

规定 @keyframes 动画的名称。

3

animation-duration

规定动画完成一个周期所花费的秒或毫秒。默认是 0。

3

animation-timing-function

规定动画的速度曲线。默认是 "ease"。

3

animation-delay

规定动画何时开始。默认是 0。

3

animation-iteration-count

规定动画被播放的次数。默认是 1。

3

animation-direction

规定动画是否在下一周期逆向地播放。默认是 "normal"。

3

animation-play-state

规定动画是否正在运行或暂停。默认是 "running"。

3

animation-fill-mode

规定对象动画时间之外的状态。

3

下面的两个例子设置了所有动画属性:

实例

运行名为 myfirst 的动画,其中设置了所有动画属性:

div
{
animation-name: myfirst;
animation-duration: 5s;
animation-timing-function: linear;
animation-delay: 2s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-play-state: running;
/* Firefox: */
-moz-animation-name: myfirst;
-moz-animation-duration: 5s;
-moz-animation-timing-function: linear;
-moz-animation-delay: 2s;
-moz-animation-iteration-count: infinite;
-moz-animation-direction: alternate;
-moz-animation-play-state: running;
/* Safari  Chrome: */
-webkit-animation-name: myfirst;
-webkit-animation-duration: 5s;
-webkit-animation-timing-function: linear;
-webkit-animation-delay: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-direction: alternate;
-webkit-animation-play-state: running;
/* Opera: */
-o-animation-name: myfirst;
-o-animation-duration: 5s;
-o-animation-timing-function: linear;
-o-animation-delay: 2s;
-o-animation-iteration-count: infinite;
-o-animation-direction: alternate;
-o-animation-play-state: running;
}

亲自试一试

实例

与上面的动画相同,但是使用了简写的动画 animation 属性:

div
{
animation: myfirst 5s linear 2s infinite alternate;
/* Firefox: */
-moz-animation: myfirst 5s linear 2s infinite alternate;
/* Safari  Chrome: */
-webkit-animation: myfirst 5s linear 2s infinite alternate;
/* Opera: */
-o-animation: myfirst 5s linear 2s infinite alternate;
}

HTML5的canvas绘图和CSS3的绘图哪个更有优越性

如题,最好能从多个方面比较,能说下区别,二者的原理。

面试总结(1)---7.28_第1张图片iGO2dU 
2013-09-27

简单解释一下:

  1. CSS更像是把多个“矩形”(div)裁剪后,然后拼接成一个图案,然后给图案上色。
  2. Canvas由点开始,延长无数个点,得到线,延长线之后得到一个面(三角形,圆形,矩形等等的图案面),然后给线或者面描边,上色。
  3. CSS目前更像是小朋友的手工课,Canvas更像是用一支笔画图,不过画出来的图更加像能够控制大小的矢量图片

下图简单说明

面试总结(1)---7.28_第2张图片

在面对曲线和更复杂图形的时候,Canvas比CSS更有办法。另外Canvas确定坐标位置的时候更加贴近我们常用到的数学思维方法。

只能显浅说说。

为什么canvas绘制的线条会模糊、有锯齿?

20170530 22:24:51

阅读数:5385

有如下的代码:

<style type="text/css">
    canvas {
        position:absolute;
        height : 100%;
        width : 100%;
    }
style>
<canvas id="canvas" width="100%" height="100%">
canvas>
<script type="text/javascript">
    var canvas = document.getElementById('canvas'),
        context = canvas.getContext('2d')
    context.translate(70, 70);
    context.moveTo(0, 0)
    context.lineTo(70, -70)
    context.stroke();
script>

结果实际的效果虚化非常严重,清晰度非常差,锯齿非常严重,如下所示: 
 
为什么会出现这样的情况呢?原因是canvas的宽度与高度必须作为属性明确指定(也不能通过CSS设置),并且只能是数字,不支持百分比。基于以上的规则,所以很容易找到症结,canvas绘制的图片本来较小,但经过CSS强行放大拉伸,所以就会出现模糊、锯齿严重的效果。

解决的办法很简单,在绘制之前,首先设置canvas的宽度,代码如下:

var canvas = document.getElementById('canvas'),
    //  计算画布的宽度
    width = canvas.offsetWidth,
    //  计算画布的高度
    height = canvas.offsetHeight,
    context = canvas.getContext('2d')
    //  设置宽高
    canvas.width = width;
    canvas.height = height;

再次刷新浏览器,终于一切正常了。

结论

HTML中很多元素的宽高必须通过属性设定,而不是CSS,比如canvas,比如SVG

html5 canvas 画图移动端出现锯齿毛边的解决方法

Posted on 2017-05-19 11:01 人生梦想起飞 阅读(2748) 评论(0) 编辑 收藏

使用HTML5canvas元素画出来的.在移动端手机上测试都发现画图有一点锯齿问题

出现这个问题的原因应该是手机的宽是720像素的, 而这个canvas是按照小于720像素画出来的, 所以在720像素的手机上显示时, 这个canvas的内容其实是经过拉伸的, 所以会出现模糊和锯齿.

解决方案一:就是在canvas标签中设置了width="200",height="200"之外, 还在外部的CSS样式表中设置了该canvas的宽度为100%,然后在画图时把canvas的的宽度设为手机端的最大像素值, 因为现在的手机端宽度的最大的只有1080像素宽, 所以就把canvas的宽度和高度设为2006倍也就是1200像素, 按照这个像素画完之后, width:100%又会把canvas的宽度和高度缩小至父元素的宽和宽那么大, 因此整个canvas被缩小了, 大尺寸的canvas内容被缩小了之后肯定不会产生锯齿现象,解决的原理其实就是画图时候将canvas的宽和高放大一定的倍数,按照放大后的canvas宽和高画图,然后画完之后再将canvas缩小为目标宽和高,这样解决的方法存在的问题是,在PC端反而锯齿会更明白,只是移动端的效果很好,所以在pc端不需要放大倍数,实例如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

 html>

     http-equiv="Content-Type" content="text/html; charset=utf-8" />

     content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">

    html5 canvas </code><code>画图移动端出现锯齿毛边的解决方法

     type="text/css">

        #canvas{

            width: 100%;

        }

    

 style="background: url(blue_bj.jpg);">

  style="width: 200px">

     id="canvas" width="200" height="200" >

 

 type="text/javascript">

// 判断是移动还是pc

function IsPC() {

    var userAgentInfo = navigator.userAgent,

        Agents = ["Android", "iPhone",

                "SymbianOS", "Windows Phone",

                "iPad", "iPod"],

        flag = true;

    for (var v = 0; v < Agents.length; v++) {

        if (userAgentInfo.indexOf(Agents[v]) > 0) {

            flag = false;

            break;

        }

    }

    return flag;

}

//PC端和移动端方法倍数的判断

var scale = 1;

if( !IsPC() ){

   scale = 6;

}

var canvas=document.getElementById("canvas");

var cxt=canvas.getContext("2d");

//画一个空心圆

cxt.beginPath();

canvas.width = canvas.width*scale;

canvas.height = canvas.height*scale;

cxt.arc(canvas.width/2,canvas.height/2,canvas.width/2-scale*16,0,360,false);

cxt.lineWidth = scale*16;

cxt.strokeStyle = "#faff6d";

cxt.stroke();

cxt.closePath();

解决方案二:使用window.devicePixelRatio设备上物理像素和设备独立像素(device-independent pixels (dips))的比例来设置canvas实际需要放大的倍数,原理与上一种方法一样,区别在于 devicePixelRatio取出的是实际的比例倍数,在pc端显示为1,避免了上种方法PC端不判断同样放大一样倍数画图出现明显锯齿问题,但是devicePixelRatio各个浏览器的兼容性不是很好,这是唯一缺陷,实现方法如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

 html>

     http-equiv="Content-Type" content="text/html; charset=utf-8" />

     content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">

    html5 canvas </code><code>画图移动端出现锯齿毛边的解决方法

 style="background: url(blue_bj.jpg);">

     id="canvas" width="200" height="200" >

 type="text/javascript">

    var canvas=document.getElementById("canvas");

    var cxt=canvas.getContext("2d");

    //画一个空心圆

     cxt.beginPath();

     var width = canvas.width,

         height=canvas.height;

    if (window.devicePixelRatio) {

        canvas.style.width = width + "px";

        canvas.style.height = height + "px";

        canvas.height = height * window.devicePixelRatio;

        canvas.width = width * window.devicePixelRatio;

        cxt.arc(canvas.width/2,canvas.height/2,canvas.width/2-16 * window.devicePixelRatio,0,360,false);

        cxt.lineWidth=16 * window.devicePixelRatio;

        cxt.strokeStyle="#faff6d";

        cxt.stroke();//画空心圆

        cxt.closePath();

        cxt.scale(window.devicePixelRatio, window.devicePixelRatio);

    }

 备注:以上两种方法经初步测试只有在安卓红米Note下的UC浏览器中不支持,原因在于安卓红米Note下的UC浏览器中对于HTML5 canvas画布是最大限制很小,不能超过256px ,超过画布就显示花屏,不能显示绘制的图,属于安卓红米Note下的UC浏览器对于canvas的兼容性不支持超过这个宽度的画图

清除,H5canvas画的图中的锯齿

20160824 22:22:06

阅读数:333

手动开启HTML5 Canvas的抗锯齿可以用

canvas.getContext('2d').imageSmoothingEnabled = true;

或者直接把整个画布偏移0.5像素:

canvas.getContext('2d').translate(0.5, 0.5);

css3动画与js动画的区别

css js动画 优缺点比较

 

我们经常面临一个抉择:到底使用JavaScript还是CSS动画,下面做一下对比

JS动画

缺点:(1)JavaScript在浏览器的主线程中运行,而主线程中还有其它需要运行的JavaScript脚本、样式计算、布局、绘制任务等,对其干扰导致线程可能出现阻塞,从而造成丢帧的情况。

        (2)代码的复杂度高于CSS动画

优点:(1)JavaScript动画控制能力很强可以在动画播放过程中对动画进行控制:开始、暂停、回放、终止、取消都是可以做到的。

        (2)动画效果比css3动画丰富,有些动画效果,比如曲线运动,冲击闪烁,视差滚动效果,只有JavaScript动画才能完成

        (3)CSS3有兼容性问题,而JS大多时候没有兼容性问题

CSS动画

缺点:

   (1)运行过程控制较弱,无法附加事件绑定回调函数。CSS动画只能暂停,不能在动画中寻找一个特定的时间点,不能在半路反转动画,不能变换时间尺度,不能在特定的位置添加回调函数或是绑定回放事件,无进度报告

   (2)代码冗长。想用 CSS 实现稍微复杂一点动画,最后CSS代码都会变得非常笨重。


优点: (1)浏览器可以对动画进行优化。

  •  浏览器使用与 requestAnimationFrame 类似的机制,requestAnimationFrame比起setTimeoutsetInterval设置动画的优势主要是:1)requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。2)在隐藏或不可见的元素中requestAnimationFrame不会进行重绘或回流,这当然就意味着更少的的cpugpu和内存使用量。
  • 强制使用硬件加速 (通过 GPU 来提高动画性能)

 

CSS动画流畅的原因

渲染线程分为main thread(主线程)compositor thread(合成器线程)
如果CSS动画只是改变transformopacity,这时整个CSS动画得以在compositor thread完成(而JS动画则会在main thread执行,然后触发compositor进行下一步操作)
JS执行一些昂贵的任务时,main thread繁忙,CSS动画由于使用了compositor thread可以保持流畅,

在主线程中,维护了一棵Layer树(LayerTreeHost),管理了TiledLayer,在compositor thread,维护了同样一颗LayerTreeHostImpl,管理了LayerImpl,这两棵树的内容是拷贝关系。因此可以彼此不干扰,当Javascriptmain thread操作LayerTreeHost的同时,compositor thread可以用LayerTreeHostImpl做渲染。当Javascript繁忙导致主线程卡住时,合成到屏幕的过程也是流畅的。
为了实现防假死,鼠标键盘消息会被首先分发到compositor thread,然后再到main thread。这样,当main thread繁忙时,compositor thread还是能够响应一部分消息,例如,鼠标滚动时,加入main thread繁忙,compositor thread也会处理滚动消息,滚动已经被提交的页面部分(未被提交的部分将被刷白)。

CSS动画比JS流畅的前提:

  • JS在执行一些昂贵的任务
  • 同时CSS动画不触发layoutpaint
    CSS动画或JS动画触发了paintlayout时,需要main thread进行Layer树的重计算,这时CSS动画或JS动画都会阻塞后续操作。

     只有如下属性的修改才符合仅触发Composite,不触发layoutpaint”

  • backface-visibility
  • opacity
  • perspective
  • perspective-origin
  • transfrom

所以只有用上了3D加速或修改opacity时,css3动画的优势才会体现出来。

     (2)代码相对简单,性能调优方向固定

     (3)对于帧速表现不好的低版本浏览器,CSS3可以做到自然降级,而JS则需要撰写额外代码

结论

 如果动画只是简单的状态切换,不需要中间过程控制,在这种情况下,css动画是优选方案。它可以让你将动画逻辑放在样式文件里面,而不会让你的页面充斥 Javascript 库。然而如果你在设计很复杂的富客户端界面或者在开发一个有着复杂UI状态的 APP。那么你应该使用js动画,这样你的动画可以保持高效,并且你的工作流也更可控。所以,在实现一些小的交互动效的时候,就多考虑考虑CSS动画。对于一些复杂控制的动画,使用javascript比较可靠。

关于Cookie的原理、作用,区别以及使用

20161015 09:35:10

阅读数:44112

1、cookie的作用:

我们在浏览器中,经常涉及到数据的交换,比如你登录邮箱,登录一个页面。我们经常会在此时设置30天内记住我,或者自动登录选项。那么它们是怎么记录信息的呢,答案就是今天的主角cookie了,Cookie是由HTTP服务器设置的,保存在浏览器中,但HTTP协议是一种无状态协议,在数据交换完毕后,服务器端和客户端的链接就会关闭,每次交换数据都需要建立新的链接。就像我们去超市买东西,没有积分卡的情况下,我们买完东西之后,超市没有我们的任何消费信息,但我们办了积分卡之后,超市就有了我们的消费信息。cookie就像是积分卡,可以保存积分,商品就是我们的信息,超市的系统就像服务器后台,http协议就是交易的过程。


 

2、机制的区别:

session机制采用的是在服务器端保持状态的方案,而cookie机制则是在客户端保持状态的方案,cookie又叫会话跟踪机制。打开一次浏览器到关闭浏览器算是一次会话。说到这里,讲下HTTP协议,前面提到,HTTP协议是一种无状态协议,在数据交换完毕后,服务器端和客户端的链接就会关闭,每次交换数据都需要建立新的链接。此时,服务器无法从链接上跟踪会话。cookie可以跟踪会话,弥补HTTP无状态协议的不足。


 

3、cookie的分类:

cookie分为会话cookie和持久cookie,会话cookie是指在不设定它的生命周期expires时的状态,前面说了,浏览器的开启到关闭就是一次会话,当关闭浏览器时,会话cookie就会跟随浏览器而销毁。当关闭一个页面时,不影响会话cookie的销毁。会话cookie就像我们没有办理积分卡时,单一的买卖过程,离开之后,信息则销毁。

持久cookie则是设定了它的生命周期expires,此时,cookie像商品一样,有个保质期,关闭浏览器之后,它不会销毁,直到设定的过期时间。对于持久cookie,可以在同一个浏览器中传递数据,比如,你在打开一个淘宝页面登陆后,你在点开一个商品页面,依然是登录状态,即便你关闭了浏览器,再次开启浏览器,依然会是登录状态。这就是因为cookie自动将数据传送到服务器端,在反馈回来的结果。持久cookie就像是我们办理了一张积分卡,即便离开,信息一直保留,直到时间到期,信息销毁。

 

4、简单的使用cookie的代码

cookie的几种常见属性:document.cookie="key=value;expires=失效时间;path=路径;domain=域名;secure;(secure表安全级别),

cookie以字符串的形式保存在浏览器中。下面贴段代码出来,是一个类似购物网站的将商品添加到购物车,再从购物车还原商品信息的过程,是自己用原生JS封装的函数。

封装的cookie的存入,读取以及删除的函数:(这里是将信息以对象的形式存放到cookie中的,会用到JSON的知识)

  1. // key : cookie
  2. // value : cookie
  3. // options : 可选配置参数
  4. //            options = {
  5. //                    expires : 7|new Date(), // 失效时间
  6. //                    path : "/", // 路径
  7. //                    domain : "", // 域名
  8. //                    secure : true // 安全连接
  9. //            }
  10. function cookie(key, value, options) {
  11.        /* read 读取 */
  12.        // 如果没有传递 value ,则表示根据 key 读取 cookie
  13.        if (typeof value === "undefined") { // 读取
  14.               // 获取当前域下所有的 cookie,保存到 cookies 数组中
  15.               var cookies = document.cookie.split("; ");
  16.               // 遍历 cookies 数组中的每个元素
  17.               for (var i = 0, len = cookies.length; i < len; i++) {
  18.                       // cookies[i] : 当前遍历到的元素,代表的是 "key=value" 意思的字符串,
  19.                       // 将字符串以 = 号分割返回的数组中第一个元素表示 key
  20.                       // 第二个元素表示 value
  21.                       var cookie = cookies[i].split("=");
  22.                       // 判断是否是要查找的 key,对查找的 key value 都要做解码操作
  23.                       if (decodeURIComponent(cookie[0]) === key) {
  24.                               return decodeURIComponent(cookie[1]);
  25.                       }
  26.               }
  27.               // 没有查找到指定的 key 对应的 value 值,则返回 null
  28.               return null;
  29.        }
  30.  
  31.        /* 存入 设置 */
  32.        // 设置 options 默认为空对象
  33.        options = options || {};
  34.        // key = value,对象 keyvalue 编码
  35.        var cookie = encodeURIComponent(key) + "=" + encodeURIComponent(value);
  36.        // 失效时间
  37.        if ((typeof options.expires) !== "undefined") { // 有配置失效时间
  38.               if (typeof options.expires === "number") { // 失效时间为数字
  39.                       var days = options.expires,
  40.                               t = options.expires = new Date();
  41.                       t.setDate(t.getDate() + days);
  42.               }
  43.               cookie += ";expires=" + options.expires.toUTCString();
  44.        }
  45.        // 路径
  46.        if (typeof options.path !== "undefined")
  47.               cookie += ";path=" + options.path;
  48.        // 域名
  49.        if (typeof options.domain !== "undefined")
  50.               cookie += ";domain=" + options.domain;
  51.        // 安全连接
  52.        if (options.secure)
  53.               cookie += ";secure";
  54.  
  55.        // 保存
  56.        document.cookie = cookie;
  57. }
  58.  
  59. // 从所有的 cookie 中删除指定的 cookie
  60. function removeCookie(key, options) {
  61.        options = options || {};
  62.        options.expires = -1; // 将失效时间设置为 1 天前
  63.        cookie(key, "", options);
  64. }


下面是商品详情页的JS代码

  1. // 找到所有的添加到购物车超级链接
  2.                       var links = $("a", $("#tab"));
  3.                       // 循环,为每个添加到购物车的超级链接添加点击事件
  4.                       for (var i = 0, len = links.length; i < len; i++) {
  5.                               links[i].onclick = function(){
  6.                                      // 获取当前超级链接所在行的所有单元格
  7.                                      var _cells = this.parentNode.parentNode.cells;
  8.                                      // 获取到即将添加到购物车中的商品信息
  9.                                      var _id = _cells[0].innerHTML,
  10.                                              _name = _cells[1].innerHTML,
  11.                                              _price = _cells[2].innerHTML;
  12.                                      // 将商品信息包装到一个对象中
  13.                                      var product = {
  14.                                              id : _id,
  15.                                              name : _name,
  16.                                              price : _price,
  17.                                              amount : 1
  18.                                      };
  19.  
  20.                                      /* 将当前选购的商品对象保存到 cookie 中去 */
  21.                                      // cookie 中读取已有的保存购物车的数组结构
  22.                                      var _products = cookie("products");
  23.                                      if (_products === null) // cookie 中不存在 products 名的 cookie
  24.                                              _products = [];
  25.                                      else // 存在,则解析 cookie 读取到的字符串为 数组 结构
  26.                                              _products = JSON.parse(_products);
  27.  
  28.                                      // 将当前选购的商品追加到数组中保存
  29.                                      _products.push(product);
  30.                                      // 继续将 _products 数组内容存回 cookie
  31.                                      cookie("products", JSON.stringify(_products), {expires:7});
  32.                               }
  33.                       }

html代码,css代码大家可以自己写

  1. "tab">
  2.               <tr>
  3.                       <td>序号td>
  4.                       <td>名称td>
  5.                       <td>价格td>
  6.                       <td>操作td>
  7.               tr>
  8.              
  9.                       <td>1td>
  10.                      
  11.                      
  12. 空调</td>
  13.                      
  14. 3999td>
  15.                       <td><a href="javascript:void(0);">添加到购物车a>td>
  16.               </tr>
  17.              
  18. 2td>
  19.                       <td>风扇td>
  20.                      
  21. 288</td>
  22.                      
  23. 添加到购物车a>td>
  24.               </tr>
  25.        table>
  26.        <a href="cart_购物车.html" target="_blank">查看购物车a>

  27. 购物车还原商品信息:

    1. // cookie 中读取购物车已有的商品信息
    2.                       var _products = cookie("products");
    3.                       // 判断购物车是否有商品
    4.                       if (_products === null || (_products = JSON.parse(_products)).length === 0)
    5.                               return;
    6.  
    7.                       // 如果有商品,则显示到页面中
    8.                       $(".result")[0].innerHTML = "";
    9.                       for (var i = 0, len = _products.length; i < len; i++) {
    10.                               // 当前遍历到的商品对象
    11.                               var prod = _products[i];
    12.                               // 克隆 .row 的节点
    13.                               var _row = $(".row")[0].cloneNode(true);
    14.                               // 将当前商品对象的信息替换节点中对应的部分,用class名获取到的节点返回类型是一个数组所以要在后面加上[0]
    15.                               $(".index", _row)[0].innerHTML = prod.id; // 编号
    16.                               $(".name", _row)[0].innerHTML = prod.name; // 名称
    17.                               $(".price", _row)[0].innerHTML = prod.price; // 价格
    18.                               $(".amount", _row)[0].innerHTML = prod.amount; // 数量
    19.                               $(".oper", _row)[0].innerHTML = "删除"
    20.  
    21.                               // 将克隆的节点副本追加到 .result div
    22.                               $(".result")[0].appendChild(_row);
    23.                       };
    24.  
    25.                       // 为每个删除的超级链接绑定点击事件
    26.                       var links = $("a", $("#container"));
    27.                       for (var i = 0, len = links.length; i < len; i++) {
    28.                               // links[i].index = i; // 为当前遍历到的超级链接附加数据
    29.                               links[i].product = _products[i]; //
    30.                               links[i].onclick = function(){
    31.                                      // alert("你点击的是第" + (this.index + 1) + "个连接");
    32.                                      var index = inArray(this.product, _products);
    33.                                     
    34.                                      if (index !== -1) {
    35.                                              _products.splice(index, 1);
    36.                                      }
    37.                                      // 更新 cookie
    38.                                      cookie("products", JSON.stringify(_products), {expires:7});
    39.  
    40.                                      // 找出页面中待删除的行
    41.                                      var _row = this.parentNode.parentNode;
    42.                                      _row.parentNode.removeChild(_row);
    43.                               };
    44.                       }

    这里的$(' ')函数是自己封装的函数,用于获取到DOM节点,可以看下我关于getElementsByClassName的兼容那篇文章。

    购物车的html代码

    1. <div id="container">
    2.               <div class="row">
    3.                       <div class="index">商品编号div>
    4.                       <div class="name">商品名称div>
    5.                       <div class="price">价格div>
    6.                       <div class="amount">数量div>
    7.                       <div class="oper">操作div>
    8.               div>
    9.               <div class="result" style="clear:both;">
    10.                       购物车为空
    11.               div>
    12.        div>

     

    浏览器的历史记录是存储在cookie,session,还是物理文件当中或者其他方式存储? 50

    在网络上,临时cookie为用户浏览器关闭时消失的含有用户有关信息的小文件,有时也称通话cookie。跟永久cookie不一样,临时cookie不保存在硬盘驱动器而是存在临时存储器中,当浏览器关闭时,将被删除。
    当应用程序创建cookie时,在设置Cookie选择项中不设置日期就可以创建临时cookie。(对于永久cookie,设置了截止日期,cookie保存在用户硬盘驱动器,直到截止日期或者用户的删除)。
    临时cookie常常用于允许返回用户已经访问过的网站,从而可在一定程度用户化信息。有些网站使用加密套接字协议层(SSL)来加密cookie携带的信息。
    两者区别:
    1、cookie数据存放在客户的浏览器上,
    session数据放在服务器上
    2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
    考虑到安全应当使用session
    3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
    考虑到减轻服务器性能方面,应当使用COOKIE
    4、单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的COOKIE不能大于3K。

    COOKIE安全与防护

    20170528 21:08:18

    阅读数:3015

         

     

    摘要

    关键词

    一、 引言 

    二、 COOKIE概述 

    三、 COOKIE安全分析 

    四、 COOKIE惯性思维 

    五、 COOKIE防护 

    六、 总结 

    七、 参考文献: 15

     

    摘要:Cookie是一小段文本信息,只能保存字符串,伴随着用户请求和页面在Web服务器和浏览器间传递,用来保持Web中的回话状态和缓存信息,记录用户的状态。Cookie的使用方便了人们的网上生活,但同时对用户的许多隐私信息构成了威胁。本文介绍了Cookie技术的相关概念、应用领域及基本特征,说明了Cookie的工作原理和Cookie结构;其次讲述了常见的Cookie技术的漏洞,最后介绍了针对Cookie技术的安全防护措。

     

    关键词:CookieHTTP无状态,保持securehttponly,跨站攻击

     

    一、引言

    Cookie技术产生于HTTP协议在互联网的急速发展。由于因特网的发展,人们对于web服务的便利性和友好性有了更大的需求。在复杂的互联网交互活动中,希望服务器能够记录活动的状态,识别不同用户的活动记录。使得用户在本次访问服务器中,服务器能有上次用户访问时的种种记录和资源。

    但是,HTTP协议是无状态的,即协议对于事物处理没有记忆能力。这种情况下,如果后续事物处理需要之前的信息,就必须要重传。举个例子,你再淘宝里面精挑细选了50件产品放在了购物车中,但是不小心关闭了淘宝界面。这时再登录时,我们肯定希望购物车中还是有这50件产品。

    http协议是不能记住我们上次把50件产品加入购物车的。在客户端和服务器进行这种动态交互的web 应用的大量出现后,http的无状态特性就极大的阻碍了这些交互应用的发展。因此,在这种需求下就推出了各种能够保存web服务器状态的技术手段,两种用于保持HTTP连接状态的技术就应运产生了。一种是session技术,另一种就是cookie技术。

    Cookie在英文中意为“小饼干,小甜品”,顾名思义就是形容cookie容量小,cookie是一小段文本文件,只含有字符。

    Cookie就是服务器给用户颁布的一个状态值,并且保存在客户端或浏览器。只要在cookie的有效期内,用户再次访问该服务器时,浏览器会检查本地的cookies,并且会自动将cookie加在请求头部中一起发送给服务器,服务器通过识别该cookie来辨别用户身份,并且将用户在服务器中的资源提供给用户。

    过度泛化的隐私问 ,正在影响互联网发展的大方向。 央视3·15晚会出人意料地没有谈食物、水和空气,而选择曝光公众缺乏意识的cookie问题,这一下子让还处在马斯洛需求层面较低层级的中国人民在对自身安全的追求上开始更加忧虑。Cookie 安全就不止为安全领域的人员所熟知,而成为一个热门的全民话题。

     

     

    二、COOKIE概述

    1. Cookie属性

    1)Cookie名称Cookie名称必须使用只能用在URL中的字符,一般用字母及数字,不能包含特殊字符,如有特殊字符想要转码。如js操作cookie的时候可以使用escape()对名称转码。

     

    2)CookieCookie值同理Cookie的名称,可以进行转码和加密

     

    3)Expires,过期日期,一个GMT格式的时间,当过了这个日期之后,浏览器就会将这个Cookie删除掉,当不设置这个的时候,Cookie在浏览器关闭后消失。

     

    4)Path,一个路径,在这个路径下面的页面才可以访问该Cookie,一般设为“/”,以表示同一个站点的所有页面都可以访问这个Cookie

     

    5Domain,子域,指定在该子域下才可以访问Cookie,例如要让Cookiea.test.com下可以访问,但在b.test.com下不能访问,则可将domain设置成a.test.com

     

    6)Secure,安全性,指定Cookie是否只能通过https协议访问,一般的Cookie使用HTTP协议既可访问,如果设置了Secure(没有值),则只有当使用https协议连接时cookie才可以被页面访问。

     

    7)HttpOnly,如果在Cookie中设置了"HttpOnly"属性,那么通过程序(JS脚本、Applet)将无法读取到Cookie信息。

     

    2. Cookie工作工程

     

    具体工作过程描述如下:

    1We b 客户端通过浏览器向 We b 服务器发送连接请求, 通过 HTTP 报文请求行中的 URL 打开某一 Web页面。

    2We b 服务器接收到请求后,根据用户端提供的信息产生一个 Set-Cookies Head

    3)将生成的Set-Cookies Header通过 Response Header存放在 HTTP 报文中回传给 Web 客户端,建立一次会话连接。

        4We b 客户端收到 H T T P 应答报文后,如果要继续已建立的这次会话,则将 Cookies 的内容从 HTTP 报文中取出,形成一个 Cookies文本文件储存在客户端计算机的硬盘中或保存 在客户端计算机的内存中。

    5)当 We b 客户端再次向 We b 服务器发送连接请求时, We b 浏览器首先根据要访问站点的 U R L 在本地计算机上寻找对应的 Cookies文本文件或在本地计算机的内存中寻找对应的 Cookies 内容。如果找到,则将此 Cookies 内容存放在 HTTP 请求报文中发给 Web 服务器。

    6Web 服务器接收到包含 Co okies 内容的 HT TP 请求后, 检索其 Cookies 中与用户有关的信息,并根据检索结果生成一个客户端所请求的页面应答传递给客户端 。

    3. Cookie 特点

     由服务器写入,保存在本地,传输于HTTP头部

     Cookie由三元组【名字name,域名domain,路径path】确定,会出现重名,即cookie不唯一

     Cookie 存在同源策略,仅以domainpath 作为同源限制,不区分端口和协议。Cookie 的同源策略类似于Web的同源策略(sop),但比其安全性高。Web的同源策略以协议,端口,域名作为同源限制。Web 同源策略的目的是为了保护用户信息的安全,防止恶意的网站窃取数据。Cookie的同源策略目的也是为了安全性。

    l Domain向上通配:一个页面可以为本域和任何父域设置cookie

    Eg:  服务器写入cookie时,当页面为 http://www.bank.com cookieX时:

    Ø Set-Cookie: user1=aaa; domain=.bank.com; path=/;      -------> domain不配陪  

    Ø Set-Cookie: user2=bbb; domain=www.bank.com; path=/;  -------> domain匹配

    Ø Set-Cookie: user3=ccc; domain=.www.bank.com; path=/;  ------->domian匹配

    Ø Set-Cookie: user4=ddd; domain=other.bank.compath=/; ------->domain不匹配

     

    l Path向下通配:一个页面可以为本路径和任何子路径设置cookie

    Eg:  服务器写入cookie时,当页面为 http://www.bank.com path/hello时:

    Ø Set-Cookie: user1=aaa; domain=www.bank.com; path=/;         -------> path不配陪  

    Ø Set-Cookie: user1=aaa; domain=www.bank.com; path=/hello;      -------> path配陪  

    Ø Set-Cookie: user2=bbb; domain=www.bank.com; path=/hello/world; -------> path匹配

     

    4. Cookie版本

    主流有两个版本 

    版本 0 由 Netscape 公司制定的,也被几乎所有的浏览器 支持。Java中为了保持兼容性 目前只支持到版本 0, Cookie内容中不能空格,方括号,圆括号,等于号(=),逗号,双引号, 斜杠,问号,符号,冒号,分号。

    版本 1 根据 R F C 2109 文档制定的。放宽了很多限制。 版本 中所限制的字符都可以使用。但为了保持兼容性,程序 开发者都会尽量避免使用这些特殊字符

     

    三、COOKIE安全分析

    1. Cookie 泄漏(欺骗)

        如下图,cookiehttp协议中是明文传输的,并且直接附在http报文的前面,所以只要在网络中加个嗅探工具,获取http包,就可以分析并获得cookie的值。

        此时,当我们获取到别人的cookie的值,就产生了一种攻击漏洞,即cookie欺骗。我们将获取到的cookie值加在http请求前,服务器就会把我们当作是该cookie的用户,我们就成功的冒充了其他用户,可以使用其他用户在服务器的资源等。

     

     

    既然明文cookie不安全,那么我们就使用加密传输cookie。这样即使数据包被截取,cookie也不会被泄漏。把http协议改成加密的https,如下图:

     

    但是攻击者依旧可以通过社会工程学等等诱使用户主动访问在http协议下的服务器,从而获取明文cookie,因此加密后的cookie依旧不安全。如下图:用户先是通过HTTPS访问了bank.com服务器。而后,攻击者在网站weibo.com中插入js代码,该代码中img指向访问http下的non.bank.com服务器。攻击者通过社会工程学诱使用户去访问这条链接,当用户一点击该链接,就自动访问non.bank.com,让服务器给用户一个cookie。因为cookie同源策略中domain是向上匹配的。所以服务器会将该用户在bank.comcookie 也分配给non.bank.com。这时就得到了bank.com给用户的cookie就被泄漏了。

     

    HTTPS也不安全的情况下,我们考虑cookie 自身属性。

    Cookie 中有个属性secure,当该属性设置为true时,表示创建的 Cookie 会被以安全的形式向服务器传输,也就是只能在 HTTPS 连接中被浏览器传递到服务器端进行会话验证,如果是 HTTP 连接则不会传递该cookie信息,所以不会被窃取到Cookie 的具体内容。就是只允许在加密的情况下将cookie加在数据包请求头部,防止cookie被带出来。

    另一个是 HttpOnly属性,如果在Cookie中设置了"HttpOnly"属性,那么通过程序(JS脚本、Applet)将无法读取到Cookie信息,这样能有效的防止XSS攻击。

    secure属性是防止信息在传递的过程中被监听捕获后信息泄漏,HttpOnly属性的目的是防止程序获取cookie后进行攻击。

    但是这两个属性并不能解决cookie在本机出现的信息泄漏的问题(FireFox的插件FireBug能直接看到cookie的相关信息)

     

    2. Cookie 注入\覆盖

    Cookie 欺骗是攻击者登录的是受害者的身份。而Cookie 注入是认证为攻击者的攻击方式,受害者登录的是攻击者的身份。

    Cookie注入简单来说就是利用Cookie而发起的注入攻击。从本质上来讲,Cookie注入与一般的注入(例如,传统的SQL注入)并无不同,两者都是针对数据库的注入,只是表现形式上略有不同。Cookie 注入就是将提交的参数以cookie的方式提交。而一般的注入是使用getpost方式提交。Get方式提交就是直接在网址之后加需注入的内容,而post 是经过表单提取的方式。Postget不同就在于,get方式我们可以在ie地址栏看到我们提交的参数,而post 不能。

     ASP 脚本中 Request 对象是用户与网站数据交互之中非常关键的点,Request 对象 获取客户端提交数据常用的 GET 和 POST 两种方式,同时可以不通过集合来获取数据,即直接用“request(name)”等。

    相对postget方式注入来说,cookie注入就要稍微繁琐,要进行cookie注入,我们首先就要修改cookie,这里就需要使用到Javascript语言。另外cookie注入的形成有两个必须条件: 条件1是程序对getpost方式提交的数据进行了过滤,但未对cookie方式提交的数据库进行过滤;条件2是在条件1的基础上还需要程序对提交数据获取方式是直接request("xxx")的方式,未指明使用request对象的具体方法进行获取。

        Cookie 注入 从技术手段来讲,比较有技术含量。如果只是简单的注入,例如,让受害者登录攻击者的邮箱,那么只要攻击者查看邮箱的信件就会发现异常,容易被发现。那么这种情况下,就需要进行精准的攻击。例如攻击”一个网页“中的一个组成界面,替换“一次http行为”中的某些阶段行为。精确攻击将在下文cookie惯性思维中细讲,这里不多做描述。精确攻击就很难被发现,极为隐蔽。

        如下图,用户先是用HTTPS的方式访问bank.com,而后,我们让用户使用HTTP的方式来访问non.bank.com,这时我们能得到一个cookie ,攻击者此时就可以将该明文cookie 替换成攻击者attackcookie。在domain 向上匹配的同源策略下和cookie 优先级的情况下,访问non.bank.com时得到的cookie 会更优先,这时用户通过https访问bank.com时,就会使用attack优先级更高的cookie

     

      

    四、COOKIE惯性思维

    .盲目认为cooke可信,没有做复杂验证

    cookie来自服务器还是第三方?我们通常认为cookie服务器颁发的,所以很可能认为cookie是安全的。但是cookie是从客户端向服务器提交的,所以严格来说,服务器接收到的cookie很可能是来自第三方的。攻击者窃取了他人的cookie然后提交给主机的。我们如果能够进行复杂的验证,例如过滤等,就会有效降低cookie的威胁。但是这个验证,本身存在一定的技术困难。

     

    .cookie 是唯一的?

    事实是cookie不唯一,因此cookie会存在重名的情况。同名的cookie处理有歧义,因为RFC标准中对于cookie同名的处理就是不严格的,所以存在cookie同名上的安全隐患。

    浏览器的cookie优先级排序:

    Ø path更长;

    Ø path相同,更早创建(删除对方的cookie

    由于存在重名cookie的优先级,那么攻击者就可能利用cookie的同源策略来制造同名cookie,并且利用浏览器对于重名cookie的优先级的定义,从而使重名cookie中,攻击者所有的哪个cookie优先级更高。

    .一个HTTP页面   NO!!!

    我们经常认为一个网页界面是一个http页面,但其实,那是由很多个界面叠加组成的。这些不同的界面可能由于path 不同而拥有不同的cookie。此时,就可能形成了一种精准攻击,针对于某个界面进行cookie注入,而其他界面仍然是正常界面。那么这种情景下,用户是很难发现异常的。如下图:

     

    .一次HTTP操作?   NO!!!

        如下图,举个例子,我们在网上进行支付行为,我们外表看来的一次支付行为可能分为很多个HTTP操作,其中的跳转可能就不止一个。而每个不同的跳转和页面都会有不同的cookie。因此,这也会造成精准攻击。攻击者用自己的cookie 替换掉中间两个跳转,攻击者就可以从受害者卡里划走一笔钱到自己的账户中。同样,这种精准攻击也是极其难发现的。因为可能上面的登录名还是受害者的,但是钱却不知不觉的转到攻击者账户区。

     

     

    .精确清理cookie

    实际上,我们并不经常清理cookie,或者说,cookie难以清理。由于cookie 的便利性和我们对于本地cookie的不熟悉性,我们并不能精准的清理cookie,这就会导致泄漏的cookie可能一支被利用,从而形成持久化攻击。

    五、COOKIE防护

    .重名检查

    但是这个有难度。因为cookie RFC定义的,而修改RFC中的cookie 标准是有困难的。

     

    .清理cookie

    Cookie是由于便利性而存在的,那么总是在使用后清除cookie,其实就达不到cookie的便利作用了。

     

    .不在 Cookies 中存放敏感信息。

    这是一个 理想化的思路,其实质就是抛弃 Cookies,但明显违背安全平台的设计思路,不能因为 Cookies 可能存在欺骗攻击而废止它的便利性。

     

    严格保护数据库不泄露。

    保护数据库不泄露不仅仅是 Cookies 防御技术中需要注意的安全措施,也是其它安全措施中必须注意的重点。在数据库足够安全的情况下,即使产生cookie注入,cookie 注入的危害也会被极大的减小。这时从cookie 造成损失的后果上来减小cookie的危害。可以对数据库进行加密等等。

     

    严格堵住脚本系统中可能提交盗取 Cookies 的代码,把好验证关。

    为了实现正常的用户交互功能,一般的脚 本系统都有发帖、留言、讨论、评论等功能,这些功能都允许用户提交或者上传一些自己的代码、程序,这样的代码有可能就是盗取 Cookies 的代码,因此,出于安全考虑,管理员要对这些代码进行严格审查,或者使用过滤插件、过滤软件过滤掉用户提交的一些非法信息来减小恶意代码的运行。

     

    使用 Session 和 Cookies 双重验证。

    一般给予cookie 的系统会在cookie 中存储两个或多个变量,常见的是 username ( 用户名和 userlevel ( 用户等级,对于安全程序员来说, 既然cookie 是不安全的,而又必须把用户登录信息 存储下来以方便交互,就应该增加存储在 session

    cookie保存在客户端,容易被篡改,session保存在服务器端,用户难以修改,当用户与网站的交互结束时,Session 的生命周期随即结束,从这个层面上说,session类似一次一密,因此Session 的安全性比 Cookies 要高。所以两者结合适用,比较安全。

    该方法技术实现为:在 cookie 中存储用户名和密码,当用户访问一个页面是先读取 session,如果有内容则以 session 为准,否则读取 cookie,按照 cookie 中提供的用户名和密码进行 “不透明”的登录一次,用以判断 cookie 中的内容是否合法,若合法再进而存入 session 中。

     

    .加防篡改验证码,加个登录随机验证码

    在用户登录时,多增加一层验证,需要验证两个 cookie,一个验证的是用户名,一个验证的是随机数,而这个随机数是系统自动产生的,某时间段内相对为唯一的 “验证码”。

    这种方法的技术实现方式为:修改用户登录页面代码,当用户名和密码通过系统正确验证之后,从数据库中取出对应这个用户名的 randnum,并写入 cookie,也就是说此时类似于产生了两个 cookie

    这样以来,就算是攻击者通过不法手段伪造了管理员的用户名,但这个随机产生的验证码就很难猜到了,而且这个随机数是不停变化的,就能在一定程度上增加了cookie攻击的难度。

    .运用HSTS平台,对于特定的域名强制进行HTTPS访问。

     

     

    六、总结

        Cookie web中是广泛使用的,它给我们的上网带来了极大的便利性,例如,当我们浏览过一次优酷,那么这条记录就会被保存在浏览器中。当然,有利也有弊,我们的上网安全也因之收到威胁。Cookie 可以也能被利用来进行XSSCSRF等跨站攻击,它本身不是病毒也不是木马,对于主机本身不会产生威胁,但是容易被利用来进行攻击。对于cookie 安全,本文给出了一些安全分析,也给出了对应的防护策略。但我仍然认为,最佳的防御应该是优化网站本身,设置复杂而周全的规则策略使攻击者不能获取到有效信息,从而来堵住cookie漏洞,同时也经常给站点打补丁,进行分析。在大局势下,安全总是相对的,不安全却是绝对的。我们所能采取的措施就是 时刻保持警惕,不断了解最新的技术和安全报告, 一旦发现问题就迅速解决,尽可能地不给居心不良 者以可乘之机。

    cookie安全加密

    cookie一般情况下用于记录用户登录状态的,比如userid,千万不要记录密码,由于cookie是存储在客户端的,所以cookie很容易被人劫持修改。比如登录成功后在客户端写入cookie('userid') = 1,在服务器读取cookie userid的值,如果userid在数据库用户表中可以找到则证明当前用户userid1且合法登录,显然这样是不可行的。如果用户自行修改userid2或者其它用户的userid,那么服务器就认为当前合法登录的用户userid2,这样用户就不需要知道userid2的密码就合法登录了,修改cookie可以通过直接修改浏览器cookie文件或者通过javascript修改cookie值达到欺骗服务器的目的,所以需要对存储在客户端的cookie要进行加密。

    提到加密可能大家首先想到MD5,通过MD5算法对userid进行加密然后存储在客户端cookie中,由于MD5是不逆的,所以服务器在收到cookie(‘userid’)的时候也不可以解密,那么服务器应该在用户数据表中添加一个字段专门存储MD5加密后的userid,然后通过SQL查询到userid。虽然MD5是不可逆的,但是MD5还是可以暴力破解的,尤其是对于userid这种纯数字的,比如userid12345MD5加密后的值为827CCB0EEA8A706C4C34A16891F84E7B,我们把加密后的值放到http://www.cmd5.com/中解密看看,不到2秒结果12345就出来了。如果用户知道了你的userid只是经过了一次MD5加密,那么用户随便对一个已经存在的userid进行MD5加密,然后伪造cookie(‘userid’),此时服务器会认为用户为合法用户了,获取了其它用户的权限。那么我的建议是对userid进行两次MD5加密或者加一个复杂的key,因为越复杂MD5暴力破解就越慢,如果暴力破解需要2万年那就没有任何意义了。

    上面提到的使用已知加密算法(如MD5)还是有漏洞的,最好的是自己编写一套加密算法,这样用户就无法伪造其它用户了。当然了不是每个人都有编写加密算法的能力,我们还是可以用DES加密算的,DES加密需要密钥,而且还可以解密,当用户登录成功后使用预先设置好的密钥对userid进行DES加密,然后存储到客户端cookie中。服务器读取cookie(‘userid’)时在服务器端使用相同的密钥进行DES解密,然后取得userid,由于用户不知道密钥(密钥存储在服务器上),所以用户也无法伪造成其它用户了。我的项目用的是Thinkphp框架,采用的Thinkphp自带的加密算法。此外为了防止用户通过javascript修改伪造cookie,可以设置cookiehttponly属性为true,这样cookie就只能通过服务器读取不能通过javascript读取了,也防止了恶意用户通过XSS跨站脚本攻击获取其它用户的cookie信息,一定程度上提示了安全性。

    别以为这样就万事大吉了,既然不能伪造cookie了,但还是可以通过抓包获取其它用户的cookie的,毕竟cookie是在网络中传输的,那怎么办呢?可以用https协议代替普通http协议,这样恶意用户抓到的报文已经是密文了,也就不知道cookie了。

    简单易用的cookie加密方法

    在保存用户信息阶段,主要的工作是对用户的信息进行加密并保存到客户端。加密用户的信息是较为繁琐的,大致上可分为以下几个步聚:
    ① 得到用户名、经MD5加密后的用户密码、cookie有效时间(本文设置的是两星期,可根据自己需要修改)
    ② 自定义的一个webKey,这个Key是我们为自己的网站定义的一个字符串常量,这个可根据自己需要随意设置
    ③ 将上两步得到的四个值得新连接成一个新的字符串,再进行MD5加密,这样就得到了一个MD5明文字符串
    ④ 将用户名、cookie有效时间、MD5明文字符串使用“:”间隔连接起来,再对这个连接后的新字符串进行Base64编码
     

    ⑤ 设置一个cookieName,将cookieName和上一步产生的Base64编码写入到客户端。


     

    其实弄明白了保存原理,读取及校验原理就很容易做了。读取和检验可以分为下面几个步骤:
    ① 根据设置的cookieName,得到cookieValue,如果值为空,就不帮用户进行自动登陆;否则执行读取方法
    ② 将cookieValue进行Base64解码,将取得的字符串以split(“:”)进行拆分,得到一个String数组cookieValues(此操作与保存阶段的第4步正好相反),这一步将得到三个值:
    cookieValues[0] ---- 用户名
    cookieValues[1] ---- cookie有效时间
    cookieValues[2] ---- MD5明文字符串
    ③ 判断cookieValues的长度是否为3,如果不为3则进行错误处理。
    ④ 如果长度等于3,取出第二个,即cookieValues[1],此时将会得到有效时间(long型),将有效时间与服务器系统当前时间比较,如果小于当前时间,则说明cookie过期,进行错误处理。
    ⑤ 如果cookie没有过期,就取cookieValues[0],这样就可以得到用户名了,然后去数据库按用户名查找用户。
    ⑥ 如果上一步返回为空,进行错误处理。如果不为空,那么将会得到一个已经封装好用户信息的User实例对象user
    ⑦ 取出实例对象user的用户名、密码、cookie有效时间(即cookieValues[1])、webKey,然后将四个值连接起来,然后进行MD5加密,这样做也会得到一个MD5明文字符串(此操作与保存阶段的第3步类似)
    ⑧ 将上一步得到MD5明文与cookieValues[2]进行equals比较,如果是false,进行错误处理;如果是true,则将user对象添加到session中,帮助用户完成自动登陆

     


     


     

    [csharp] view plain copy

     

    1. using System;  
    2. using System.Web;  
    3.   
    4. namespace Mvc4WebBootstrap1.Models  
    5. {  
    6.     public class CookieController  
    7.     {  
    8.         private static String mystatickey = "Dazdingo";  
    9.         public static void CookieSetter(String username, int valuetime)  
    10.         {  
    11.             valuetime = 30;//暂时指定有效期为30  
    12.             String todaystr = DateTime.Now.ToString("yyyyMMdd");  
    13.             String md5str = username + valuetime + todaystr + mystatickey;  
    14.             md5str = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(md5str, "MD5");  
    15.             String cookiestr = username + ":" + valuetime + ":" + todaystr + ":" + md5str;  
    16.             HttpCookie cookie = new HttpCookie(cookiestr);  
    17.             cookie.Name = "myweb.com";  
    18.             System.Web.HttpContext.Current.Response.Cookies.Add(cookie);  
    19.         }  
    20.         public static int CookieReader(String username)//0=no cookie 1=success 2=somewrong  
    21.         {  
    22.             username = "";  
    23.             if (System.Web.HttpContext.Current.Request.Cookies["MyCook"] == nullreturn 0;  
    24.             String cookiestr = System.Web.HttpContext.Current.Request.Cookies["myweb.com"].Value;  
    25.             String[] sarr = cookiestr.Split(new char[]{':'});  
    26.             username = sarr[0];  
    27.             String todaystr = DateTime.Now.ToString("yyyyMMdd");  
    28.             int cookiedate = int.Parse(sarr[2]);  
    29.             int todaydate = int.Parse(todaystr);  
    30.             int valuetime = int.Parse(sarr[1]);  
    31.             if ((todaydate - cookiedate) < valuetime) return 2;  
    32.             String md5str = sarr[0] + sarr[1] + sarr[2] + mystatickey;  
    33.             md5str = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(md5str, "MD5");  
    34.             if (md5str == sarr[3]) return 1;  
    35.             return 2;  
    36.         }  
    37.     }  
    38. }  

    JavaScript Cookies

    • JS Timing
    • JS

    cookie 用来识别用户。

    实例

    创建一个欢迎 cookie

    利用用户在提示框中输入的数据创建一个 JavaScript Cookie,当该用户再次访问该页面时,根据 cookie 中的信息发出欢迎信息。

    什么是cookie?

    cookie 是存储于访问者的计算机中的变量。每当同一台计算机通过浏览器请求某个页面时,就会发送这个 cookie。你可以使用 JavaScript 来创建和取回 cookie 的值。

    有关cookie的例子:

    名字 cookie

    当访问者首次访问页面时,他或她也许会填写他/她们的名字。名字会存储于 cookie 中。当访问者再次访问网站时,他们会收到类似 "Welcome John Doe!" 的欢迎词。而名字则是从 cookie 中取回的。

    密码 cookie

    当访问者首次访问页面时,他或她也许会填写他/她们的密码。密码也可被存储于 cookie 中。当他们再次访问网站时,密码就会从 cookie 中取回。

    日期 cookie

    当访问者首次访问你的网站时,当前的日期可存储于 cookie 中。当他们再次访问网站时,他们会收到类似这样的一条消息:"Your last visit was on Tuesday August 11, 2005!"。日期也是从 cookie 中取回的。

    创建和存储 cookie

    在这个例子中我们要创建一个存储访问者名字的 cookie。当访问者首次访问网站时,他们会被要求填写姓名。名字会存储于 cookie 中。当访问者再次访问网站时,他们就会收到欢迎词。

    首先,我们会创建一个可在 cookie 变量中存储访问者姓名的函数:

    function setCookie(c_name,value,expiredays)
    {
    var exdate=new Date()
    exdate.setDate(exdate.getDate()+expiredays)
    document.cookie=c_name+ "=" +escape(value)+
    ((expiredays==null) ? "" : ";expires="+exdate.toGMTString())
    }

    上面这个函数中的参数存有 cookie 的名称、值以及过期天数。

    在上面的函数中,我们首先将天数转换为有效的日期,然后,我们将 cookie 名称、值及其过期日期存入 document.cookie 对象。

    之后,我们要创建另一个函数来检查是否已设置 cookie

    function getCookie(c_name)
    {
    if (document.cookie.length>0)
      {
      c_start=document.cookie.indexOf(c_name + "=")
      if (c_start!=-1)
        { 
        c_start=c_start + c_name.length+1 
        c_end=document.cookie.indexOf(";",c_start)
        if (c_end==-1) c_end=document.cookie.length
        return unescape(document.cookie.substring(c_start,c_end))
        } 
      }
    return ""
    }

    上面的函数首先会检查 document.cookie 对象中是否存有 cookie。假如 document.cookie 对象存有某些 cookie,那么会继续检查我们指定的 cookie 是否已储存。如果找到了我们要的 cookie,就返回值,否则返回空字符串。

    最后,我们要创建一个函数,这个函数的作用是:如果 cookie 已设置,则显示欢迎词,否则显示提示框来要求用户输入名字。

    function checkCookie()
    {
    username=getCookie('username')
    if (username!=null && username!="")
      {alert('Welcome again '+username+'!')}
    else 
      {
      username=prompt('Please enter your name:',"")
      if (username!=null && username!="")
        {
        setCookie('username',username,365)
        }
      }
    }

    这是所有的代码:

     

    cookie的存储和获取

    在做用户登录时经常会用到cookie,如何将用户名和密码保存至cookie中呢?如何获取cookie中的数据呢?

    一、用jquery.cookie.js保存数据

      在页面内引入jQuery.cookie.js,如果在页面上有 记住密码 这个单选框,判断checked是否为true

      -----如果是,获取用户名和密码框的值,$.cookie(id,对应id存储的值,{expires:存储的期限})

    1     if($("#rememberme").prop("checked") == true) {
    2             var userName = $("#user").val();
    3             var passWord = $("#psw").val();
    4             $.cookie("rememberme", "true", {expires: 7}); // 存储一个带7天期限的 cookie
    5             $.cookie("user", userName, {expires: 7}); 
    6             $.cookie("psw", passWord, {expires: 7}); 
    7         }

      -----如果没有勾选 记住密码,设置存储期限为-1即可

      这样提交之后cookie中就会存储这些数据啦!

      你可以打开控制台 选中Application-->Storage-->Cookie进行查看面试总结(1)---7.28_第3张图片

    二、获取cookie中的数据

      方法 $.cookie(name)

      例:$.cookie('rememberme')$.cookie('user')$.cookie('psw')

      如果cookie设置的期限还没过,我们就需要将cookie中的数据显示到页面上,先判断cookie中的rememberme是否为true,如果为true,将cookie中的值赋给对应的文本框,勾选记住密码单选框。

    1 if($.cookie('rememberme')==='true'){
    2         $("#user").val($.cookie('user'));
    3         $("#psw").val($.cookie('psw'));
    4         $("#rememberme").prop('checked',true);
    5 }

     

    前端性能优化之CSS详细解读

    避免使用@import

    外部的CSS文件中使用@import会使得页面在加载时增加额外的延迟。

    一个CSS文件first.css包含了以下内容:@import url(“second.css”)。浏览器先把first.css下载、解析和执行后,发现及处理第二个文件second.css。简单的 解决方法是使用标记来替代@import,并行下载CSS文件,从而加快页面加载速度.

     

    避免AlphaImageLoader滤镜

    什么是AlphaImageLoaderIE独有属性,用于修正7.0以下版本中显示PNG图片的半透明效果。

    问题:浏览器加载图片时它会终止内容的呈现并且冻结浏览器,在每一个元素(不仅仅是图片)它都会运算一次,增加了内存开支。

    解决方案:1PNG8格式来代替,这种格式能在IE中很好地工作。

                  2、确实需要使用AlphaImageLoader,使用下划线_filter,使IE7以上版本的用户无效。

     

    避免CSS表达式

    例:background-color: expression((new Date()).getHours()%2?"#FFFFFF": "#000000" );

    CSS表达式是动态设置CSS属性的强大(但危险)方法。Internet Explorer从第5个版本开始支持CSS表达式。

    问题:在页面显示和缩放、滚动、移动鼠标时都会要 重新计算一次。给CSS表达式增加一个计数器可以跟踪表达式的计算频率。在页面中随便移动鼠标都可以轻松达到10000次以上的计算量。

    解决:减少CSS 达式计算次数的方法就是使用一次性的表达式,它在第一次运行时将结果赋给指定的样式属性,并用这个属性来代替CSS表达式。如果样式属性必须在页面周期内 动态地改变,使用事件句柄来代替CSS表达式是一个可行办法。如果必须使用CSS表达式,一定要记住它们要计算成千上万次并且可能会对你页面的性能产生影 响。

     

    避免通配选择器

    在学习CSS初期,我们在做网页的时候经常会使用*{margin0padding0;},以此来消除标签的默认布局和不同浏览器对于同一个标签的渲染。

    而我们有时候会看到reset的写法。body,p,h1,h2,h3,h4,h5,input,select,textarea,table{margin0padding0;}

    这些人为什么要这么写,下面的内容我们会得到答案

     

    例:#header > a {font-weight:blod;}

     

    CSS选择器是从右到左进行规则匹配。所以在浏览器中这条语句实现为:

     

    浏览器遍历页面中所有的a元素——>其父元素的id是否为header

     

     

     

    例:#header  a {font-weight:blod;}

     

    这个例子比上一个消耗的时间更多

     

    遍历页面中所有a元素——>向其上级遍历直到根节点

     

     

    例:.selected * {color: red;}

     

    匹配文档中所有的元素——>分别向上逐级匹配classselected的元素,直到文档的根节点

     

     

    所以我们应该避免使用通配选择器。

     

    移除无匹配的样式

    第一,删除无用的样式后可以缩减样式文件的体积,加快资源下载速度;

    第二,对于浏览器而言,所有的样式规则的都会被解析后索引起来,即使是当前页面无匹配的规则。移除无匹配的规则,减少索引项,加快浏览器查找速度;

     

    避免单规则的属性选择器

    浏览器匹配所有的元素——>检查是否有href属性并且herf属性值等于”#index”——>分别向上逐级匹配classselected的元素,直到文档的根节点。

     

    避免类正则的属性选择器

    正则表达式匹配会比基于类别的匹配会慢很多。大部分情况下我们应尽量避免使用 *= |= ^= $= ~=语法的属性选择器

     

    Web前段优化,提高加载速度 css

    前言: 

    在同样的网络环境下,两个同样能满足你的需求的网站,一个“Duang”的一下就加载出来了,一个纠结了半天才出来,你会选择哪个?研究表明:用户最满意的打开网页时间是2-5秒,如果等待超过10秒,99%的用户会关闭这个网页。也许这样讲,各位还不会有太多感触,接下来我列举一组数据:Google网站访问速度每慢400ms就导致用户搜索请 求下降0.59%;Amazon每增加100ms网站延迟将导致收入下降1%;雅虎如果有400ms延迟会导致流量下降5-9%。网站的加载速度严重影响了用户体验,也决定了这个网站的生死存亡。

    可能有人会说:网站的性能是后端工程师的事情,与前端并无多大关系。我只能说,too young too simple。事实上,只有10%~20%的最终用户响应时间是用在从Web服务器获取HTML文档并传送到浏览器的,那剩余的时间去哪儿了?来瞄一下性能黄金法则

    只有10%~20%的最终用户响应时间花在了下载HTML文档上。其余的80%~90%时间花在了下载页面中的所有组件上。

    接下来我们将研究一下前端攻城狮如何来提高页面的加载速度。

     

    一、减少HTTP请求

    上面说到80%~90%时间花在了下载页面中的所有组件进行的HTTP请求上。因此,改善响应时间最简单的途径就是减少HTTP请求的数量。

    图片地图:

    假设导航栏上有五幅图片,点击每张图片都会进入一个链接,这样五张导航的图片在加载时会产生5个HTTP请求。然而,使用一个图片地图可以提高效率,这样就只需要一个HTTP请求。

    https://images2015.cnblogs.com/blog/861963/201603/861963-20160317164616318-1916945778.png

    服务器端图片地图:将所有点击提交到同一个url,同时提交用户点击的x、y坐标,服务器端根据坐标映射响应

    客户端图片地图:直接将点击映射到操作

    <img src="planets.jpg" border="0" usemap="#planetmap" alt="Planets" />
     
    <map name="planetmap" id="planetmap">
         <area shape="rect" coords="180,139,14" href ="venus.html" alt="Venus" />
         <area shape="rect" coords="129,161,10" href ="mercur.html" alt="Mercury" />
         <area shape="rect" coords="0,0,110,260" href ="sun.html" alt="Sun" />
         <area shape="rect" coords="140,0,110,260" href ="star.html" alt="Sun" />
    map>

    使用图片地图的缺点:指定坐标区域时,矩形或圆形比较容易指定,而其它形状手工指定比较难

    CSS Sprites

    CSS Sprites直译过来就是CSS精灵,但是这种翻译显然是不够的,其实就是通过将多个图片融合到一副图里面,然后通过CSS的一些技术布局到网页上。特别是图片特别多的网站,如果能用css sprites降低图片数量,带来的将是速度的提升。

    https://images2015.cnblogs.com/blog/861963/201603/861963-20160317170620553-1120134760.png

    <div>
        <span id="image1" class="nav">span>
        <span id="image2" class="nav">span>
        <span id="image3" class="nav">span>
        <span id="image4" class="nav">span>
        <span id="image5" class="nav">span>
    div>

    .nav {
        width: 50px;
        height: 50px;
        display: inline-block;
        border: 1px solid #000;
        background-image: url('E:/1.png');
    }
    #image1 {
            background-position: 0 0;
    }
    #image2 {
            background-position: -95px 0;
    }
    #image3 {
            background-position: -185px 0;
    }
    #image4 {
            background-position: -275px 0;
    }
    #image5 {
            background-position: -366px -3px;
    }

    运行结果:

    https://images2015.cnblogs.com/blog/861963/201603/861963-20160317171840287-908391981.png

    PS:使用CSS Sprites还有可能降低下载量,可能大家会认为合并后的图片会比分离图片的总和要大,因为还有可能会附加空白区域。实际上,合并后的图片会比分离的图片总和要小,因为它降低了图片自身的开销,譬如颜色表、格式信息等。

    字体图标

    在可以大量使用字体图标的地方我们可以尽可能使用字体图标,字体图标可以减少很多图片的使用,从而减少http请求,字体图标还可以通过CSS来设置颜色、大小等样式,何乐而不为。

    合并脚本 和样式表

    将多个样式表或者脚本文件合并到一个文件中,可以减少HTTP请求的数量从而缩短效应时间。

    然而合并所有文件对许多人尤其是编写模块化代码的人来说是不能忍的,而且合并所有的样式文件或者脚本文件可能会导致在一个页面加载时加载了多于自己所需要的样式或者脚本,对于只访问该网站一个(或几个)页面的人来说反而增加了下载量,所以大家应该自己权衡利弊。

     

    二、使用CDN

    如果应用程序web服务器离用户更近,那么一个HTTP请求的响应时间将缩短。另一方面,如果组件web服务器离用户更近,则多个HTTP请求的响应时间将缩短。

     CDN(内容发布网络)是一组分布在多个不同地理位置的Web服务器,用于更加有效地向用户发布内容。在优化性能时,向特定用户发布内容的服务器的选择基于对网络慕课拥堵的测量。例如,CDN可能选择网络阶跃数最小的服务器,或者具有最短响应时间的服务器。

    CDN还可以进行数据备份、扩展存储能力,进行缓存,同时有助于缓和Web流量峰值压力。

    CDN的缺点:

    1、响应时间可能会受到其他网站流量的影响。CDN服务提供商在其所有客户之间共享Web服务器组。

    2、如果CDN服务质量下降了,那么你的工作质量也将下降

    3、无法直接控制组件服务器

     

    三、添加Expires头

    页面的初次访问者会进行很多HTTP请求,但是通过使用一个长久的Expires头,可以使这些组件被缓存,下次访问的时候,就可以减少不必要的HTPP请求,从而提高加载速度。

    Web服务器通过Expires头告诉客户端可以使用一个组件的当前副本,直到指定的时间为止。例如:

    Expires: Fri, 18 Mar 2016 07:41:53 GMT

    Expires缺点: 它要求服务器和客户端时钟严格同步;过期日期需要经常检查

    HTTP1.1中引入Cache-Control来克服Expires头的限制,使用max-age指定组件被缓存多久。

    Cache-Control: max-age=12345600

    若同时制定Cache-Control和Expires,则max-age将覆盖Expires头

     

    四、压缩组件

    从HTTP1.1开始,Web客户端可以通过HTTP请求中的Accept-Encoding头来表示对压缩的支持

    Accept-Encoding: gzip,deflate

    如果Web服务器看到请求中有这个头,就会使用客户端列出来的方法中的一种来进行压缩。Web服务器通过响应中的Content-Encoding来通知 Web客户端。

    Content-Encoding: gzip

    代理缓存

    当浏览器通过代理来发送请求时,情况会不一样。假设针对某个URL发送到代理的第一个请求来自于一个不支持gzip的浏览器。这是代理的第一个请求,缓存为空。代理将请求转发给服务器。此时响应是未压缩的,代理缓存同时发送给浏览器。现在,假设到达代理的请求是同一个url,来自于一个支持gzip的浏览器。代理会使用缓存中未压缩的内容进行响应,从而失去了压缩的机会。相反,如果第一个浏览器支持gzip,第二个不支持,你们代理缓存中的压缩版本将会提供给后续的浏览器,而不管它们是否支持gzip。

    解决办法:在web服务器的响应中添加vary头Web服务器可以告诉代理根据一个或多个请求头来改变缓存的响应。因为压缩的决定是基于Accept-Encoding请求头的,因此需要在vary响应头中包含Accept-Encoding。

    vary: Accept-Encoding

     

    五、将样式表放在头部

    首先说明一下,将样式表放在头部对于实际页面加载的时间并不能造成太大影响,但是这会减少页面首屏出现的时间,使页面内容逐步呈现,改善用户体验,防止“白屏”。

    我们总是希望页面能够尽快显示内容,为用户提供可视化的回馈,这对网速慢的用户来说是很重要的。

    将样式表放在文档底部会阻止浏览器中的内容逐步出现。为了避免当样式变化时重绘页面元素,浏览器会阻塞内容逐步呈现,造成“白屏”。这源自浏览器的行为:如果样式表仍在加载,构建呈现树就是一种浪费,因为所有样式表加载解析完毕之前务虚会之任何东西

     

    六、将脚本放在底部

    更样式表相同,脚本放在底部对于实际页面加载的时间并不能造成太大影响,但是这会减少页面首屏出现的时间,使页面内容逐步呈现。

    js的下载和执行会阻塞Dom树的构建(严谨地说是中断了Dom树的更新),所以script标签放在首屏范围内的HTML代码段里会截断首屏的内容。

    下载脚本时并行下载是被禁用的——即使使用了不同的主机名,也不会启用其他的下载。因为脚本可能修改页面内容,因此浏览器会等待;另外,也是为了保证脚本能够按照正确的顺序执行,因为后面的脚本可能与前面的脚本存在依赖关系,不按照顺序执行可能会产生错误。

     

    七、避免CSS表达式

    CSS表达式是动态设置CSS属性的一种强大并且危险的方式,它受到了IE5以及之后版本、IE8之前版本的支持。

    p {
        width: expression(func(),document.body.clientWidth > 400 ? "400px" : "auto");
        height: 80px;
        border: 1px solid #f00;
    }

    <p><span>span>p>
    <p><span>span>p>
    <p><span>span>p>
    <p><span>span>p>
    <p><span>span>p>
    <script>
        var n = 0;
        function func() {
            n++;
             alert();
            console.log(n);
        }
    script>

     

    鼠标移动了几次,函数的运行次数轻而易举的达到了几千次,危险性显而易见。

    如何解决:

    一次性表达式:

    p {
        width: expression(func(this));
        height: 80px;
        border: 1px solid #f00;
    }

    <p><span>span>p>
    <p><span>span>p>
    <p><span>span>p>
    <p><span>span>p>
    <p><span>span>p>
    <script>
        var n = 0;
        function func(elem) {
            n++;
            elem.style.width 400 ? '400px' : "auto";
            console.log(n);
        }
    script>

    事件处理机制

    用js事件处理机制来动态改变元素的样式,使函数运行次数在可控范围之内。

     

    八、使用外部的JavaScript和CSS

    内联脚本或者样式可以减少HTTP请求,按理来说可以提高页面加载的速度。然而在实际情况中,当脚本或者样式是从外部引入的文件,浏览器就有可能缓存它们,从而在以后加载的时候能够直接使用缓存,而HTML文档的大小减小,从而提高加载速度。

    影响因素:

    1、每个用户产生的页面浏览量越少,内联脚本和样式的论据越强势。譬如一个用户每个月只访问你的网站一两次,那么这种情况下内联将会更好。而如果该用户能够产生很多页面浏览量,那么缓存的样式和脚本将会极大减少下载的时间,提交页面加载速度。

    2、如果你的网站不同的页面之间使用的组件大致相同,那么使用外部文件可以提高这些组件的重用率。

    加载后下载

    有时候我们希望内联样式和脚本,但又可以为接下来的页面提供外部文件。那么我们可以在页面加载完成止呕动态加载外部组件,以便用户接下来的访问。

     1   function doOnload() {
     2       setTimeout("downloadFile()",1000);
     3   }
     4   
     5   window.onload = doOnload;
     6   
     7   function downloadFile() {
     8       downloadCss("http://abc.com/css/a.css");
     9       downloadJS("http://abc.com/js/a.js");
    10  }
    11  
    12  function downloadCss(url) {
    13      var ele = document.createElement('link');
    14      ele.rel = "stylesheet";
    15      ele.type = "text/css";
    16      ele.href = url;
    17  
    18      document.body.appendChild(ele);
    19  }
    20  
    21  function downloadJS(url) {
    22      var ele = document.createElement('script');
    23      ele.src = url;
    24      document.body.appendChild(ele);
    25  }

    在该页面中,JavaScript和CSS被加载两次(内联和外部)。要使其正常工作,必须处理双重定义。将这些组件放到一个不可见的IFrame中是一个比较好的解决方式。

     

     九、减少DNS查找

    当我们在浏览器的地址栏输入网址(譬如: www.linux178.com) ,然后回车,回车这一瞬间到看到页面到底发生了什么呢?

    域名解析 --> 发起TCP的3次握手 --> 建立TCP连接后发起http请求 --> 服务器响应http请求,浏览器得到html代码 --> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户

    域名解析是页面加载的第一步,那么域名是如何解析的呢?以Chrome为例:

    1.  Chrome浏览器 会首先搜索浏览器自身的DNS缓存(缓存时间比较短,大概只有1分钟,且只能容纳1000条缓存),看自身的缓存中是否有www.linux178.com 对应的条目,而且没有过期,如果有且没有过期则解析到此结束。
     注:我们怎么查看Chrome自身的缓存?可以使用 chrome://net-internals/#dns 来进行查看
     
    2.  如果浏览器自身的缓存里面没有找到对应的条目,那么Chrome会搜索操作系统自身的DNS缓存,如果找到且没有过期则停止搜索解析到此结束.
     注:怎么查看操作系统自身的DNS缓存,以Windows系统为例,可以在命令行下使用 ipconfig /displaydns 来进行查看 
      
    3.  如果在Windows系统的DNS缓存也没有找到,那么尝试读取hosts文件(位于C:\Windows\System32\drivers\etc),看看这里面有没有该域名对应的IP地址,如果有则解析成功。
     
    4.  如果在hosts文件中也没有找到对应的条目,浏览器就会发起一个DNS的系统调用,就会向本地配置的首选DNS服务器(一般是电信运营商提供的,也可以使用像Google提供的DNS服务器)发起域名解析请求(通过的是UDP协议向DNS的53端口发起请求,这个请求是递归的请求,也就是运营商的DNS服务器必须得提供给我们该域名的IP地址),运营商的DNS服务器首先查找自身的缓存,找到对应的条目,且没有过期,则解析成功。如果没有找到对应的条目,则有运营商的DNS代我们的浏览器发起迭代DNS解析请求,它首先是会找根域的DNS的IP地址(这个DNS服务器都内置13台根域的DNS的IP地址),找打根域的DNS地址,就会向其发起请求(请问www.linux178.com这个域名的IP地址是多少啊?),根域发现这是一个顶级域com域的一个域名,于是就告诉运营商的DNS我不知道这个域名的IP地址,但是我知道com域的IP地址,你去找它去,于是运营商的DNS就得到了com域的IP地址,又向com域的IP地址发起了请求(请问www.linux178.com这个域名的IP地址是多少?),com域这台服务器告诉运营商的DNS我不知道www.linux178.com这个域名的IP地址,但是我知道linux178.com这个域的DNS地址,你去找它去,于是运营商的DNS又向linux178.com这个域名的DNS地址(这个一般就是由域名注册商提供的,像万网,新网等)发起请求(请问www.linux178.com这个域名的IP地址是多少?),这个时候linux178.com域的DNS服务器一查,诶,果真在我这里,于是就把找到的结果发送给运营商的DNS服务器,这个时候运营商的DNS服务器就拿到了www.linux178.com这个域名对应的IP地址,并返回给Windows系统内核,内核又把结果返回给浏览器,终于浏览器拿到了www.linux178.com对应的IP地址,该进行一步的动作了。
     
    注:一般情况下是不会进行以下步骤的
     
    如果经过以上的4个步骤,还没有解析成功,那么会进行如下步骤:
    5.  操作系统就会查找NetBIOS name Cache(NetBIOS名称缓存,就存在客户端电脑中的),那这个缓存有什么东西呢?凡是最近一段时间内和我成功通讯的计算机的计算机名和Ip地址,就都会存在这个缓存里面。什么情况下该步能解析成功呢?就是该名称正好是几分钟前和我成功通信过,那么这一步就可以成功解析。
     
    6.  如果第5步也没有成功,那会查询WINS 服务器(是NETBIOS名称和IP地址对应的服务器)
     
    7.  如果第6步也没有查询成功,那么客户端就要进行广播查找
     
    8.  如果第7步也没有成功,那么客户端就读取LMHOSTS文件(和HOSTS文件同一个目录下,写法也一样)
     
    如果第八步还没有解析成功,那么就宣告这次解析失败,那就无法跟目标计算机进行通信。只要这八步中有一步可以解析成功,那就可以成功和目标计算机进行通信。

    DNS也是开销,通常浏览器查找一个给定域名的IP地址要花费20~120毫秒,在完成域名解析之前,浏览器不能从服务器加载到任何东西。那么如何减少域名解析时间,加快页面加载速度呢?

    当客户端DNS缓存(浏览器和操作系统)缓存为空时,DNS查找的数量与要加载的Web页面中唯一主机名的数量相同,包括页面URL、脚本、样式表、图片、Flash对象等的主机名。减少主机名的 数量就可以减少DNS查找的数量。

    减少唯一主机名的数量会潜在减少页面中并行下载的数量(HTTP 1.1规范建议从每个主机名并行下载两个组件,但实际上可以多个),这样减少主机名和并行下载的方案会产生矛盾,需要大家自己权衡。建议将组件放到至少两个但不多于4个主机名下,减少DNS查找的同时也允许高度并行下载。

     

    十、精简JavaScript

    精简

    精简就是从代码中移除不必要的字符以减少文件大小,降低加载的时间。代码精简的时候会移除不必要的空白字符(空格,换行、制表符),这样整个文件的大小就变小了。

    混淆

    混淆是应用在源代码上的另外一种方式,它会移除注释和空白符,同时它还会改写代码。在混淆的时候,函数和变量名将会被转换成更短的字符串,这时代码会更加精炼同时难以阅读。通常这样做是为了增加对代码进行反向工程的难度,这也同时提高了性能。

    缺点:

    混淆本身比较复杂,可能会引入错误。

    需要对不能改变的符号做标记,防止JavaScript符号(譬如关键字、保留字)被修改。

    混淆会使代码难以阅读,这使得在产品环境中调试问题更加困难。

    在以上提到了关于用gzip之类的压缩方式来压缩文件,这边说明一下,就算使用gzip等方式来压缩文件,精简代码依然是有必要的。一般来说,压缩产生的节省是高于精简的,在生产环境中,精简和压缩同时使用能够最大限度的获得更多的节省。

    CSS的精简

    CSS的精简带来的节省一般来说是小于JavaScript精简的,因为CSS中注释和空白相对较少。

    除了移除空白、注释之外,CSS可以通过优化来获得更多的节省:

    合并相同的类;

    移除不使用的类;

    使用缩写,譬如

    .right {
        color: #fff;
     
        padding-top: 0; 
     
        margin: 0 10px;
        
        border: 1px solid #111
    }
    .wrong {
        color: #ffffff;
     
        padding-top: 0px; 
     
        margin-top: 0;
        margin-bottom: 0;
        margin-left: 10px;
        margin-right: 10px;
     
     
        border-color: #111;
        border-width: 1px;
        border-style: solid;
    }

    上面.right是正确的的写法,颜色使用缩写,使用0代替0px,合并可以合并的样式。另外,在精简的时候其实样式最后一行的';'也是可以省略的。

    来看看精简的例子:

    https://images2015.cnblogs.com/blog/861963/201603/861963-20160319121239349-487470248.png

    https://images2015.cnblogs.com/blog/861963/201603/861963-20160319121402396-289570233.png

    https://images2015.cnblogs.com/blog/861963/201603/861963-20160319121409240-1295036550.png

    以上分别是jQuery-2.0.3的学习版(未精简)和精简版,可见精简文件的大小比源文件小了155k,而且,在精简版中jquery还做了混淆,譬如用e代替window等,从而获得最大的节省。

     

    十一、避免重定向

    什么是重定向?

    重定向用于将用户从一个URL重新路由到另一个URL。

    常用重定向的类型

    301:永久重定向,主要用于当网站的域名发生变更之后,告诉搜索引擎域名已经变更了,应该把旧域名的的数据和链接数转移到新域名下,从而不会让网站的排名因域名变更而受到影响。

    302:临时重定向,主要实现post请求后告知浏览器转移到新的URL。

    304:Not Modified,主要用于当浏览器在其缓存中保留了组件的一个副本,同时组件已经过期了,这是浏览器就会生成一个条件GET请求,如果服务器的组件并没有修改过,则会返回304状态码,同时不携带主体,告知浏览器可以重用这个副本,减少响应大小。

    重定向如何损伤性能?

    当页面发生了重定向,就会延迟整个HTML文档的传输。在HTML文档到达之前,页面中不会呈现任何东西,也没有任何组件会被下载。

    来看一个实际例子:对于ASP.NET webform开发来说,对于新手很容易犯一个错误,就是把页面的连接写成服务器控件后台代码里,例如用一个Button控件,在它的后台click事件中写上:Response.Redirect("");然而这个Button的作用只是转移URL,这是非常低效的做法,因为点击Button后,先发送一个Post请求给服务器,服务器处理Response.Redirect("")后就发送一个302响应给浏览器,浏览器再根据响应的URL发送GET请求。正确的做法应该是在html页面直接使用a标签做链接,这样就避免了多余的post和重定向。

    重定向的应用场景

    1. 跟踪内部流量

    重定向经常用于跟踪用户流量的方向,当拥有一个门户主页的时候,同时想对用户离开主页后的流量进行跟踪,这时可以使用重定向。例如: 某网站主页新闻的链接地址http://a.com/r/news,点击该链接将产生301响应,其Location被设置为http://news.a.com。通过分析a.com的web服务器日志可以得知人们离开首页之后的去向。

    我们知道重定向是如何损伤性能的,为了实现更好的效率,可以使用Referer日志来跟踪内部流量去向。每个HTTP请求都有一个Referer表示原始请求页(除了从书签打开或直接键入URL等操作),记录下每个请求的Referer,就避免了向用户发送重定向,从而改善了响应时间。

    2. 跟踪出站流量

    有时链接可能将用户带离你的网站,在这种情况下,使用Referer就不太现实了。

    同样也可以使用重定向来解决跟踪出站流量问题。以百度搜索为例,百度通过将每个链接包装到一个302重定向来解决跟踪的问题,例如搜索关键字“前端性能优化”,搜索结果中的一个URL为https://www.baidu.com/link?url=pDjwTfa0IAf_FRBNlw1qLDtQ27YBujWp9jPN4q0QSJdNtGtDBK3ja3jyyN2CgxR5aTAywG4SI6V1NypkSyLISWjiFuFQDinhpVn4QE-uLGG&wd=&eqid=9c02bd21001c69170000000556ece297,即使搜索结果并没有变,但这个字符串是动态改变的,暂时还不知道这里起到怎样的作用?(个人感觉:字符串中包含了待访问的网址,点击之后会产生302重定向,将页面转到目标页面(待修改,求大神们给我指正))

    除了重定向外,我们还可以选择使用信标(beacon)——一个HTTP请求,其URL中包含有跟踪信息。跟踪信息可以从信标Web服务器的访问日记中提取出来,信标通常是一个1px*1px的透明图片,不过204响应更优秀,因为它更小,从来不被缓存,而且绝不会改变浏览器的状态。

     

    十二、删除重复脚本

    在团队开发一个项目时,由于不同开发者之间都可能会向页面中添加页面或组件,因此可能相同的脚本会被添加多次。

    重复的脚本会造成不必要的HTTP请求(如果没有缓存该脚本的话),并且执行多余的JavaScript浪费时间,还有可能造成错误。

    如何避免重复脚本呢?

    1. 形成良好的脚本组织。重复脚本有可能出现在不同的脚本包含同一段脚本的情况,有些是必要的,但有些却不是必要的,所以需要对脚本进行一个良好的组织。

    2. 实现脚本管理器模块。

    例如:

     1  function insertScript($file) {
     2      if(hadInserted($file)) {
     3           return;
     4       }
     5       exeInsert($file);
     6   
     7       if(hasDependencies($file)) {
     8   
     9           $deps = getDependencies($file);
    10  
    11          foreach ($deps as $script) {
    12              insertScript($script);
    13          }
    14  
    15          echo "";
    16  
    17      }
    18  }

    先检查是否插入过,如果插入过则返回。如果该脚本依赖其它脚本,则被依赖的脚本也会被插入。最后脚本被传送到页面,getVersion会检查脚本并返回追加了对应版本号的文件名,这样如果脚本的版本变化了,那么以前浏览器缓存的就会失效。

     

    十三、配置ETag

    以前浏览器缓存的就会失效。

    什么是ETag?

    实体标签(EntityTag)是唯一标识了一个组件的一个特定版本的字符串,是web服务器用于确认缓存组件的有效性的一种机制,通常可以使用组件的某些属性来构造它。

    条件GET请求

    如果组件过期了,浏览器在重用它之前必须首先检查它是否有效。浏览器将发送一个条件GET请求到服务器,服务器判断缓存还有效,则发送一个304响应,告诉浏览器可以重用缓存组件。

    那么服务器是根据什么判断缓存是否还有效呢?有两种方式:

    ETag(实体标签);

    最新修改日期;

    最新修改日期

    原始服务器通过Last-Modified响应头来返回组件的最新修改日期。

    举个栗子:

    当我们不带缓存访问www.google.com.hk的时候,我们需要下载google的logo,这时会发送这样一个HTTP请求:

    Request

    GET googlelogo_color_272x92dp.png HTTP 1.1

    Host: www.google.com.hk

    Response:

    HTTP 1.1 200 OK

    Last-Modified:Fri, 04 Sep 2015 22:33:08 GMT

    https://images2015.cnblogs.com/blog/861963/201603/861963-20160319143807209-1678450570.png

    当需要再次访问相同组件的时候,同时缓存已经过期,浏览器会发送如下条件GET请求:

    Request

    GET googlelogo_color_272x92dp.png HTTP 1.1

    If-Modified-Since:Fri, 04 Sep 2015 22:33:08 GMT

    Host: www.google.com.hk

    Response:

    HTTP 1.1 304 Not Modified

    https://images2015.cnblogs.com/blog/861963/201603/861963-20160319143556756-1561871554.png

    实体标签

    ETag提供了另外一种方式,用于检测浏览器缓存中的组件与原始服务器上的组件是否匹配。摘抄自书上的例子:

    不带缓存的请求:

    Request

    GET /i/yahoo/gif HTTP 1.1

    Host: us.yimg.com

    Response:

    HTTP 1.1 200 OK

    Last-Modified:Tue,12 Dec 200603:03:59 GMT

    ETag:”10c24bc-4ab-457elc1f“

    再次请求相同组件:

    Request

    GET /i/yahoo/gif HTTP 1.1

    Host: us.yimg.com

    If-Modified-Since:Tue,12 Dec 200603:03:59 GMT

    If-None-Match:”10c24bc-4ab-457elc1f“

    Response:

    HTTP 1.1 304 Not Midified

    为什么要引入ETag?

    ETag主要是为了解决Last-Modified无法解决的一些问题:

    1. 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;

    2. 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);

    3. 某些服务器不能精确的得到文件的最后修改时间。

    ETag带来的问题

    ETag的问题在于通常使用某些属性来构造它,有些属性对于特定的部署了网站的服务器来说是唯一的。当使用集群服务器的时候,浏览器从一台服务器上获取了原始组件,之后又向另外一台不同的服务器发起条件GET请求,ETag就会出现不匹配的状况。例如:使用inode-size-timestamp来生成ETag,文件系统使用inode存储文件类型、所有者、组和访问模式等信息,在多台服务器上,就算文件大小、权限、时间戳等都相同,inode也是不同的。

    最佳实践

    1. 如果使用Last-Modified不会出现任何问题,可以直接移除ETag,google的搜索首页则没有使用ETag。

    2. 确定要使用ETag,在配置ETag的值的时候,移除可能影响到组件集群服务器验证的属性,例如使用size-timestamp来生成时间戳。

     

    十四、使Ajax可缓存

    维基百科中这样定义Ajax:

    AJAX即“Asynchronous JavaScript and XML”(异步的JavaScript与XML技术),指的是一套综合了多项技术的浏览器端网页开发技术。Ajax的概念由杰西·詹姆士·贾瑞特所提出。

    传统的Web应用允许用户端填写表单(form),当提交表单时就向Web服务器发送一个请求。服务器接收并处理传来的表单,然后送回一个新的网页,但这个做法浪费了许多带宽,因为在前后两个页面中的大部分HTML码往往是相同的。由于每次应用的沟通都需要向服务器发送请求,应用的回应时间依赖于服务器的回应时间。这导致了用户界面的回应比本机应用慢得多。

    与此不同,AJAX应用可以仅向服务器发送并取回必须的数据,并在客户端采用JavaScript处理来自服务器的回应。因为在服务器和浏览器之间交换的数据大量减少(大约只有原来的5%)[来源请求],服务器回应更快了。同时,很多的处理工作可以在发出请求的客户端机器上完成,因此Web服务器的负荷也减少了。

    类似于DHTML或LAMP,AJAX不是指一种单一的技术,而是有机地利用了一系列相关的技术。虽然其名称包含XML,但实际上数据格式可以由JSON代替,进一步减少数据量,形成所谓的AJAJ。而客户端与服务器也并不需要异步。一些基于AJAX的“派生/合成”式(derivative/composite)的技术也正在出现,如AFLAX。

    Ajax的目地是为突破web本质的开始—停止交互方式,向用户显示一个白屏后重绘整个页面不是一种好的用户体验。

    异步与即时

    Ajax的一个明显的有点就是向用户提供了即时反馈,因为它异步的从后端web服务器请求信息。

    用户是否需要等待的关键因素在于Ajax请求是被动的还是主动的。被动请求是为了将来来使用而预先发起的,主动请求是基于用户当前的操作而发起的

    什么样的AJAX请求可以被缓存?

    POST的请求,是不可以在客户端缓存的,每次请求都需要发送给服务器进行处理,每次都会返回状态码200。(可以在服务器端对数据进行缓存,以便提高处理速度)

    GET的请求,是可以(而且默认)在客户端进行缓存的,除非指定了不同的地址,否则同一个地址的AJAX请求,不会重复在服务器执行,而是返回304。

    Ajax请求使用缓存

    在进行Ajax请求的时候,可以选择尽量使用get方法,这样可以使用客户端的缓存,提高请求速度。

     

    css优化,js优化以及web性能优化

    20170727 00:09:07

    阅读数:3021

    Css优化总结

          对于css的优化可以从网络性能和css语法优化两方面来考虑。

    Css性能优化方法如下:

    1css压缩

    Css 压缩虽然不是高端的知识,但是很有用。其原理也很简单,就是把我们css代码中没有用的空白符等删除,达到缩减字符个数的目的。

     

    压缩css代码的工具:

    AYUI compressor,可以在线压缩cssjs代码。

    Bgulp自动化构建工具,中的gulp-minify-css

     

    2gzip压缩

    Gzip是一种流行的文件压缩算法,现在应用的十分广泛,尤其实在Linux这个平台上,这个不止是对css,当应用Gzip压缩一个文本时,效果是非常明显的。大约可以减少70%以上的文件大小(这取决于文件中的内容)。

    在没有 gzip压缩的情况下,web服务器直接把html页面、css脚本以及js脚本发送到浏览器,而支持gzip的浏览器在本地进行解压和解码,并显示原文件。这样我们传输的文件字节数减少了,自然可以达到网络性能优化的目的。Gzip压缩需要服务器的支持,所以需要在服务器端进行配置。

     

    3、合写css(通过少些css属性来达到减少css字节吗的目的)

    例子:

    background:#000 url(image.jpg) top left no-repeat;

    font:font-style   font-weight    font-size  font-familiy;

    margin:5px 10px 20px 15px;

    padding:5px;

    border:2px 5px 10px 3px;

    border-top:2px 5px 10px 3px;

     

    4、利用继承

    Css的继承机制可以帮我们在一定程度上缩减字节数,我们知道css很多属性可以继承,即在父容器设置了默认属性,子容器会默认也使用这些属性。

    可继承的属性举例:

    所有元素都可以继承的属性visibility cursor

    内联元素和块元素可以继承的属性:

    Letter-spacingword-spacingwhite-spaceline-heightcolorfontfont-familyfont-sizefont-stylefont-variantfont-weighttext-decorationtext-transformdirection

    块状元素可以继承的属性:

    Text-indenttext-align

    列表元素可以继承的属性:

    List-stylelist-style-typelist-style-positionlist-style-image

     

    表格元素可以继承的属性:

    Border-collapse

     

    不可以继承的属性:

    Displaymarginborderpaddingbackgroundheightmin-heightmax-heightwidthmin-widthmax-widthoverflowpositionleftrighttopbottomz-indexfloatcleartable-layoutvertical-alignpage-break-afterpage-break-beforeunicode-bidi

     

    5、抽离、拆分css,不加载所有css

    抽离原则:在很多时候,我们把页面通用的css写到了一个文件,这样加载一次后,就可以利用缓存,但这样做兵不适合所有的场景。所以抽离拆分的时候要考虑好。

     

    6css放在head中,减少repaintreflow

    Css方法在页面的顶部,有利于优化的原因???

    当浏览器从上到下一遍下载html生成dom tree,一边根据浏览器默认以及现有css生成render tree来渲染页面。当遇到新的css的时候下载并结合现有css重新生成render tree。则刚才的渲染功能就全废了。当我们把所有css放在页面的顶部,就没有重新渲染的过程了。

     

    脚本中应该尽量少用repaintreflow

    Reflow:当dom元素出现隐藏/显示,尺寸变化。位置变化的时候,逗号让浏览器重新渲染页面,以前的渲染工作白费了。

    Repaint:当元素的背景颜色,边框颜色变化的时候,不会引起reflow的变化,是会让浏览器重新渲染该元素。

     

    7、避免使用通配符或隐式通配符:visible.

    8、避免层级或过度限制csscss是从右向左解析的)

    A、不要用标签或class来限制id

    #test .info   /div #test这都属于画蛇添足

    Id已经可以唯一而且快速的定位到一个元素了。

     

    B、不要用标签限制class

    Div .info  不好

    css代码编写中,如果直接使用class不能达到目的,一般是class设计出现了问题,css需要重构。

    C、尽量使用最具体的类别,避免使用后代选择器,在css选择器中,后代选择器非但没有加快css查找,反而后代选择器是css中耗费最贵的。

     

     

    JavaScript优化总结

    1、避免全局查找

    在一个函数中尽量将全局对象存储为局部变量来查找,因为访问局部变量的数要更快一些。

    function(){

    alert(window.location.href+window.location.host);

    }

    修改为:

    funciton(){

    var location=window.location;

    alert(location.href+location.host);

    }

     

    2、定时器

    如果针对的是不断运行的代码,不应该使用setTimeout,而应该使用setInterval。因为setTimeout每一次都会初始化一个定时器。而setInterval只会在开始的时候初始化一个定时器。

    var timeoutTimes=0;

    function timeout(){

          timeoutTimes++;

          if(timeoutTimes<10){

               setTimeout(timeout,10);

          }

    }

    修改为:

    var intervalTimes=0;

    function interval(){

          intervalTimes++;

          if(intervalTimes>=10){

               clearInterval(interval)''

          }

    }

    var interval = setInterval(interval,10);

     

    3、字符串连接

    如果需要连接多个字符串,应该少使用+=

    s+=a;s+=b;s+=c;

    修改为:

    s+=a+b+c;

    而如果是收集字符串,比如多次对同一个字符进行+=操作的话,最好使用一个缓存,使用js数组来收集,最后join方法连接起来。

    var buf=[];

    for(var i=0;i<100;i++){

          buf.push(i.toString());

    }

    var all=buf.join("");

     

    4、避免with语句

    和函数类似,with语句会创建自己的作用域,因此会增加其中执行的代码的作用域链的长度。犹豫额外的作用域链的查找,在with语句中执行的代码肯定会比外面执行的低吗要慢,在能不使用with语句的时候,尽量不要使用with语句。

    with(a,b,c,d){

          property1=1;

          property2=2;

    }

    修改为:

    var obj=a.b.c.d;

    obj.property1=1;

    obj.property2=2;

     

    5、数字转为字符串

    一般用“”+1来将数字转为字符串,虽然看起来比较丑一点,但事实上这个效率最高。

    “”+1>String()>.toString()>new String()

     

    6、浮点数转化为整型

    很多人喜欢使用pareseInt(),其实parseInt()是用于将字符串转为数字,而不是浮点数和整型之间的转换。我们应该使用的是Math.floor()Math.round();

     

    7、多个类型声明

    所有变量可以使用单个var语句来声明,这样就是组合在一起的语句,以减少整个脚本的执行时间。

    var   A=1,

           B=2,

           C=3;

     

    8、使用直接量

    var aTest=new Array();//var aTest=[]

    var aTest=new Object();//var aTest={}

    var reg=new RegExp();//var reg=/ /;

    创建具有特殊性的一般对象,也可以使用字面量。

    var oFruit=new O();

    oFruit.color="red";

    oFruit.name="apple";

    应改为

    varoFruit={color:"red",name:'apple'}

     

    9、使用DocumentFragment优化多次的append

    一旦需要跟新DOM,请考虑使用文档碎片来构建结构,然后再添加到现有的文档中。

    for(var i=0;i<1000;i++){

          varel=document.createElement('p');

          el.innerHTML=i;

          document.body.append(el);

    }

    应该改为

    varfrag=document.createDocumentFragment();

    for(var i=0;i<1000,i++){

          varel=document.createElement('p');

          el.innerHTML=i;

          frag.append(el);

    }

    document.body.appendChild(frag);

     

    10、使用一次innerHTML复制代替构建dom元素。

    对于大的dom更改,使用innerHTML要比使用标准的dom方法创建同样的dom结构快的多。

    varfrag=document.createDocumentFragment();

    for(var i=0;i<1000;i++){

          varel=document.createElement('p');

          el.innerHTML=i;

          frag.appendChild(el);

    }

    document.body.appendChild(frag);

    应该改为

    var html=[];

    for(var i=0;i<1000;i++){

          html.push('

    '+i+"

    ");

    }

    document.body.innerHTML=html.join("");

     

    11、使用firstChildnextSibling代替childNodes遍历dom元素。

    var nodes=element.childNodes;

    for(var i=0,l=nodes.length;i

          varnode=nodes[i];

    }

    应该改为

    var node=element.firstChild;

    while(node){

          node.node.nextSibling;

    }

     

    12、删除dom节点

    删除dom节点之前,一定要删除注册在该节点上的事件,不管是observe方式还是用attachEvent方式注册的事件。否则将会产生无法回收的内存。另外,removeChildinnerHTML=‘’两者之间尽量选择后者,因为在内存泄漏工具中监测的结果是用removeChild无法有效的释放dom节点。

     

    13、简化终止条件

    由于每次循环过程,都会计算终止条件。所以必须保证他尽可能的快。也就是避免属性查找或其他操作。最好是将循环控制量保存到局部变量中,也就是说对数组或列表对象遍历的时候,提前将length保存到局部变量中,避免循环的每一步重复取值。

    varlist=document.getElementsByTagName('p');

    for(var i=0;i

     

    }

    应该改为

    for(var i=0,l=list.length;i

         

    }

     

    14、使用后测试循环

    js中,我们使用for(;;),while(),for(in)三种循环。for(in)的效率极差。因为他需要查询散列键,只要可以,就应该尽量少用。

    for(;;)while循环,while优于for(;;),可能for(;;)结构问题,需要经常的跳转。do..while更好。

     

    15、尽量少用eval函数

    使用eval函数相当于在运行时,再次调用解释引擎对内容进行运行,需要消耗大量时间,而且使用eval带来的安全性问题也是不容忽视的。

     

    16、不要给setTimeoutsetInterval传递字符串参数。

    var num=0;

    setTimeout(num++,10);

    应该改为

    var num=0;

    function addNum(){

          num++;

    }

    setTimeout(addNum,10);

     

    17、缩短否定检测

    if(oTest!="#ff0000"){}

    if(oTest!=null){}

    if(oTest!=false){}

    以上都不太好

    if(!oTest){这个比较好}

     

    18、可以用三目运算符替换条件分支,可以提高效率。

     

     

    Web性能优化

    1、避免坏请求

    有时候页面中的htmlcss会向服务器请求一个不存在的资源,比如图片或者html文件,这会造成浏览器与服务器之间过多的往返请求。

    类似于:

    浏览器:我需要这个图像

    服务器:我没有这个图像

    浏览器:你确定吗?这个文档说你有

    服务器:真的没有

    这个降低页面加载速度。因此检查坏连接很有必要。可通过GooglePageSpeed工具,找到问题后,补充相当资源文件或者修改资源链接地址即可。

     

    2、避免css@import

    使用@import方法引用css文件可能会带来一些影响页面加载速度的问题。比如导致文件按顺序加载(一个加载完成后才会加载另一个),无法并行加载;

    检查工具:css delivery

    查到@import url(“style.css”)

    就替换为:

     

    3、避免使用document.write

    js中,可以使用document.write。在网页上显示内容或者调用外部资源,而通过此方法,浏览器采取一些多余的步骤(下载资源,读取资源)。运行js来了解需要做什么,调用其他资源时,需要重新在执行一次这个过程。由于浏览器之前不知道要显示什么,所以会降低页面加载的速度。

    要知道,任何能够被document.write调用的资源,都可以通过html调用。这样速度会更快

    document.write('');

    改为

     

    4、合并多个外部css文件

    网站中每使用一个css文件,都会让你的页面加载速度慢一点。可以css delivery工具,来检测页面代码中css文件。然后通过复制粘贴合并成一个。

     

    5、合并多个外部js文件

    可以用resource check来检测页面中所引用的js文件数,然后可以通过复制粘贴的方法将多个文件合并成一个。

     

    6、通过css sprites来整合图像

    若页面中有6个小图像,那么浏览器在显示时会分别下载,你可以通过css sprites将这些图像合并成为一个,可以减少页面加载所需要的时间。

    Css sprites两个步骤:整合图像,定位图像

     

    7、延迟js加载

    浏览器在执行js代码时,会停止处理页面。当页面中很多js文件或者代码要加载时,将导致严重的延迟。尽管可以使用defer,异步或将js代码放自爱页面底部来延迟js的加载。但这些都不是一个好的解决方案。

    好方法

     

    8、启用压缩/Gzip

    使用gziphtmlcss文件进行压缩,通常可以大约节省50%70%,这样加载页面只需要更少的带宽和更少的时间。

     

    9、如果你的cssjs较小,可以将cssjs内嵌到html页面中,这样可以减少页面加载所需要的文件数,从而加快页面的加载。

     

    10、用minify css压缩css代码

     

    11、尽量减少dns查询次数

    当浏览器和服务器建立链接时,它需要进行dns解析,将域名解析为ip地址,然而,一旦客户端需要执行dns lookup时,等待时间将会取决于域名服务器的有效响应速度。

    虽然所有的ispdns服务器都能缓存域名和ip地址映射表。但如果缓存的dns记录过期了而需要更新,则可能需要遍历多个dns节点,有时候需要通过全球范围内来找到可信任的域名服务器,一旦域名服务器工作繁忙,请求解析时,就需要排队则进一步延时等待时间。

    所有减少dns查询次数很重要,页面加载就尽量避免额外耗时,为了减少dns查询次数,最好的解决方法就是在页面中减少不同的域名请求的机会、

    可通过request checker工具来检测页面中存在多少请求后,进行优化。

     

    12、尽量减少重定向

    有时候为了特定需求,需要在网页中使用重定向。重定向的意思是,用户的原始请求(如请求A)被重定向到其他的请求(如请求B);

    网页中使用重定向会造成网站性能和速度下降,因为浏览器访问网址是一连串的过程,如果访问到一半,而跳转到新的地址,就会重复发起一连串的过程,这将浪费很多时间。所有我们尽量避免重定向。Google建议

    A       不要链接到一个包含重定向的页面

    B       不要请求包含重定向的资源

     

    13、优化样式表和脚步顺序

    Style标签和样式表调用代码应该放置在js代码的前面,这样可以使页面的加载速度加快。

     

    14、避免js阻塞渲染

    浏览器在遇到一个引入外部js文件的

    你可能感兴趣的:(面试总结)