前端必须知道的HTTP

HTTP和Socket是前端经常遇到网络通信相关概念。

HTTP与TCP

HTTP(HyperText Transfer Protocol)的曝光度很高,网页访问、接口请求、服务调用等许多都是通过HTTP协议传输。在历史上,HTTP的设计初衷并没有考虑到这么多应用场景,当初旨在实现从WWW(万维网)服务器传输超文本内容到浏览器,其应用广泛主要得益于它的设计。HTTP是应用层协议,也是基于TCP传输层协议的一种实现。下图中,与TCP上层的应用程序,可以通过HTTP进行通信。

TCP/IP简易网络模型图示

我们经常说的三次握手,就是建立TCP连接的过程。当我们需要通过HTTP协议传输数据时,需要先建立一个TCP连接,三次握手就像对暗号的过程,首先客户端发一个暗号到服务端请求建立连接(第一次握手),然后服务端做出响应回复客户端一个暗号(第二次),发送暗号的同时,服务端要确保客户端收到了自己的信息,所以客户端必须回复一句“收到了”(第三次)。TCP握手建连是一个典型的传令兵逻辑问题,这里不展开说了,但此处引出一个特性,TCP建连不代表后续通信是可靠的。TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”,而断开意味着释放资源。

回到HTTP,有一个特点是客户端每次发送请求都需要服务器响应,请求结束后会主动释放连接。在HTTP 1.0中,客户端每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。 而在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。

HTTP KeepAlive

网上经常有HTTP长连接、短连接的叫法,主要是因为请求结束就会断开这个特性。 而上面提到的HTTP1.0和1.1的区别之一就是TCP建连保持的时间。在HTTP1.1中我们可以通过在服务器和客户端设置header信息,实现对保持时间的配置:

Connection: Keep-Alive
Keep-Alive: timeout=5, max=100

Keep-Alive参数[1]有两个:

timeout:指定了一个空闲TCP连接需要保持打开状态的最小时长(以秒为单位)。
max:在TCP连接关闭之前,在此连接可以发送的请求的最大值。

那么现在,我们可以利用这些设置,获得更长时间的TCP连接,比如在连续请求2个HTTP资源时,当其中一个请求先完成,该请求使用的TCP连接不会立刻中断,那么第二个资源请求可以继续使用该连接保持和服务端的通信。

在实际应用中,一个网页通过HTTP1.1协议加载资源,浏览器在同一时刻在同域(domain)下创建的持久TCP请求数是有限制的,不同浏览器(及版本)的限制不同。比如Chrome是6个。这些规则是浏览器根据自身功能和操作系统限制而采用的策略[2]

network.http.max-persistent-connections-per-server=6

HTTP队首阻塞

利用KeepAlive特性,在客户端(浏览器)并发的同域HTTP请求,会共用一个TCP连接。但是在HTTP1.0/1.1协议下,浏览器会依次等待发出的请求响应,因此当第一个HTTP请求响应在队列中消耗较长的传输时间,处于同一TCP连接中的其他HTTP请求就需要排队等待接收响应。这就叫队首阻塞。

Chrome每个主机最多强制实施6个TCP连接。如果您一次请求12个项目,则前6个开始,后半部分排队。一旦原始一半完成,队列中的第一项将开始其请求过程。

实例

而在HTTP2中(这里简单提一下),不会存在首阻塞的现象[3]

参考引用

[1] MDN – KeepAlive https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Keep-Alive

[2]浏览器TCP限制 https://www.zhihu.com/question/20474326/answer/15691654

[3]HTTP2 https://segmentfault.com/a/1190000006923359

CSS3 原生变量var()用法

变量声明及规范

CSS中原生的变量定义语法是:–,变量使用语法是:var(–),其中*表示我们的变量名称。

/**
 *  全局声明变量
 */
:root {
  --1: #ccc;
}

/**
 *  按选择器声明变量,变量只在各选择器中生效
 */
#main {
  --1: #ccc;
}

/**
 *  变量使用
 */
.message-box {
  background-color: var(--1);
}

/**
 *  为变量添加默认值,如果变量不存在,默认值生效
 */
.message-box {
  background-color: var(--1, #fff);
}

/**
 *  如果变量存在,且是无效值(不符合属性规范的值),则设置为CSS默认值。
 */
.message-box {
  background-color: var(--1, #fff);
}

最佳实践

一个响应式设计,CSS变量参与运算,无JS参与,CSS代码量减少,逻辑更简洁清晰。

box {
    --columns: 4;
    --margins: calc(24px / var(--columns));
    --space: calc(4px * var(--columns));
    --fontSize: calc(20px - 4 / var(--columns));
}
@media screen and (max-width: 1200px) {
    .box {
        --columns: 3;
    }
}
@media screen and (max-width: 900px) {
    .box {
        --columns: 2;
    }
}
@media screen and (max-width: 600px) {
    .box {
        --columns: 1;
    }
}

(案例出处: http://www.divcss5.com/css3-style/c50629.shtml

npx是什么?npx的用法?

关于npx的用法,我也是先读了阮一峰的博文,写得比较清楚了。

npx需要node@5.2及以上版本支持,如果不支持可以用npm安装。

本文就简洁地讲一下重点:

什么是npx?

npx是一个用来执行其他命令的命令

// 查看node版本
node -v

// npx命令查看node版本,与上面命令等效(假设只有全局node模块被安装)
npx node -v

npx 有什么作用?

npx在执行命令时,既可以检查当前项目中已安装的命令,也可以检查全局命令。比如:

// 在当前项目路径下安装了create-react-app,但没有全局安装,此时初始化一个react项目
./node_modules/.bin/create-react-app myapp

// 因为没有全局安装,所以直接执行 create-react-app 会报错,只能引用本地命令的路径

// 使用npx,就可以直接访问当前项目下的命令
npx create-react-app myapp

以上npx运行的时候,会到node_modules/.bin路径和环境变量$PATH里面检查命令是否存在。 由于 npx 会检查环境变量$PATH,所以系统命令也可以调用。当然如果环境变量里也没有这个命令,npx会在当前工程路径下载这个命令(临时),执行完后会删除这个命令。所以npx每次都有可能调用最新的命令。

npx的用法根据参数配置来实现,具体看文档。看不懂源文档可以看阮一峰的。其中 -p -c –no-install –ignore-existing 浅显易懂。

使用体会

解决了在当前工程目录的命令执行问题,通常命令会写在package.json的scripts里,现在可以简化了。

需要思考的是,什么时候使用项目模块或全局模块。

比如在react项目中,强烈建议用npx执行create-react-app命令。曾经我遇到的node版本导致的项目跨团队开发startup问题,以及某些包依赖的版本问题,用npx完全可以解决,并且这是一个不错的选择,至少减少了文档中冗余的说明和沟通的成本。

文档引用

什么是图灵机

万物皆数学。

图灵(Turing)在他的论文《论数字计算在决断难题中的应用》中提出了图灵机的概念。当然后人们称之为图灵机。

图灵机不是真正的机器,简单说是一种模型,一种思维方式。

图灵机由4个部分组成:输入集合、输出集合、内部状态、固定的程序指令。

在现实中总有很多场景惊人的相似,如打酱油、吃瓜,当我们用图灵机来描述这些行为时,会将生动的事物变得轻描淡写,把感官逻辑变得生涩。以打酱油作为个例子:

  1. 打酱油的地方在出门:左转,右转,右转,左转,左转,左转,右转,左转的地方。
  2. 当我们遇到指示牌就会沿着指示牌改变方向。
  3. 如果手里的没封口的酱油瓶撒出了一些,最好还是原路返回再打满。

上面这些仿佛机器一般的逻辑,已经不能再直白,但是用图灵机来描述,将会是另一番样子:

  • 输入集合:{向左转指示牌,向右转指示牌}
  • 输出集合:{向左转,向右转}
  • 内部状态:{酱油瓶满,酱油瓶不满}
  • 固定程序指令:
    • 如果酱油瓶满,向前走,看见向左转的指示牌,就向左转。
    • 如果酱油瓶满,向前走,看见向右转的指示牌,就向右转。
    • 如果酱油瓶不满,向后退,看见向左转的指示牌,就向右转。
    • 如果酱油瓶不满,向后退,看见向右转的指示牌,就向左转。

打酱油的例子换成图灵机描述后,最难理解的是固定程序指令。它是一种脚本,而当我们按照脚本来行动的时候,要考虑酱油瓶自身的状态。我们拿着酱油瓶,看着指示牌,再看看酱油瓶的状态,从而决定到底是前进还是后退,是向左还是向右。

图灵机的组成

如果把上面4个组成部分放在这张图上,可以看到,Tape的每一格就是向左或向右的指示牌,Program就是固定的程序,CurrentState就是内部状态,整个“方盒子”的移动,就是根据上面这些条件和专题进行运算后的结果。

对图灵机来说,Tape可以是无限长的,上面存满了信息。无论这些信息是0或1,还是A或B,都是抽象的存在。理解到这里,可以说现实的各类行为和决策都可以通过图灵机来描述。

除了图灵机,还有个概念叫“图灵完备”。

可图灵指在可计算性理论中,编程语言或任意其他的逻辑系统如具有等用于通用图灵机的计算能力。换言之,此系统可与通用图灵机互相模拟。

《维基百科》

粗犷地说一个系统具备图灵机那四点抽象概念,基本上就算是图灵完备了。