1

1.盒模型

盒模型是CSS的核心概念,描述了元素如何显示,以及(在一定程度上)如何互相作用互相影响。页面中所有的元素都被看作一个矩形盒子,这个盒子包含元素的内容、内边距padding、边框border和外边距margin

默认盒模型的width属性应用给内容区
  • 通过修改box-sizing属性可以改变计算盒子大小的方式。box-sizing的默认值为content-box,即默认盒模型的width属性应用给内容区
  • 如果把box-sizing的值修改为border-box,那么width和height属性的值将会包含内边距和边框。此时,外边距仍会影响盒子在页面中占据的整体空间,即它的宽度不会计算到width中。
  • 应用

默认情况下,让.group中的 .block宽度在任意情况下都占其父元素的1/3,可以使用以下规则:

.group .block{
    width:33.333%;
}

但是,如果给.block添加了内边距,它的宽度就会变成.group元素的1/3外加应用给它的内边距。如果这是三栏布局,就会破坏。要解决这个问题,可以再增加一个内部元素,然后改为为这个元素添加内边距,或者设置一个不同的box-sizing值,从而修改盒子宽度的计算方式:

.group .block{
    width:33.333%;
    box-sizing:border-box;
    padding:20px;
}
//此时,无论是否添加内边距,无论内边距是多少,.block的宽度始终保持为其父元素的1/3
CSS规定:上、下方位的内、外边距(5%),仍然基于包含块(父元素)的宽度来计算,某些情况不一定

2.垂直水平居中

<body>
<div id="test">
</div>
</body>

第一种

//最简单,已知高宽
#test{
    width: 200px;
    height: 200px;
    background: pink;
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    margin: auto;
}
绝对定位盒模型的特性
left+right+margin+padding+width=包含块的宽度
padding、margin默认为0,设为auto=包含块宽-width

第二种

//已知高宽
#test{
    width: 200px;
    height: 200px;
    background: deeppink;
    position: absolute;
    left: 50%;
    top: 50%;
    margin-left: -100px;
    margin-top: -100px;
    }

第三种

//已知或未知高宽
#test{
    width: 200px;
    height: 200px;
    background: red;
    position: absolute;
    left: 50%;
    top: 50%;//相对父
    //可用于已知
    //transform: translate(-100px,-100px);
    //未知元素水平垂直居中
    transform: translate(-50%,-50%);
   //相对自身,可用于未知高宽
  }

第四种

//图片垂直水平居中,可应用于未知高宽
<div id="test">
    <img src="img/玩具鸭.png">
</div>
html,body{
    height: 100%;
}
/*伪元素方法*/
#test{
    height: 100%;
    text-align: center;
}
img{
    vertical-align: middle;
}

#test:after{
    content: "";
    display: inline-block;
    height: 100%;
    width: 0px;
    vertical-align: middle;
}

第五种

<body>
<div class="inner"></div>
</body>
/*felx方法*/
html,body{
    height: 100%;
    width: 100%;
}
body{
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
}
.inner{
    width: 200px;
    height: 200px;
    background: red;
}

3.过渡/变形/动画的属性

在说动画之前,先顺便提一下过渡

过渡


CSS transition提供了一种在更改CSS属性的控制动画速度的方法,其可以让属性变化成为一个持续一段时间的过程,而不是立即生效。
transition-property:默认值all,指定应用过渡的属性名称
transition-duration:以秒或毫秒为单位指定过渡的时间(0s默认0s,必须带单位),可指定多个时长分别对应多个属性变化
transition-timing-function:给每个属性设置时间变化模式(属性值有easylinearease-inease-out...)
transition-delay:延迟的时间

关于过渡的坑:
#test{
    transition-property:width;
    transition-duration:2s;
    transition-timing-function:liner;
}
body:hover #test{
    transition-property:height;
    width:200px;
    height:200px;
}
实际上:在鼠标移入body时候:width瞬间变化,height过渡;移除body时候:width过渡变回原型,height瞬间变回。
<script ...>
//transition元素首次渲染还没有结束情况下,是不会被触发的
    var test=document.querySelector("#test");
    test.style.width="300px"
</script>
并没有触发,在初始化时候,直接将width设置成为300px
window.load=function(){
    var test=document.querySelector("#test");
    test.style.width="300px"
}
在页面加载完成时触发,transition有变化的过程
transition在页面初试渲染完成后才触发,变化的过程只展示在渲染以后
transition简写顺序中,第一个时间解析为transition-duration;第二个解析为transition-delay
在绝大部分变换样式的切换时,变换组合个数或位置不一样时,是没有办法触发过渡的

变形


2D变形:transform属性允许修改CSS视觉格式模型的坐标空间,transform属性只对block级元素生效
平移:
transform:rotate(200dge)旋转200度
transform:translateX(300px),向右平移300px,这里需要思考CSS坐标轴方向
transform:translateY(300px),向下平移300px
transform:translate(300px,300px),向右下平移
transform:translate(300px),单值只动X轴
斜切:
transform:skew(45deg)斜切
transform:skewX(45deg)沿X轴斜切
transform:skewY(45deg)沿Y轴斜切
transform:skew(45deg,45deg)斜切 O --> \
缩放:
transform:scale(1.5)1.5倍放大
transform:scaleX(n)X轴放大n倍
transform:scaleY(n)Y轴放大n倍
基点的变化:
transform-origin:left top按照左上角转
transform-origin:50px 50px这里转

变换组合
顺序是从右往左,变换的底层原理其实就是矩阵的运算

3D变形
perspective景深:让3D场景有近大远小效果,是一个不可继承属性但可作用于后代
应用景深的元素称为“舞台元素”,舞台元素的所有后代元素都会受影响(如果后代中也添加perspective属性,效果叠加而不是覆盖)
transform:perspective(1000px);默认是none,沿Z轴1000px,使用该元素需要放在transform的首位,放在其他函数后面会被忽略
transform:rotate3d(Xdeg,Ydeg,Zdeg)
transform:translateZ(100px)
transform:translate3d(100px,100px,100px)
transform:scaleZ(n);没有变化,需要和translateZ()配合使用,两值相乘为沿Z轴移动距离从而有扩大或缩小效果
transform:scale3d(n,n,n)
灭点:景深原理,控制灭点的位置,景深越大,灭点越远,元素变形越小
perspective-origin:left top;控制视角的位置
transform-style:指定子元素如何在空间中展示,营造有层级的3D舞台效果,不可继承,作用于子元素
transform-style:flat(默认);所有子元素在2D平面呈现
transform-style:preserve-3d;所有元素在3D平面呈现
backface-visibility:hidden/visible;属性用来设置是否显示元素的背面,默认是显示

动画


animation-name指定应用的一系列动画,每个名称代表一个由@keyforms定义的动画序列
animation-duration指定一个动画周期的时长,默认0s无动画
animation-timing-function指定动画快慢
animation-delay动画开始前等待时间
animation-iteration-count动画执行的次数
animation-direction动画执行的方向(例如from-->to):normal/reverse
animation-fill-mode控制元素在动画外的状态
animation-fill-mode:backwards;from前状态和from保持一致,to以后是原本
animation-fill-mode:forwards;to以后状态和to保持一致,from以前是原本
animation-fill-mode:both;from前状态和from保持一致,to以后状态和to保持一致
animation-play-state:pause/running;定义动画的运行和暂停

4.link 和 import 区别

<link href="./index.css" rel="stylesheet"> 

这段代码告诉浏览器把index.css文件下载下来,然后把其中的样式应用到网页上。同样的代码可以把这个样式表应用到任意多个网页上,因此这是一种跨网页甚至跨网站重用样式表的推荐方式。

<style type="text/css">
    @import url("./index.css");
<style>

可以在HTML文档的head部分把@import指令放在style中,也可以在外部样式表中使用它。后一种用法意味着,如果网页加载外部样式表,那么浏览器后续可能还需要下载更多的CSS文件。

区别
1: Link属于html标签,而@import是CSS中提供的
2:在页面加载的时候,link会同时被加载,而@import引用的CSS会在页面加载完成后才会加载引用的CSS
3:@import只有在ie5以上才可以被识别,而link是html标签,不存在浏览器兼容性问题
4:线上网页最好把需要加载的CSS文件控制在1或者2个,如果用link元素加载CSS文件,然后又在其中使用@import,并不能把请求控制为1个,因为这意味着现需要加载一个请求下载链接的文件,此外还要发送额外请求取得所有导入的文件

5.DOM解析和渲染 (CSS)

推荐阅读
CSS加载不会阻塞DOM树的解析,但是CSS加载会阻塞DOM树的渲染,CSS加载会阻塞后面的js语句的执行

6.跨域(CORS)

CORS(Cross-Origin Resource Sharing,跨域资源共享)定义了在必须访问跨源资源时,浏览器与服务器应该如何沟通。基本思想是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是应该失败。
比如一个简单的GET或POST请求,它没有自定义的头部,而主体内容是text/plain。在发送该请求时,需要给它附加一个额外的Origin头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。
Origin头部的示例:
Origin:http://www.nczonline.net
如果服务器认为这个请求可以接受,就在Access-Control-Allow-Origin头部中回发相同源信息(如果是公共资源,可以回发“*”),例如:
Access-Control-Allow-Origin:http://www.nczonline.net
如果没有这个头部或者有这个头部但源信息不匹配,浏览器就会驳回请求。正常情况下,浏览器会处理请求。注意,请求和响应都不包含cookie信息。

IE对CORS的实现

IE8引入了XDR(XDomainRequest)类型。与XHR类似,XDR与XHR不同之处在于:

  • cookie不会随着请求发出,也不会随响应返回
  • 只能设置请求头部信息中的Content-Type字段
  • 不能访问响应头部信息
  • 只支持GET和POST请求

这些变化使CSRF(Cross-Site Request Forgery,跨站点请求伪造)和XSS(Cross-Site Scripting,跨站点脚本)的问题得到了缓解。被请求的资源可以根据它认为合适的任意数据(用户代理、来源页面等)来决定是否设置Access-Control-Allow-Origin头部。作为请求的一部分,Origin头部的值表示请求的来源域,以便远程资源明确地识别XDR请求。
XDR对象的使用方法与XHR对象非常类似。也是创建一个XDomainRequest的实例,调用open()方法,再调用send()方法。但是与XHR对象的open()方法不同,XDR对象的open()方法只接收两个参数:请求的类型和URL。
所有的XDR请求都是异步执行的,不能用它来创建同步请求。请求返回之后,会触发load事件,响应的数据也会保存在responseText属性中,如下表示。
屏幕快照 2020-05-11 下午10.31.20.png
在接收到响应后,只能访问响应的原始文本;没有办法确认响应的状态代码。而且,只要响应有效就会触发load事件,如果失败就会触发error事件,除了错误本身之外,没有其他信息可用,因此唯一能够确定的就只有请求未成功。要检测错误,可以指定一个onerror事件处理程序。
在请求返回前调用abort()方法可以终止请求:

xdr.abort()://终止请求

也支持timeout属性以及ontimeout事件处理程序
为了支持POST请求,XDR对象提供了contentType属性,用来表示发送数据的格式,这个属性是通过XDR对象影响头部信息的唯一方式
屏幕快照 2020-05-11 下午4.24.04.png

其他浏览器对CORS的实现

Firefox3.5+、Safari4+、Chrome、IOS版Safari和Android平台支持的WebKit都通过XMLHttpRequest对象实现了对CORS的原生支持。
在尝试打开不同来源的资源时,无需额外编写代码就可以触发这个行为。要求位于另一个域中的资源,使用标准的XHR对象并在open()方法中传入绝对的URL即可。

var xhr= new XMLHttpRequest()
xhr.onreadystatechange=function () {
    if (xhr.readyState==4){
        if ((xhr.status>=200 && xhr.status<300) || xhr.status==304){
            console.log(xhr.responseText)
        }else {
            console.log("request was unsuccessful:"+xhr.status)
        }
    }
}
xhr.open("get","https://developer.mozilla.org/en-US/docs/Web/API/XDomainRequest",true)
xhr.send(null);

打印页面内容:
屏幕快照 2020-05-11 下午4.26.26.png
查看详细内容
屏幕快照 2020-05-11 下午4.29.25.png
与 IE中的XDR对像不同,通过跨域XHR对象可以访问status和statusText属性,而且还支持同步请求。跨域XHR对象也有一些限制,为了安全这些限制是必需的:

  • 不能使用setRequestHeader()设置自定义头部
  • 不能发送和接收cookie
  • 调用getAllResponseHeaders()方法总会返回空字符串

由于无论同源请求还是跨源请求都使用相同的接口,因此对于本地资源,最好使用相对URL,在访问远程资源时再使用绝对URL。这样做能消除歧义,避免出现限制访问头部或本地cookie信息等问题。

Preflighted Requests(预检请求)

CORS通过一种叫做Preflighted Requests的透明服务器验证机制支持开发人员使用自定义的头部、GET或POST之外的方法,以及不同类型的主体内容。在使用下列高级选项来发送请求时,就会像服务器发送一个Preflighted请求。这种请求使用OPTIONS方法,发送下列头部。
Origin:与简单的请求相同
Access-Control-Request-Method:请求自身使用的方法
Access-Control-Request-Header:(可选)自定义的头部信息,多个头部以逗号分隔
示例:一个带有自定义头部NCZ的使用POST方法发送的请求

Origin:http://www.nczonline.net
Access-Control-Request-Method:POST
Access-Control_Request-Headers:NCZ

发送这个请求后,服务器可以决定是否允许这种类型的请求。服务器通过在响应中发送如下头部与浏览器进行沟通。

Access-Control-Allow-Origin:与简单的请求相同
Access-Control-Allow-Methods:允许的方法,多个方法以逗号分隔
Access-Control-Allow-Header:允许的头部,多个头部以逗号分隔
Access-Control-Max-Age:应该将这个Preflight请求缓存多长时间
示例:

Access-Control-Allow-Origin:http://www.nczonline.net
Access-Control-Allow-Methods:POST,GET
Access-Control-Allow-Header:NCZ
Access-Control-Max-Age:1728000

Preflight请求结束后,结果将按照响应中指定的时间缓存起来。而为此付出的代价只是第一次发送这种请求时会多一次HTTP请求。支持Preflight请求的浏览器包括Firefox3.5+、Safari4+、Chrome。

带凭据的请求

默认情况下,跨源请求不提供凭据(cookie、HTTP认证及客户端SSL证明等)。通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。如果服务器接受带凭据的请求,会用下面的HTTP头部来响应。

Access-Control-Allow-Credentials:true

如果发送的事带凭据的请求,但服务器的响应中没有包含这个头部,那么浏览器就不会把响应交给JavaScript(于是,reponseText中将是空字符串,status的值为0,而且会调用onerror()事件处理程序)。另外,服务器还可以在Preflight响应中发送这个HTTP头部,表示允许源发送带凭据的请求。
支持withCredentials属性的浏览器有Firefox3.5+、Safari4+、Chrome。

跨浏览器的CORS

即使浏览器对CORS的支持程度并不一样,但是所有浏览器都支持简单的(非Preflight和不带凭据的)请求,因此有必要实现一个跨域浏览器的方案。检测XHR是否支持CORS的最简单方式,就是检查是否存在withCredentials属性。再结合检测XDomianRequest对象是否存在,就可以兼顾所有浏览器了。

function createCORSRequest(method,url) {
    var xhr=new XMLHttpRequest();
    if ('withCredentials' in xhr){
        xhr.open(method,url,true)
    }else if (typeof XDomainRequest != "undefined"){
        vxr = new XDomainRequest();
        xhr.open(method,url)
    }else {
        xhr=null;
    }
    return xhr
}
var request=createCORSRequest("get","https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise")
if(request){
    request.onload=function () {
        //对request.responseText进行处理
        console.log(request.responseText)
    }
    request.send()
}

Firefox、Safari和Chrome中的XMLHttpRequest对象与IE中的XDomainRequest对象类似,都提供了够用的接口,因此以上模式还是相当有用的。两个对象共同的属性/方法如下:

  • abort():用于停止正在进行的请求
  • onerror():用于替代onreadystatechange检测错误
  • onload:用于替代onreadystatechange检测成功
  • responseText:用于取得响应内容
  • send():用于发送求情

以上成员都包含在createCORSRequest()函数返回的对象中,在所有浏览器中都能正常使用。

图像Ping

图像Ping
一个网页可以从任何网页中加载图像,不用担心跨域不跨域。这也是在线广告跟踪浏览量的主要方式。动态的创建图像,使用它们的onloadonerror事件处理程序来确定是否接受到了响应。
动态创建图像经常用于“图像”Ping。图像Ping是与服务器进行简单、单向的跨域通信的一种方式。请求的数据是通过查询字符串形式发送的,而响应可以是任意内容,但通常是像素或204响应。通过图像Ping,浏览器得不到任何具体的数据,但通过监听loaderror事件,它能知道响应是什么时候接受到的。如下例子:

var img=new Image()
Img.onload=img.onerror=function(){
    alert(“Done!”);
};
Img.src=“http://www.example.com/test?name=Nicholas”;

这里创建了一个Image的实例,然后将onload和onerror事件处理程序指定为同一个函数。这样无论是什么响应,只要请求完成,就能得到通知。请求从设置src属性那一刻开始,而这个例子在请求中发送了一个name参数。

图像Ping最常用于跟踪用户点击页面或动态广告曝光次数。图像Ping有两个主要的缺点,一是只能发送GET请求,二是无法访问服务器的响应文本。因此,图像Ping只能用于浏览器与服务器间的单向通信。

JSONP

JSONP是JSON with padding(填充式JSON或参数JSON)的简写,是应用JSON的一种新方法,在后来的web服务中非常流行的。JSONP看起来与JSON差不多,只不过被包含在函数调用中的JSON,就像下面这样:
callback({“name”:”Nicholas”});
JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的。而数据就是传入回调函数中的JSON数据。下面是一个典型的JSONP请求:

http://freegeoip.net/json/?callback=handleResponse

这个URL是在请求一个JSONP地理定位服务。通过查询字符串来指定JSONP服务的回调参数是很常见的,就像上面URL所示,这里的回调函数的名字叫handleResponse()
JSONP是通过动态<script>元素来使用的,使用时可以为src属性指定一个跨域URL。这里的<script>元素与<img>元素类似,都有能力不受限制地从其他域加载资源。因为JSONP是有效的JavaScript代码,所以在请求完成后,即在JSONP响应加载到页面中以后,就会立即执行。例如:

function handleResponse(response){
    console.log('you are at ip'+response.ip+',which is in'+response.city+','+response.region_name);
}
var script=document.createElement('script');
script.src='https://ipstack.com/?callback=handleResponse';
document.body.insertBefore(script, document.body.firstChild);

这个例子通过查询地理定位服务来显示你的IP地址和位置信息。

JSONP之所以在开发人员中极为流行,主要原因是它非常简单易用。与图像Ping相比,它的优点在于能够直接访问响应文本,支持在浏览器与服务器之间双向通信。不过,JSONP也有两点不足:
首先,JSONP是从其他域中加载代码执行。如果其他域不安全,很可能会在响应中夹带一些恶意代码,而此时除了完全放弃JSONP调用之外,没有办法追究。因此在使用不是你自己运维的Web服务时,一定得保证它安全可靠。
其次,要确定JSONP请求是否失败并不容易。虽然HTML5给<script>元素新增了一个onerror事件处理程序,但目前还没有得到任何浏览器支持。为此,开发人员不得不使用计时器检测指定时间内是否接收到了响应。但就算这样也不能尽如人意,毕竟不是每个用户上网速度和带宽都一样。

Comet

Comet指的是一种更高级的Ajax技术(经常也有人称为“服务器推送”)。Ajax是一种从页面像服务器请求数据的技术,而Comet则是一种服务器向页面推送数据的技术。Comet能够让信息近乎实时地被推送到页面上,非常适合处理体育比赛的分数和股票报价。
有两种实现Comet的方式:长轮询和流。长轮询是传统轮询(也没有称为短轮询)的一个翻版,即浏览器定时向服务器发送请求,看有没有更新的数据。如图展示短轮询的时间线:
屏幕快照 2020-05-12 下午12.12.49.png
长轮询把短轮询颠倒了一下。页面发起一个服务器的请求,然后服务器一直保持连接打开,直到有数据可发送。发送完数据后,浏览器关闭连接,随即又发起一个到服务器的新请求,这一过程在页面打开期间一直持续不断。如图展示了长轮询的时间线:
屏幕快照 2020-05-12 下午12.14.55.png
无论是短轮询还是长轮询,浏览器都要在接受数据之前,先发起对服务器的连接。两者最大的区别在于服务器如何发送数据。短轮询是服务器立即发送响应,无论数据是否有效,而长轮询是等待发送响应。轮询的优势是所有浏览器都支持,因为使用XHR对象和setTimeout()就能实现。而你要做的就是决定什么时候发送请求。

第二种流行的comet实现是HTTP流。流不同于上述两种轮询,因为它在页面的整个生命周期内只使用一个HTTP连接。具体来说,就是浏览器向服务器发送一个请求,而服务器保持连接打开,然后周期性地向浏览器发送数据。使用XHR对象实现HTTP流的典型代码如下所示:

function createStreamingClient(url,progress,finished) {
    var xhr=new XMLHttpRequest();
    received=0;
    xhr.open("get",url,true);
    xhr.onreadystatechange=function () {
        var result;
        if(xhr.readyState==3){
            //只取得最新数据并调整计数器
            result=xhr.responseText.substring(received);
            received+=result.length;
            //调用progress回调函数
            progress(result);
        }else if (xhr.readyState==4){
            finished(xhr.responseText)
        }
    };
    xhr.send(null);
    return xhr;
}
var client=createStreamingClient("https://developer.mozilla.org/en-US/docs/Web/API/XDomainRequest",function (data) {
    alert("Received:"+data);
},function (data) {
    alert("Done!");
    })

这个createStreamingClient()函数接收三个参数:要连接的URL、在接收到数据时调用的函数以及关闭连接时调用的函数。有时候,当连接关闭时,很有可能还需要重新建立,所以关注连接什么时候关闭还是有必要的。
只要readystatechange事件发生,而且readyState值为3,就对responseText进行分割以取得最新数据。这里的received变量用于记录已经处理了多少字符,每次readyState值为3时都递增,然后,通过progress回调函数来处理传入的新数据。而当readyState值为4时,则执行finished回调函数,传入响应返回的内容。

这个简单的例子能在大多数浏览器中正常运行(IE除外),但管理Comet的连接很容易出错,需要时间不断改进才能达到完美。Comet是未来Web 的一个重要组成部分,为了简化这一技术,又为Comet创建了两个新的接口。

其他跨域技术

SSE(服务器发送事件)
Web Sockets
SSE+Web Sockets

HTTP

http详解及问题

HTTP协议

概况

HTTP是超文本传输协议,它定义了客户端和服务器之间的交换报文的格式和方式,默认使用80端口。它使用TCP作为传输层协议,保证了数据传输的可靠性。

HTTP 是一个无状态的协议,HTTP 服务器不会保存关于客户的任何信息。

HTTP 有两种连接模式,一种是持续连接,一种非持续连接。非持续连接指的是服务器必须为每一个请求的对象建立和维护 一个全新的连接。持续连接下,TCP 连接默认不关闭,可以被多个请求复用。采用持续连接的好处是可以避免每次建立 TCP 连接三次握手时所花费的时间。在 HTTP1.0 以前使用的非持续的连接,但是可以在请求时,加上 Connection: keep-a live 来要求服务器不要关闭 TCP 连接。HTTP1.1 以后默认采用的是持续的连接。目前对于同一个域,大多数浏览器支持 同时建立 6 个持久连接。

HTTP 请求报文

参考
HTTP 报文有两种,一种是请求报文,一种是响应报文。
HTTP 请求报文的格式如下:
屏幕快照 2020-05-14 下午7.45.27.png
HTTP 请求报文的第一行叫做请求行,后面的行叫做首部行,首部行后还可以跟一个实体主体。请求首部之后有一个空行,这 个空行不能省略,它用来划分首部与实体。

  • 请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。比如 GET /data/info.html HTTP/1.1
HTTP客户程序,向服务器发送请求的时候必须指明请求类型(一般是GET或者 POST)。如有必要,客户程序还可以选择发送其他的请求头。大多数请求头并不是必需的,但Content-Length除外。对于POST请求来说 Content-Length必须出现。
常见的请求头字段含义:
Accept: 浏览器可接受的MIME类型。
Accept-Charset:浏览器可接受的字符集。
Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间。
Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。
Authorization:授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中。
Content-Length:表示请求消息正文的长度。
Host: 客户机通过这个头告诉服务器,想访问的主机名。Host头域指定请求资源的Intenet主机和端口号,必须表示请求url的原始服务器或网关的位置。HTTP/1.1请求必须包含主机头域,否则系统会以400状态码返回。
If-Modified-Since:客户机通过这个头告诉服务器,资源的缓存时间。只有当所请求的内容在指定的时间后又经过修改才返回它,否则返回304“Not Modified”应答。
Referer:客户机通过这个头告诉服务器,它是从哪个资源来访问服务器的(防盗链)。包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。
User-Agent:User-Agent头域的内容包含发出请求的用户信息。浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用。
Cookie:客户机通过这个头可以向服务器带数据,这是最重要的请求头信息之一。
Pragma:指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝。
From:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它。
Connection:处理完这次请求后是否断开连接还是继续保持连接。如果Servlet看到这里的值为“Keep- Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入 ByteArrayOutputStream,然后在正式写出内容之前计算它的大小
Range:Range头域可以请求实体的一个或者多个子范围。例如,
表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999
但是服务器可以忽略此请求头,如果无条件GET包含Range请求头,响应会以状态码206(PartialContent)返回而不是以200 (OK)。
UA-Pixels,UA-Color,UA-OS,UA-CPU:由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU类型。
  • 方法字段就是HTTP使用的请求方法,方法字段可以取几种不同的值,一般有 GET、POST、HEAD、PUT 和 DELETE。
一般GET方法只被用于向服务器获取数据。
POST 方法用于将实体提交到指定的资源,通常会造成服务器资源的修改。
HEAD方法与 GET 方法类似,但是在返回的响应 中,不包含请求对象。PUT 方法用于上传文件到服务器,DELETE 方法用于删除服务器上的对象。虽然请求的方法很多,但更多表达的是一种语义上的区别,并不是说 POST 能做的事情,GET就不能做了,主要看我们如何选择。

HTTP 响应报文

HTTP 报文有两种,一种是请求报文,一种是响应报文。
HTTP 响应报文的格式如下:
屏幕快照 2020-05-14 下午7.45.48.png

HTTP响应报文也由三部分组成:响应行、响应头、响应体

  • 响应行一般由协议版本、状态码及其描述组成 比如 HTTP/1.1 200 OK

其中协议版本HTTP/1.1或者HTTP/1.0,200就是它的状态码,OK则为它的描述。

//常见状态码:
100~199:表示成功接收请求,要求客户端继续提交下一次请求才能完成整个处理过程。
200~299:表示成功接收请求并已完成整个处理过程。常用200
300~399:为完成请求,客户需进一步细化请求。例如:请求的资源已经移动一个新地址、常用302(意味着你请求我,我让你去找别人),307和304(我不给你这个资源,自己拿缓存)
400~499:客户端的请求有错误,常用404(意味着你请求的资源在web服务器中没有)403(服务器拒绝访问,权限不够)
500~599:服务器端出现错误,常用500
  • 响应头用于描述服务器的基本信息,以及数据的描述,服务器通过这些数据的描述信息,可以通知客户端如何处理等一会儿它回送的数据。

设置HTTP响应头往往和状态码结合起来。例如,有好几个表示“文档位置已经改变”的状态代码都伴随着一个Location头,而401(Unauthorized)状态代码则必须伴随一个WWW-Authenticate头。然而,即使在没有设置特殊含义的状态代码时,指定应答头也是很有用的。应答头可以用来完成:设置Cookie,指定修改日期,指示浏览器按照指定的间隔刷新页面,声明文档的长度以便利用持久HTTP连接,……等等许多其他任务。

常见的响应头字段含义:
Allow:服务器支持哪些请求方法(如GET、POST等)。
Content-Encoding:文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE4、IE5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader(“Accept- Encoding”))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。
Content-Length:表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入 ByteArrayOutputStram,完成后查看其大小,然后把该值放入Content-Length头,最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容。
Content- Type:表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置 Content-Type,因此HttpServletResponse提供了一个专用的方法setContentType。
Date:当前的GMT时间,例如,Date:Mon,31Dec200104:25:57GMT。Date描述的时间表示世界标准时,换算成本地时间,需要知道用户所在的时区。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。
Expires:告诉浏览器把回送的资源缓存多长时间,-1或0则是不缓存。
Last-Modified:文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,否则返回一个304(Not Modified)状态。Last-Modified也可用setDateHeader方法来设置。
Location:这个头配合302状态码使用,用于重定向接收者到一个新URI地址。表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302。
Refresh:告诉浏览器隔多久刷新一次,以秒计。
Server:服务器通过这个头告诉浏览器服务器的类型。Server响应头包含处理请求的原始服务器的软件信息。此域能包含多个产品标识和注释,产品标识一般按照重要性排序。Servlet一般不设置这个值,而是由Web服务器自己设置。
Set-Cookie:设置和页面关联的Cookie。Servlet不应使用response.setHeader(“Set-Cookie”, …),而是应使用HttpServletResponse提供的专用方法addCookie。
Transfer-Encoding:告诉浏览器数据的传送格式。
WWW-Authenticate:客户应该在Authorization头中提供什么类型的授权信息?在包含401(Unauthorized)状态行的应答中这个头是必需的。例如,response.setHeader(“WWW-Authenticate”, “BASIC realm=\”executives\”“)。注意Servlet一般不进行这方面的处理,而是让Web服务器的专门机制来控制受密码保护页面的访问。
注:设置应答头最常用的方法是HttpServletResponse的setHeader,该方法有两个参数,分别表示应答头的名字和值。和设置状态代码相似,设置应答头应该在发送任何文档内容之前进行。
setDateHeader方法和setIntHeadr方法专门用来设置包含日期和整数值的应答头,前者避免了把Java时间转换为GMT时间字符串的麻烦,后者则避免了把整数转换为字符串的麻烦。
HttpServletResponse还提供了许多设置
setContentType:设置Content-Type头。大多数Servlet都要用到这个方法。
setContentLength:设置Content-Length头。对于支持持久HTTP连接的浏览器来说,这个函数是很有用的。
addCookie:设置一个Cookie(Servlet API中没有setCookie方法,因为应答往往包含多个Set-Cookie头)。
  • 响应体就是响应的消息体,如果是纯数据就是返回纯数据,如果请求的是HTML页面,那么返回的就是HTML代码,如果是JS就是JS代码,如此之类。

首部行

首部可以分为四种首部,请求首部、响应首部、通用首部和实体首部。通用首部和实体首部在请求报文和响应报文中都可以设置,区别在于请求首部和响应首部。

  • 常见的请求首部有 Accept 可接收媒体资源的类型、Accept-Charset 可接收的字符集、Host 请求的主机名。
  • 常见的响应首部有 ETag 资源的匹配信息,Location 客户端重定向的 URI。
  • 常见的通用首部有 Cache-Control 控制缓存策略、Connection 管理持久连接。
  • 常见的实体首部有 Content-Length 实体主体的大小、Expires 实体主体的过期时间、Last-Modified 资源的最后修改时间。

HTTP/1.1 协议缺点

HTTP/1.1 默认使用了持久连接,多个请求可以复用同一个 TCP 连接,但是在同一个 TCP 连接里面,数据请求的通信次序 是固定的。服务器只有处理完一个请求的响应后,才会进行下一个请求的处理,如果前面请求的响应特别慢的话,就会造成许 多请求排队等待的情况,这种情况被称为“队头堵塞”。队头阻塞会导致持久连接在达到最大数量时,剩余的资源需要等待其他 资源请求完成后才能发起请求。

为了避免这个问题,一个是减少请求数,一个是同时打开多个持久连接。这就是我们对网站优化时,使用雪碧图、合并脚本的原因。

HTTP/2协议

2009 年,谷歌公开了自行研发的 SPDY 协议,主要解决 HTTP/1.1 效率不高的问题。这个协议在 Chrome 浏览器上证明 可行以后,就被当作 HTTP/2 的基础,主要特性都在 HTTP/2 之中得到继承。2015 年,HTTP/2 发布。

HTTP/2 主要有以下新的特性:

  • 二进制协议

HTTP/2 是一个二进制协议。在 HTTP/1.1 版中,报文的头信息必须是文本(ASCII 编码),数据体可以是文本,也可以是 二进制。HTTP/2 则是一个彻底的二进制协议,头信息和数据体都是二进制,并且统称为"帧",可以分为头信息帧和数据帧。 帧的概念是它实现多路复用的基础。

  • 多路复用

HTTP/2 实现了多路复用,HTTP/2 仍然复用 TCP 连接,但是在一个连接里,客户端和服务器都可以同时发送多个请求或回 应,而且不用按照顺序一一发送,这样就避免了"队头堵塞"的问题。

  • 数据流

HTTP/2 使用了数据流的概念,因为 HTTP/2 的数据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的 请求。因此,必须要对数据包做标记,指出它属于哪个请求。HTTP/2 将每个请求或回应的所有数据包,称为一个数据流。每 个数据流都有一个独一无二的编号。数据包发送的时候,都必须标记数据流 ID ,用来区分它属于哪个数据流。

  • 头信息压缩

HTTP/2 实现了头信息压缩,由于 HTTP 1.1 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是 重复的,比如 Cookie 和 User Agent ,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。

HTTP/2 对这一点做了优化,引入了头信息压缩机制。一方面,头信息使用 gzip 或 compress 压缩后再发送;另一方面, 客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引 号,这样就能提高速度了。

  • 服务器推送

HTTP/2 允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送。使用服务器推送,提前给客户端推送必要的资源 ,这样就可以相对减少一些延迟时间。这里需要注意的是 http2 下服务器主动推送的是静态资源,和 WebSocket 以及使用 SSE 等方式向客户端发送即时数据的推送是不同的。

HTTP/2 协议缺点

因为 HTTP/2 使用了多路复用,一般来说同一域名下只需要使用一个 TCP 连接。由于多个数据流使用同一个 TCP 连接,遵 守同一个流量状态控制和拥塞控制。只要一个数据流遭遇到拥塞,剩下的数据流就没法发出去,这样就导致了后面的所有数据都 会被阻塞。HTTP/2 出现的这个问题是由于其使用 TCP 协议的问题,与它本身的实现其实并没有多大关系。

HTTP/3 协议

由于 TCP 本身存在的一些限制,Google 就开发了一个基于 UDP 协议的 QUIC 协议,并且使用在了 HTTP/3 上。 QUIC 协议在 UDP 协议上实现了多路复用、有序交付、重传等等功能

HTTPS 协议

HTTP 存在的问题

HTTP 报文使用明文方式发送,可能被第三方窃听。
HTTP 报文可能被第三方截取后修改通信内容,接收方没有办法发现报文内容的修改。
HTTP 还存在认证的问题,第三方可以冒充他人参与通信。

HTTPS 简介

HTTPS 指的是超文本传输安全协议,HTTPS 是基于 HTTP 协议的,不过它会使用 TLS/SSL 来对数据加密。使用 TLS/ SSL 协议,所有的信息都是加密的,第三方没有办法窃听。并且它提供了一种校验机制,信息一旦被篡改,通信的双方会立 刻发现。它还配备了身份证书,防止身份被冒充的情况出现。

TLS 握手过程

  1. 第一步,客户端向服务器发起请求,请求中包含使用的协议版本号、生成的一个随机数、以及客户端支持的加密方法。
  2. 第二步,服务器端接收到请求后,确认双方使用的加密方法、并给出服务器的证书、以及一个服务器生成的随机数。
  3. 第三步,客户端确认服务器证书有效后,生成一个新的随机数,并使用数字证书中的公钥,加密这个随机数,然后发给服务器。并且还会提供一个前面所有内容的 hash 的值,用来供服务器检验。
  4. 第四步,服务器使用自己的私钥,来解密客户端发送过来的随机数。并提供前面所有内容的 hash 值来供客户端检验。
  5. 第五步,客户端和服务器端根据约定的加密方法使用前面的三个随机数,生成对话秘钥,以后的对话过程都使用这个秘钥来加密信息。

实现原理


TLS 的握手过程主要用到了三个方法来保证传输的安全。

首先是对称加密的方法,对称加密的方法是,双方使用同一个秘钥对数据进行加密和解密。但是对称加密的存在一个问题,就 是如何保证秘钥传输的安全性,因为秘钥还是会通过网络传输的,一旦秘钥被其他人获取到,那么整个加密过程就毫无作用了。 这就要用到非对称加密的方法。

非对称加密的方法是,我们拥有两个秘钥,一个是公钥,一个是私钥。公钥是公开的,私钥是保密的。用私钥加密的数据,只 有对应的公钥才能解密,用公钥加密的数据,只有对应的私钥才能解密。我们可以将公钥公布出去,任何想和我们通信的客户, 都可以使用我们提供的公钥对数据进行加密,这样我们就可以使用私钥进行解密,这样就能保证数据的安全了。但是非对称加 密有一个缺点就是加密的过程很慢,因此如果每次通信都使用非对称加密的方式的话,反而会造成等待时间过长的问题。

因此我们可以使用对称加密和非对称加密结合的方式,因为对称加密的方式的缺点是无法保证秘钥的安全传输,因此我们可以 非对称加密的方式来对对称加密的秘钥进行传输,然后以后的通信使用对称加密的方式来加密,这样就解决了两个方法各自存 在的问题。

但是现在的方法也不一定是安全的,因为我们没有办法确定我们得到的公钥就一定是安全的公钥。可能存在一个中间人,截取 了对方发给我们的公钥,然后将他自己的公钥发送给我们,当我们使用他的公钥加密后发送的信息,就可以被他用自己的私钥 解密。然后他伪装成我们以同样的方法向对方发送信息,这样我们的信息就被窃取了,然而我们自己还不知道。

为了解决这样的问题,我们可以使用数字证书的方式,首先我们使用一种 Hash 算法来对我们的公钥和其他信息进行加密生成 一个信息摘要,然后让有公信力的认证中心(简称 CA )用它的私钥对消息摘要加密,形成签名。最后将原始的信息和签名合 在一起,称为数字证书。当接收方收到数字证书的时候,先根据原始信息使用同样的 Hash 算法生成一个摘要,然后使用公证 处的公钥来对数字证书中的摘要进行解密,最后将解密的摘要和我们生成的摘要进行对比,就能发现我们得到的信息是否被更改 了。这个方法最要的是认证中心的可靠性,一般浏览器里会内置一些顶层的认证中心的证书,相当于我们自动信任了他们,只有 这样我们才能保证数据的安全。

事件绑定

addEventListener参数

element.addEventListener(type, function, useCapture)
【事件类型】,【事件处理程序】,【可选。布尔值,指定事件是否在捕获或冒泡阶段执行。true:事件句柄在捕获阶段执行false:(默认)事件句柄在冒泡阶段执行

捕获

捕获则和冒泡相反,目标元素被触发后,会从目标元素的最顶层的祖先元素事件往下执行到目标元素为止。
addEventListener函数的第三个参数均改为true,事件为捕获阶段执行。
注意: 在事件处理程序中删除目标元素也能阻止事件冒泡,目标元素在文档中是事件冒泡的前提

冒泡

冒泡是从下向上,DOM元素绑定的事件被触发时,此时该元素为目标元素,目标元素执行后,它的的祖元素绑定的事件会向上顺序执行。
addEventListener函数的第三个参数设置为false说明不为捕获事件,即为冒泡事件。

当一个元素绑定两个事件,一个冒泡,一个捕获

首先,无论是冒泡事件还是捕获事件,元素都会先执行捕获阶段。
从上往下,如有捕获事件,则执行;一直向下到目标元素后,从目标元素开始向上执行冒泡元素,即第三个参数为true表示捕获阶段调用事件处理程序,如果是false则是冒泡阶段调用事件处理程序。(在向上执行过程中,已经执行过的捕获事件不再执行,只执行冒泡事件。

事件执行顺序

执行次数:绑定了几个事件便执行几次
例如:two元素绑定了两个不同事件,点击two都会执行这两个事件。而执行顺序有所差异。

<div id='one'> 
    <div id='two'> 
        <div id='three'> 
            <div id='four'> 
            </div> 
        </div> 
    </div>
</div>
one.addEventListener('click',function(){
alert('one');
},true);
two.addEventListener('click',function(){
alert('two,bubble');
},false);
two.addEventListener('click',function(){
alert('two,capture');
},true);
three.addEventListener('click',function(){
alert('three,bubble');
},true);
four.addEventListener('click',function(){
alert('four');
},true);
1、如果two为目标元素,目标元素的同类型事件按顺序执行,而其他元素根据W3C的标准执行,即先捕获后冒泡。

点击two执行结果:
one(因为是two的父元素支持捕获事件所以先执行)
two,bubble
two,capture(顺序执行,注意逗号不是间隔,是输出内容。)

2、如果目标元素不是two,则two的同类型事件按先捕获后冒泡触发执行,也就是跟前面讨论的执行过程是一样的,只不过两个事件都绑定在同一个DOM元素上。

点击three执行结果:
one
two,capture
three,bubble
two,bubble

所以

绑定在被点击元素的事件是按照代码顺序发生
其他元素通过冒泡或者捕获“感知”的事件。
按照W3C的标准,先发生捕获事件,后发生冒泡事件。所有事件的顺序是:
其他元素捕获阶段事件 -> 本元素代码顺序事件 -> 其他元素冒泡阶段事件

日常输入输出
9 声望0 粉丝

愿望是成为优秀的前端工程师