0%

TCP传输连接有三个阶段:连接建立、数据传送、连接释放。

在TCP连接建立过程中要解决三个问题:

1.要使每一方能够确知对方的存在。

2.要允许双方协商一些参数(比如最大窗口值、是否使用时间戳选项等)

3.能够运输实体资源(比如缓存大小,连接表中的项目等)进行分配

使用三次握手的原因

首先我们需要知道为什么要有三次握手: 主要是为了防止已经失效的连接请求报文段突然又传送到了服务器B,从而发生错误。

TCP传输连接管理 - 连接建立 - 三次握手

客户端A,服务器端B

AB都要先创建传输控制模块TCB。

第一次握手:

客户端向服务器端发起请求,同步位SYN=1,序列号seq = x。(SYN=1的报文段不能携带数据)
这时,客户端进入到 SYN-SENT状态,即同步已发送。

第二次握手

服务器B收到请求的报文段后,如同意建立连接,那么就向A发送确认。 同步位SYN=1,ACK=1,确认号ack=x+1,同时自己也选择一个初始的序列号 seq = y
这时,服务器端进入SYN-RCVD状态,即同步收到。

第三次握手

客户端收到服务器端的确认之后还要给服务器发送确认。确认报文段 ACK = 1,确认号ack = y+1,自己的序列号seq = x+1。 此时连接建立,A进入ESTABLISHED状态,即连接已经建立。
当B收到A发来的确认报文时,也进入ESTABLISHED状态。

tcp三次握手的过程,accept发生在三次握手哪个阶段?
发生在三次握手之后,三次握手完成后,客户端和服务器就建立了tcp连接。这时可以调用accept函数获得此连接。

TCP和UDP的区别?

  • TCP是基于连接的协议,也就是说,在正式收发数据前,必须和对方简历可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来
  • UDP是与TCP相应的协议。他是面向非连接的协议,他不与对方建立连接,而是直接就把数据包发送过去了
  • UDP适用于一次只传送少量数据,对可靠性要求不高的应用环境

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是应用层上的一种客户端/服务端模型的通信协议,它由请求和响应构成。

HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。

HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。

HTTP特点

  1. 协议:协议规定了通信双方必须遵循的数据传输格式,这样通信双方按照约定的格式才能准确的通信。

  2. 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。

  3. 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。

  4. 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。

  5. 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

  6. 支持B/S及C/S模式。

HTTP使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。URL是一种特殊类型的URI,包含了用于查找某个资源的足够的信息,URL,全称是Uniform Resource Locator, 中文叫统一资源定位符,是互联网上用来标识某一处资源的地址。

影响一个 HTTP 网络请求的因素主要有两个:带宽和延迟。

  • 带宽:如果说我们还停留在拨号上网的阶段,带宽可能会成为一个比较严重影响请求的问题,但是现在网络基础建设已经使得带宽得到极大的提升,我们不再会担心由带宽而影响网速,那么就只剩下延迟了。

  • 延迟:

    • 浏览器阻塞(HOL blocking):浏览器会因为一些原因阻塞请求。浏览器对于同一个域名,同时只能有 4 个连接(这个根据浏览器内核不同可能会有所差异),超过浏览器最大连接数限制,后续请求就会被阻塞。

    • DNS 查询(DNS Lookup):浏览器需要知道目标服务器的 IP 才能建立连接。将域名解析为 IP 的这个系统就是 DNS。这个通常可以利用DNS缓存结果来达到减少这个时间的目的。

    • 建立连接(Initial connection):HTTP 是基于 TCP 协议的,浏览器最快也要在第三次握手时才能捎带 HTTP 请求报文,达到真正的建立连接,但是这些连接无法复用会导致每次请求都经历三次握手和慢启动。三次握手在高延迟的场景下影响较明显,慢启动则对文件类大请求影响较大。

HTTP1.0和HTTP1.1的一些区别

HTTP1.0最早在网页中使用是在1996年,那个时候只是使用一些较为简单的网页上和网络请求上,而HTTP1.1则在1999年才开始广泛应用于现在的各大浏览器网络请求中,同时HTTP1.1也是当前使用最为广泛的HTTP协议。 主要区别主要体现在:

  1. 缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。

  2. 带宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。

  3. 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。

  4. Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。

  5. 长连接,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。

HTTP2.0和HTTP1.X相比的新特性

  • 新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮,二进制协议解析起来更高效、“线上”更紧凑,更重要的是错误更少。

  • 多路复用(MultiPlexing),即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。

  • header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。

  • 服务端推送(server push),同SPDY一样,HTTP2.0也具有server push功能。当浏览器请求一个网页时,服务器将会发回HTML,在服务器开始发送JavaScript、图片和CSS前,服务器需要等待浏览器解析HTML和发送所有内嵌资源的请求。服务器推送服务通过“推送”那些它认为客户端将会需要的内容到客户端的缓存中,以此来避免往返的延迟。

HTTP之请求消息Request

客户端发送一个HTTP请求到服务器的请求消息包括以下格式:

请求行(request line)、请求头部(header)、空行和请求数据四个部分组成。
第一部分:请求行,用来说明请求类型,要访问的资源以及所使用的HTTP版本.

GET说明请求类型为GET, 访问的资源,该行的最后一部分说明使用的是HTTP1.1版本。

第二部分:请求头部,紧接着请求行(即第一行)之后的部分,用来说明服务器要使用的附加信息

从第二行起为请求头部,HOST将指出请求的目的地.User-Agent,服务器端和客户端脚本都能访问它,它是浏览器类型检测逻辑的重要基础.该信息由你的浏览器来定义,并且在每个请求中自动发送

第三部分:空行,请求头部后面的空行是必须的

即使第四部分的请求数据为空,也必须有空行。

第四部分:请求数据也叫主体,可以添加任意的其他数据。

HTTP之响应消息Response

一般情况下,服务器接收并处理客户端发过来的请求后会返回一个HTTP的响应消息。

HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
第一部分:状态行,由HTTP协议版本号, 状态码, 状态消息 三部分组成。

状态行,(HTTP/1.1)表明HTTP版本为1.1版本,状态码为200,状态消息为(ok)

第二部分:消息报头,用来说明客户端要使用的一些附加信息

Date:生成响应的日期和时间;Content-Type:指定了MIME类型的HTML(text/html),编码类型是UTF-8

第三部分:空行,消息报头后面的空行是必须的
第四部分:响应正文,服务器返回给客户端的文本信息。

空行后面的html部分为响应正文。

请求和响应常见通用头

  • Content-type:请求体/响应体的类型,如:text/plain,application/json(json数据格式),application/xml(xml数据格式),
  • Accept:说明接收的类型,可以多个值,用逗号分开
  • Content-length: 请求体/响应体的长度,单位字节
  • Content-Encoding:请求体/响应体的编码格式,如gzip,deflate
  • Accept-Encoding: 告知对方我方接受的Content-Encoding
  • ETag:给当前资源的标识,和Last-Modified,If-None-Match,If-Modified-since配合,用于缓存控制
  • Cache-Control:取值一般为no-cache或max-age=xx,xx为整数,表示该资源缓存有效期(秒)
  • Connection:链接的管理
  • Transfer-Encoding:报文主体的传输编码方式
  • Date:创建报文的时间

常见的请求头:

  1. Authorization:用于设置身份认证信息
  2. Accept:可处理的媒体类型
  3. Accept-Encoding:可接收的内容编码
  4. Accept-Language:可接收的自然语言
  5. User-Agent:用户标识,如:OS和浏览器的类型和版本
  6. If-Modified-Since:值为上一次服务器返回的Last-Modified值,用于确认某个资源是否被更改过,没有更改过就从缓存中读取
  7. If-None-Match: 值为上一次服务器返回的TTag值,一般会和If-Modified-Since一起出现
  8. Cookie: 已有的Cookie
  9. Refer:表示请求引用自哪个地址,比如从A页面跳转到B页面时,值为页面A的地址
  10. Host:请求资源所在的服务器的主机和端口号

常见的响应头:

  1. Date:服务器的日期
  2. Accept-Ranges:可接受的字节范围
  3. Last-Modified-Fied:该资源最后被修改的时间
  4. Transfer-Encoding: 取值一般为Chunked
  5. Set-Cookie:设置Cookie
  6. Location:重定向到另一个URL,如输入浏览器baidu.com回车,会自动跳到https://www.baidu.com,就是通过响应头控制的。
  7. Server:后台服务器的信息
  • 实体首部字段(请求报文与响应报文的的实体部分使用的首部字段)

    • Allow:资源可支持的HTTP方法
    • Content-Type:实体主类的类型
    • Content-Encoding:实体主体适用的编码方式
    • Content-Language:实体主体的自然语言
    • Content-Length:实体主体的的字节数
    • Content-Range:实体主体的位置范围,一般用于发出部分请求时使用

HTTP工作原理

HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。

以下是 HTTP 请求/响应的步骤:

1、客户端连接到Web服务器

一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接。

2、发送HTTP请求

通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成

3、服务器接受请求并返回HTTP响应

Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。

4、释放连接TCP连接

若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;

5、客户端浏览器解析HTML内容

客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。

例如:在浏览器地址栏键入URL,按下回车之后会经历以下流程:

1、读取缓存: 搜索自身的 DNS 缓存(Domain Name System 的缩写,作用就是根据域名查出IP地址。)。(如果 DNS 缓存中找到IP 地址就跳过了接下来解析 IP 地址步骤,直接访问该 IP 地址。) 如果在DNS缓存中没有找到对应的IP则浏览器向 DNS 服务器请求解析该 URL 中的域名所对应的 IP 地址;

2、解析出 IP 地址后,根据该 IP 地址和默认端口 80,和服务器建立TCP连接

3、浏览器发出读取文件(URL 中域名后面部分对应的文件)的HTTP 请求,该请求报文作为TCP 三次握手的第三个报文的数据发送给服务器;

4、服务器对浏览器请求作出响应,并把对应的 html 文本发送给浏览器;

5、释放TCP连接;

6、浏览器将该 html 文本并显示内容;  

HTTP中存在的问题:

  1. 窃听风险:通信使用明文传输,内容可能会被窃听(第三方可能获知通信内容)
  2. 冒充风险:不验证通信方的身份,因此有可能遭遇伪装。
  3. 篡改风险:无法证明报文的完整性,所以有可能已遭篡改。

HTTPS不是应用层一个新的协议,通常HTTP直接和TCP通信,HTTPS则是先和安全层(SSL/TLS)通信,然后安全层在和TCP层通信。SSL/TLS协议就是为了解决HTTP存在的问题而生的。解决方法:

  1. 所有的信息都是加密传输,第三方无法窃听。
  2. 配备身份验证,防止身份被冒充。
  3. 具备校验机制,一旦被篡改,通信双方会立刻发现。

加密和解密同用一个密钥的方式成为共享密钥加密也称为对称密钥加密。

HTTPS使用对称加密和非对称加密结合。

 

互联网的层划分

七层划分为:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层

五层划分为:应用层、传输层、网络层、数据链路层、物理层

四层划分为:应用层、传输层、网络层、网络接口层

应用层: 文件传输,电子邮件,文件服务,虚拟终端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet

表示层 :数据格式化,代码转换,数据加密 没有协议

会话层: 解除或建立与别的接点的联系 没有协议

传输层 :提供端对端的接口 TCP,UDP

网络层 :为数据包选择路由 IP,ICMP,RIP,OSPF,BGP,IGMP

数据链路层 :传输有地址的帧以及错误检测功能 SLIP,CSLIP,PPP,ARP,RARP,MTU

物理层: 以二进制数据形式在物理媒体上传输数据 ISO2110,IEEE802,IEEE802.2

目前为止接触的项目开发均是前后端分离模式,前端利用从后台获取的数据渲染到页面上。之前用的基本都是客户端渲染,最近接触了服务端渲染。

我到目前为止接触的项目开发均是前后端分离模式,前端利用从后台获取的数据渲染到页面上。之前用的基本都是客户端渲染,最近接触了服务端渲染。

最近的开发使用了Next.js,Next是一个轻量级的React服务端渲染应用框架 。一直不是很清楚服务端渲染(server side render)和客户端(client side render)渲染的具体实现,于是网上搜罗了一番,下面简单记录一下。

一. 客户端渲染(CSR)

客户端发起页面请求,服务端把页面(响应的字符串)发送给客户端,客户端从上到下依次解析,解析过程中,发现网络请求,再次向服务器发送网络请求,客服端拿到响应的结果,模板引擎渲染到HTML页面。

优点:灵活,真正的前后端分离,方便于前后台各自更新维护。

缺点: 对SEO不友好,增加了HTTP请求的次数,减缓了页面加载速度。

二. 服务端渲染(SSR)

在后端看来,页面文件其实就是一个“字符串”,所以服务端完全可以在获取到HTML文件的内容之后经过一些处理再返回给客户端,也就说,服务端可以将数据插入到HTML 字符串中之后再返回给客户端

优点:对 SEO搜索引擎友好,减少了HTTP请求的次数,加速了页面初次渲染的速度

缺点:不灵活,前后端耦合太深。

在 SPA 模式下,所有的数据请求和 Dom 渲染都在浏览器端完成,所以当我们第一次访问页面的时候很可能会存在“白屏”等待,而服务端渲染所有数据请求和 html内容已在服务端处理完成,浏览器收到的是完整的 html 内容,可以更快的看到渲染内容,在服务端完成数据请求肯定是要比在浏览器端效率要高的多。

服务端渲染和客户端渲染结合

网站的流量来源主要还是靠搜索引擎,所以网站的 SEO 还是很重要的,而客户端渲染模式对搜索引擎不够友好,要想彻底解决这个问题只能采用服务端渲染。

最好的方案就是服务端渲染和客户端渲染的结合,第一次访问页面是服务端渲染,基于第一次访问后续的交互就是客户端渲染的效果和体验,同时还不影响SEO效果。

Next.js 中的服务端渲染和客户端渲染

getiInitialProps 是 Next.js 最伟大的发明,它确定了一个规范,一个页面组件只要把访问 API 外部资源的代码放在 getInitialProps 中就足够,其余的不用管,Next.js 自然会在服务器端或者浏览器端调用 getInitialProps 来获取外部资源,并把外部资源以 props 的方式传递给页面组件。

getInitialProps()是可以在服务端运行的,当页面第一次加载时,服务器收到请求,getInitialProps()会执行,getInitialProps()返回的数据,会序列化后添加到 window.__NEXT_DATA__.props上,写入HTML源码里,类似于。这样服务端的getInitialProps()就实现了把数据传送给了客户端。当我们通过Next.js的路由Link来进行页面跳转的时候,客户端就会从window.__NEXT_DATA__里获取数据渲染页面,就无需重新获取数据。

方法一:基于视口(浏览器窗口)的垂直居中

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>垂直居中</title>
</head>
<style>
.wrapper {
overflow: hidden;
width: 1000px;
height: 500px;
background: #999;
}
.center {
width: 18em;
height: 10em;
text-align: center;
background-color: orange;
color: #fff;


/* 1vh = 1% * 视口高度 */
margin: 50vh auto;
transform: translateY(-50%);
}

</style>
<body>
<div class="wrapper">
<div class="center">
基于视口的垂直居中<br />
不要求原生有固定的宽高。<br />
但是这种居中是在整个页面窗口内居中,不是基于父元素<br />

</div>
</div>
</body>
</html>
Copy

方法二:定宽居

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>垂直居中</title>
</head>
<style>
.center {
width: 18em;
height: 10em;
text-align: center;
background-color: orange;
color: #fff;

position: absolute;
top: 50%;
left: 50%;
margin-left: -9rem;
margin-top: -5rem;
}
</style>
<body>
<div class="center">
要求原生有固定的宽高。<br/>
position: absolute;<br/>
top和left 为 50%;<br/>
margin上为高的一半<br/>
margin左为宽的一半<br/>
</div>

</body>
</html>
Copy

方法三:calc居中

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>垂直居中</title>
</head>
<style>
.center {
width: 18em;
height: 10em;
text-align: center;
background-color: orange;
color: #fff;

position: absolute;
top: calc(50% - 5em);
left: calc(50% - 9em);
}
</style>
<body>
<div class="center">
要求原生有固定的宽高。<br/>
position: absolute;<br/>
top 为 calc(50% 剪 一半高)
left 为 calc(50% 剪 一半宽)
</div>

</body>
</html>
Copy

方法四:transform居中

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>垂直居中</title>
</head>
<style>
.center {
width: 18em;
height: 10em;
text-align: center;
background-color: orange;
color: #fff;

position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
<body>
<div class="center">
不要求原生有固定的宽高。<br/>
position: absolute;<br/>
top和left 为 50%;<br/>
transform: translate(-50%, -50%);
</div>

</body>
</html>
Copy

方法五:flex居中方法一

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>垂直居中</title>
</head>
<style>
.wrapper {
width: 1000px;
height: 600px;
background: #999;

display: flex;
}
.center {
width: 18em;
height: 10em;
text-align: center;
background-color: orange;
color: #fff;

margin: auto;
}
</style>
<body>
<div class="wrapper">
<div class="center">
使用flex居中<br/>
父元素 display: flex; <br/>
居中块: margin: auto;
</div>
</div>
</body>
</html>
Copy

方法六: flex居中方法二

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>垂直居中</title>
</head>
<style>
.wrapper {
width: 1000px;
height: 600px;
background: #999;

display: flex;
/* 盒子横轴的对齐方式 */
justify-content: center;
/* 盒子纵轴的对齐方式 */
align-items: center;
}
.center {
width: 18em;
height: 10em;
text-align: center;
background-color: orange;
color: #fff;
}
</style>
<body>
<div class="wrapper">
<div class="center">
使用flex居中<br/>
父元素 display: flex; <br/>
justify-content: center;<br/>
align-items: center;<br/>
</div>
</div>
</body>
</html>

写了这末多项目了,每个项目的页面布局都离不开Flex布局,就连前段时间写的小程序项目都是用的Flex布局,下面简单整理一下Flex布局相关知识点。

Flex 是 Flexible Box 的缩写,Flex Box布局称为Flex布局弹性布局

直接设置flex布局的元素称为flex容器,它里面的子元素称为flex项目。

Flex容器默认有两根轴,主轴和交叉轴,交叉轴垂直于主轴。

一. 作用在flex容器上的相关属性

  1. flex-direction

    flex-direction属性决定主轴的方向(即项目的排列方向)。

    1
    2
    3
    .box {
    flex-direction: row | row-reverse | column | column-reverse;
    }
    • row(默认值):主轴为水平方向,起点在左端。

    • row-reverse:主轴为水平方向,起点在右端。

    • column:主轴为垂直方向,起点在上沿。

    • column-reverse:主轴为垂直方向,起点在下沿。

  2. flex-wrap

    默认情况下,项目都排在一条线(又称”轴线”)上。flex-wrap属性定义,如果一条轴线排不下,如何换行。

    1
    2
    3
    .box{
    flex-wrap: nowrap | wrap | wrap-reverse;
    }
    • nowrap(默认):不换行。

    • wrap:换行,第一行在上方。

    • wrap-reverse:换行,第一行在下方。

  3. flex-flow

    flex-flow属性是flex-directionflex-wrap的缩写,表示flex布局的flow流动特性,默认值为row nowrap

    1
    2
    3
    4
    5
    6
    7
    flex-flow: <‘flex-direction’> || <‘flex-wrap’>

    //例子:
    .container {
    display: flex;
    flex-flow: row-reverse wrap-reverse;
    }
  4. justify-content

    justify-content属性定义了项目在主轴上的对齐方式。

    1
    2
    3
    .box {
    justify-content: flex-start | flex-end | center | space-between | space-around;
    }
    • flex-start(默认值):左对齐
    • flex-end:右对齐
    • center: 居中
    • space-between:两端对齐,项目之间的间隔都相等。
    • space-around:每个项目两侧的间隔相等。项目之间的间隔比项目与边框的间隔大一倍。
  5. align-items

    align-items中的items指的就是flex子项目,因此align-items指的就是flex子项目相对于flex容器在垂直方向(交叉轴)上的对齐方式

    1
    2
    3
    .box {
    align-items: flex-start | flex-end | center | baseline | stretch;
    }
    • flex-start:交叉轴的起点对齐。
    • flex-end:交叉轴的终点对齐。
    • center:交叉轴的中点对齐。
    • baseline: 项目的第一行文字的基线对齐。
    • stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。
  6. align-content

    align-content可以看成和justify-content是相似且对立的属性,justify-content指明水平方向flex子项的对齐和分布方式,而align-content则是指明垂直方向每一行flex元素的对齐和分布方式。如果所有flex子项只有一行,则align-content属性是没有任何效果的

    1
    2
    3
    .box {
    align-content: flex-start | flex-end | center | space-between | space-around | stretch;
    }
    • flex-start:与交叉轴的起点对齐。
    • flex-end:与交叉轴的终点对齐。
    • center:与交叉轴的中点对齐。
    • space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
    • space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
    • stretch(默认值):轴线占满整个交叉轴。

二. 作用在子项目上的相关属性

  1. order

    order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。

    1
    2
    3
    .item {
    order: <integer>;
    }
  2. flex-grow

    flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。

    1
    2
    3
    .item {
    flex-grow: <number>; /* default 0 */
    }
  3. flex-shrink

    flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。负值对该属性无效。

    1
    2
    3
    .item {
    flex-shrink: <number>; /* default 1 */
    }

    如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。

  4. flex-basis

    flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。

    1
    2
    3
    .item {
    flex-basis: <length> | auto; /* default auto */
    }

    它可以设为跟widthheight属性一样的值(比如350px),则项目将占据固定空间。

  5. flex

    flex属性是flex-grow, flex-shrinkflex-basis的简写,默认值为0 1 auto。后两个属性可选。

    1
    2
    3
    .item {
    flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
    }

    该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。

    建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。

  6. align-self

    align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch

    1
    2
    3
    .item {
    align-self: auto | flex-start | flex-end | center | baseline | stretch;
    }
注:

在Flex布局中,flex子元素的设置float,clear以及vertical-align属性都是没有用的。

Flexbox布局最适合小规模布局(一维布局),而Grid布局则适用于更大规模的布局(二维布局)。

最近老大让我调整一下图片懒加载的视觉效果,作为一个电商平台,懒加载是非常重要的。我们使用的懒加载库react-lazyload,现在的浏览效果是要等到整个图片高度都完全出现在可视区域的时候图片才会加载出来,这样看着让人怀疑到是不是网络不好,用户体验很不好,后来仔细阅读了react-lazyload库,发现使用offset属性可以预加载出图片。例如,如果您想预加载一个组件,使它在viewport下面100px(offset=100)就加载该组件,让用户看不到延迟加载的效果。下面简单整理了一下懒加载和预加载。

一.懒加载

1. 什么是懒加载?

懒加载也称为延迟加载或惰性加载,一般指在长网页中延迟加载图像,是一种优化网页性能的方式。用户滚动到它们之前,可视区域外的图像不会加载。这与图像预加载相反,在长网页上使用延迟加载将使网页加载更快。在某些情况下,它还可以帮助减少服务器负载。常适用图片很多,页面很长的电商网站场景中。

2. 使用懒加载的好处?
  • 能提升用户的体验,不妨设想下,用户打开像手机淘宝长页面的时候,如果页面上所有的图片都需要加载,由于图片数目较大,等待时间很长,用户难免会心生抱怨,这就严重影响用户体验
  • 减少无效资源的加载,这样能明显减少了服务器的压力和流量,也能够减小浏览器的负担。
  • 防止并发加载的资源过多会阻塞js的加载,影响网站的正常使用。
3.懒加载的原理

首先将页面上的图片的 src 属性设为空字符串,而图片的真实路径则设置在data-original属性中, 当页面滚动的时候需要去监听scroll事件,在scroll事件的回调中,判断我们的懒加载的图片是否进入可视区域,如果图片在可视区内将图片的 src 属性设置为data-original 的值,这样就可以实现延迟加载。

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
52
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Lazyload</title>
<style>
.image-item {
display: block;
margin-bottom: 50px;
height: 200px;//一定记得设置图片高度
}
</style>
</head>
<body>
<img src="" class="image-item" lazyload="true" data-original="images/1.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/2.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/3.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/4.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/5.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/6.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/7.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/8.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/9.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/10.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/11.png"/>
<img src="" class="image-item" lazyload="true" data-original="images/12.png"/>
<script>
var viewHeight =document.documentElement.clientHeight//获取可视区高度
function lazyload(){
var eles=document.querySelectorAll('img[data-original][lazyload]'
Array.prototype.forEach.call(eles,function(item,index){
var rect
if(item.dataset.original==="")
return
rect=item.getBoundingClientRect()// 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
if(rect.bottom>=0 && rect.top < viewHeight){
!function(){
var img=new Image()
img.src=item.dataset.original
img.onload=function(){
item.src=img.src
}
item.removeAttribute("data-original"//移除属性,下次不再遍历
item.removeAttribute("lazyload"
}()
}
})
}
lazyload()//刚开始还没滚动屏幕时,要先触发一次函数,初始化首页的页面图片
document.addEventListener("scroll",lazyload)
</script>
</body>
</html>

二. 预加载

  1. 什么是预加载?

    预加载简单来说就是将所有所需的资源提前请求加载到本地,当用户需要查看时可直接从本地缓存中渲染

  2. 为什么使用预加载?

    图片预先加载到浏览器中,访问者便可顺利地在你的网站上冲浪,并享受到极快的加载速度。这对图片画廊及图片占据很大比例的网站来说十分有利,它保证了图片快速、无缝地发布,也可帮助用户在浏览你网站内容时获得更好的用户体验。

预加载和懒加载都是提高页面性能有效的办法,两者主要区别是一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。

今天安卓说发现低版本的手机打不开我写的h5页面,看了他电脑上的运行日志发现了问题,Uncaught TypeError: Object.assign is not a function

1
08-09 10:28:50.011 8628-8628/com.cheezgroup.tosharing.intl I/chromium: [INFO:CONSOLE(41480)] "Uncaught TypeError: Object.assign is not a function", source: http://192.168.1.16:3000/_next/static/development/pages/_app.js?ts=1565317716331 (41480)

网上搜了一波,发现了问题是因为Babel是不能转换Object.assign方法的。

如果想让这Object.assign运行,必须使用babel-polyfill,为当前环境提供一个polyfill,但是考虑到babel-polyfill太大,不建议安装。建议手动写一个polyfill文件,需要哪些polyfill就手动加上,然后在app.js中导入即可。

然后找了一个polyfill如下:

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
if (typeof Object.assign !== "function") {
Object.assign = function(target) {
"use strict";
if (target === null) {
// TypeError if undefined or null
throw new TypeError("Cannot convert undefined or null to object");
}

var to = Object(target);

for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];

if (nextSource !== null) {
// Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
};
}

编译运行还是报错,发现网上大多数都是这样写的,没什么差别,老大说人家都能执行,你的不行肯定是在函数初始化的时候就错了,后来发现根本就没运行上面的代码,那就是判断条件可能出错了,做了如下修改,运行成功:

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
if (!Object.prototype.assign || typeof Object.assign !== "function") {
Object.assign = function(target) {
"use strict";
if (target === null) {
// TypeError if undefined or null
throw new TypeError("Cannot convert undefined or null to object");
}

var to = Object(target);

for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];

if (nextSource !== null) {
// Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
};
}

总结:多实践,多尝试,比较差异。

今天想把本地的hexo文件夹提交到GitHub仓库,提交是成功了,但是发现主题文件夹themes/next文件为空,运行git status 时提示 changes not staged for commit:,下面记录一下解决的方法。

通过查询得出了问题的原因是:如果git仓库下面还有另外一个clone过来的git仓库,那么上传到仓库的时候,其中的另一个克隆过来的文件夹一定是空的:

解决方法:

  1. 首先删除themes/next文件夹中的 .git文件

  2. 在hexo/根目录下删除仓库中的空文件夹

    1
    2
    3
    4
    5
    //删除暂存区或分支上的文件
    git rm -r --cached "themes/next"

    git commit -m "remove empty themes/next"
    git push origin master
  3. 在hexo/根目录下重新提交

    1
    2
    3
    git add .
    git commit -m "repush"
    git push origin master

至此到GitHub仓库中就能看到themes/next文件夹不是空的了。

总结:因为该项目本身就是一个仓库,项目中clone 下来的主题文件也是一个 git 仓库,因此在该项目下正常的 git add . 是无法提交该项目下另一个clone过来的文件夹的,所以我们要做的就是删除项目下另一个clone的文件夹下的 .git 文件夹,这样就可以通过 git add . 提交该项目。

最近在更新博客,顺便就加个评论的功能,网上找了许多的插件,来必力,畅言,友言等。我使用的是NexT主题,NexT主题集成了评论功能,这里选择的是来必力。

来必力使用社交网站账户登录,免去注册过程。提高用户的参与和沟通意愿。管理/删除我的评论内容。提供管理页面,管理网站文章及评论内容。没有匿名评论,支持邮箱账号注册评论。

添加步骤:

  1. 来必力的官网上注册账号。

  2. 获取data-uid

  3. 打开NexT主题的配置文件_config.yml,找到livere_uid,填写data-uid

    1
    livere_uid: MTAyMC80NTM0OS8yMTg2Mg==
  4. 回到来必力,修改设置

最后重新生成和部署博客:

1
2
3
hexo clean
hexo g
hexo d

至此就可以看到博客上的评论框了:

以上就是添加来必力评论的步骤。

最近重新整理和利用最新版本的hexo+next重构了一下我 的博客,下面简单记录了搭建博客的完整过程。

一、环境准备

  1. Node.js,点击下载安装,因为Hexo需用通过npm安装,而npm需要node,只要安装node 就会自带npm
  2. Git, 点击下载安装这篇博客有Git的常用命令解析
  3. Github账号,如果没有账号,点击这里注册

二、在GitHub上创建Github Pages项目

创建新仓库

创建一个名称为yourusername.github.io的新仓库即可。这边的yourusername填写自己的用户名。Github会识别并自动将该仓库设为Github Pages。

然后设置github用户和邮箱:

1
2
git config --global user.name "your name"
git config --global user.name "your email"

生成SSH密钥

执行以下命令:

ssh-keygen -t rsa -C "Github的注册邮箱地址"

然后,在C:\Users\ASUS\.ssh目录会有两个文件id_rsaid_rsa.pub,打开id_rsa.pub,复制里面的所有内容到 SSH keys这里 的Key,Title随便填,然后Add SSH key就可以了

三、安装Hexo

Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。

首页进入任意磁盘创建文件夹,这里文件夹命名为hexo,在该文件夹下鼠标右键选择Git Bash Here,执行命令:

1
$ npm install -g hexo-cli

接着执行:

1
2
hexo init
npm install

新建完成后,指定文件夹的目录如下:

1
2
3
4
5
6
7
8
.
├── _config.yml
├── package.json
├── scaffolds
├── source
| ├── _drafts
| └── _posts
└── themes

_config.yml文件中填上博客的基本信息,网站的 详细配置点击这里查看 。注意,冒号后面都要有一个空格。

四、部署

_config.yml文件,找到deploy,进行以下配置:

1
2
3
4
deploy:
type: git
repo: https://github.com/username/username.github.io.git
branch: master

安装部署工具

1
npm install hexo-deployer-git --save

新建一篇博客文章,在hexo/source/_posts文件夹下新建md文件(不用加.md后缀)

1
hexo new 文件名

最后输入以下命令生成网站文件并部署:

1
2
3
hexo g //生成网页文件
hexo s //localhost:4000本地预览效果
hexo d //部署

在浏览器输入https://username.github.io/就能看到博客网站了。

五、用Next美化博客

Hexo安装后,默认主题是landscape,这款主题相信你不会很喜欢,接下来讲解目前很火的一款主题-NexT主题的使用。

在hexo文件夹下鼠标右键选择Git Bash Here,然后clone next主题:

1
git clone https://github.com/iissnan/hexo-theme-next themes/next

更新主题NexT:

1
2
cd themes/next
git pull

切换成NexT主题,在hexo根文件夹下,修改_config.yml文件中的theme:

1
2
3
4
5
6
7
theme: next

//切换后,用命令清除下缓存
hexo clean

//执行hexo s本地产看NexT主题效果
hexo s

切换next主题的风格,修改hexo/theme/next/_config.yml

1
2
3
4
5
# Schemes
#scheme: Muse
#scheme: Mist
scheme: Pisces
#scheme: Gemini

设置Menu

默认只有首页和归档,如果还要添加,编辑hexo/themes/next/_config.yml

1
2
3
4
5
6
7
8
menu:
home: / || home //首页
about: /about/ || user //关于
tags: /tags/ || tags //标签
categories: /categories/ || th //分类
archives: /archives/ || archive //归档
schedule: /schedule/ || calendar //日程表
sitemap: /sitemap.xml || sitemap //站点地图
创建分类文件夹:
1
2
hexo new page categories
成功后输出:INFO Created: ~/Documents/blog/source/categories/index.md

根据上面的路径,找到index.md这个文件,打开后并添加type: "categories"

1
2
3
4
5
---
title: 文章分类
date: 2019-07-15 23:30:33
type: "categories"
---

然后给文章添加“categories”属性

1
2
3
4
5
---
title: GitHub+Hexo+Next搭建博客
date: 2019-07-15 23:43:57
categories: hexo
---
创建标签文件夹
1
2
hexo new page tags
成功后输出:INFO Created: ~/Documents/blog/source/tags/index.md

根据上面的路径,找到index.md这个文件,打开后并添加type: "tags",保存。

1
2
3
4
5
---
title: 文章分类
date: 2019-07-15 23:54:22
type: "tags"
---

然后给文章添加“tags”属性

1
2
3
4
5
---
title: GitHub+Hexo+Next搭建博客
date: 2019-07-15 23:43:57
tags: hexo
---

根据以上步骤就可以搭建出一个属于自己的博客网站了,赶紧行动吧!