gzl的博客

  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

RN-《精通CSS》(上)

发表于 2019-08-30 更新于 2020-02-06 分类于 读书笔记

第一章 基础知识

创建结构化、语义化富HTML

1
2
3
4
5
6
<article class="post">
<header class="post-header">
<h1>How I became a CSS Master</h1>
</header>
<p>Ten-thousand hours</p>
</article>
1
2
3
4
5
6
.post {
/* 样式 */
}
.post-header {
/* 其他样式 */
}

第二章 添加样式

CSS选择符

相邻同辈选择符

1
2
3
4
5
6
<div class="container">
<p>这是第一个段落</p>
<h2>This is h2</h2>
<p>这是第二个段落</p>
<p>这是第三个段落</p>
</div>
1
2
3
4
5
h2 + p {
font-size: 2em;
font-weight: bold;
color: #777;
}

使用相邻同辈选择符,就可以选择位于某个元素后面,并与该元素拥有共同父元素的元素,可以看到只有 这是第二个段落 应用上了 CSS 样式。

一般同辈选择符

1
2
3
4
5
6
<div class="container">
<p>这是第一个段落</p>
<h2>This is h2</h2>
<p>这是第二个段落</p>
<p>这是第三个段落</p>
</div>
1
2
3
4
5
h2 ~ p {
font-size: 2em;
font-weight: bold;
color: #777;
}

使用一般同辈选择符可以选择 h2 元素后面的所有段落,可以看到 这是第二个段落 和 这是第三个段落 都应用上了 CSS 样式。

相邻同辈选择符和一般同辈选择符都不会选择前面的同辈元素,浏览器之所以不支持向前选择同辈元素,主要跟网页渲染性能有关。

通用选择符

1
2
3
4
*{
margin: 0;
padding: 0;
}

简单的应用就像上面这样,只不过现在有很多现成的库可以重设样式并且比上面的简单地把一切都设为0要好一点。

1
2
3
4
5
6
.container > * {
margin: 20px;
font-size: 2em;
font-weight: bold;
color: #777;
}

上面这样组合,可以选择 .container 的元素的所有直接后代。

伪元素

可以使用 ::first-letter 伪元素来选择一段文本的第一个字符。若要选择一段文本的第一行,可以使用 ::first-line。

::before 和 ::after 这两个伪元素非常适合用来插入小图标及版面装饰符号。

伪元素应该使用双冒号语法,与伪类区别开,伪类使用单冒号语法。

伪类

1
<a href="https://www.baidu.com">百度一下</a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* 未访问过的链接为蓝色 */
a:link {
color: blue;
}
/* 访问过的链接为绿色 */
a:visited {
color: green;
}
/* 链接在鼠标悬停及获取键盘焦点时为红色 */
a:hover,
a:focus {
color: red;
}
/* 活动状态时为紫色 */
a:active {
color: purple;
}

:link 和 :visited 应该排在前面,然后才是与用户交互相关的那些,在触摸屏和键盘等输入方式下不一定真的有悬停状态。因此,不要在重要的交互功能中使用 :hover。

结构化伪类

1
2
3
4
5
6
7
8
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
1
2
3
4
/* 奇数行的背景变为黄色 */
li:nth-child(odd) {
background: yellow;
}

nth-child 选择符可以用来交替地为表格行应用样式。

nth-child 的参数可以是 odd 或者 even,还可以是数值,表示目标元素的序数位置

1
2
3
4
/* 第三行背景变黄 */
li:nth-child(3) {
background: yellow;
}

如果参数是数值表达式,如:

1
2
3
4
/* 第一行和第四行变黄。n 首先等于 0 ,然后逐次递增 1。 */
li:nth-child(3n+1) {
background: yellow;
}
1
2
3
4
/* 前三行会变黄 */
li:nth-child(-n+3) {
background: yellow;
}

:nth-last-child 选择符与 :nth-child 类似,只不过是从最后一个元素倒序计算(而不是从第一个元素正序计算)。

:first-child 相当于 :nth-child(1) ,:last-child 对应于 :nth-last-child(1)。

层叠

稍微复杂的样式表中都可能存在两条甚至多条规则同时选择一个元素的情况,CSS通过一种叫作层叠(cascade)的机制来处理这种冲突。

层叠机制的重要性级别从高到低如下所示:

  • 标注为 !important 的用户样式;
  • 标注为 !important 的作者样式;
  • 作者样式
  • 用户样式
  • 浏览器的默认样式

在此基础上,规则再按选择符的特殊性排序。特殊性高的选择符会覆盖特殊性低的选择符。如果两条规则的特殊性相等,则后定义的规则优先。

!important > 行间样式 > id > class | 属性选择器 > 标签选择器 > 通配符选择器

CSS权重(特殊性)

  • !important Infinity
  • 行间样式 1000
  • id 100
  • class | 属性 | 伪类 10
  • 标签 | 伪元素 1
  • 通配符 0

1000 100 这些不是十进制,是 256 进制

1
<h1 class="title">这是一个h1</h1>
1
2
3
4
5
6
7
8
9
h1 {
color: red;
}
.title {
color: blue;
}
.title {
color: green;
}

h1 中文字最终会变成 green。

继承

继承和层叠看起来有点类似,但实际上他们有着本质上的不同。

有些属性,像颜色或字体大小,会被应用它们的元素的后代所继承。比如,我把 body 元素的文本颜色设置为黑色,那么 body 所有后代元素的文本颜色都会继承这个黑色,字号也一样。

在 body 上设置了一个字号,会发现页面中标题并不会变成同样的字号。可能你觉得这是因为标题不会继承文本大小。实际上,标题大小是浏览器默认样式表中设定的。任何直接应用给元素的样式都会覆盖继承的样式,因为继承的样式没有任何特殊性。

继承的属性没有任何特殊性,连0都说不上。这意味着使用特殊性为0的通用选择符设置的样式都可以覆盖继承的样式。

1
2
3
4
5
<h1 class="title">
h1
<em>这是一个em</em>
h1
</h1>
1
2
3
4
5
6
* {
color: black;
}
h1 {
color: red;
}

在上面这段代码中,表面上看 em 会继承 h1 的红色,但通用选择符给所有元素设置的黑色会覆盖它所继承的红色。通用选择符的特殊性为 0,但仍然优先于继承的属性,在这种情况下,最好是给 body 元素设置一个基准色,这样它的所有元素就都会继承这个颜色,而不是被设置成这个颜色。

第三章 可见格式化模型

盒模型

百分比问题

1
2
3
<div class="group">
<article class="block"></article>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
.group {
width: 300px;
height: 200px;
margin: 0 auto;
border: 1px solid black;
}
.block {
width: 100px;
height: 100px;
border: 1px solid red;
margin-left: 5%;
margin-top: 5%;
}

上面的代码中,5% 指的是父元素 .group 宽度的 5%,.group 宽度是在这里是 300px,那么 margin-left 为 15px。注意,CSS规定,上、下方位的内外边距,仍然基于包含块的宽度,来计算,因此 margin-top 也为 15px。原因就是元素的高度常常不会被声明,而且会因内容多少而差距很大,如果要明确设定默认高度,也最好使用 min-height,因为这个属性允许盒子随内容扩展。

匿名盒子

1
2
3
4
<section>
some text
<p>Some more text</p>
</section>

此时,some text 就算没有定义为块级元素,也会被当成块级元素,这种情况下,这个盒子被称为匿名块盒子。

浮动模型

浮动盒子可以向左或向右移动,直到其外边沿接触包含块的外边沿,或接触另一个浮动盒子的外边沿。浮动盒子也会脱离常规文档流,因此常规流中的其他块级盒子的表现,几乎当浮动盒子根本不存在一样。

1
2
3
4
5
<div class="container">
<div class="first">1234567891231231315135</div>
<div class="second"></div>
<div class="third"></div>
</div>
1
2
3
4
5
6
7
8
9
10
.container > div {
width: 100px;
height: 100px;
border: 1px solid black;
margin-bottom: 20px;
}
.container .first {
width: auto;
float: right;
}

上面的代码可以看出,.first 会脱离文档流并向右移动,直至其右边接触包含块的有边沿。同时,.first 的宽度也会收缩为适应于其中内容的最小宽度,除非通过 width 或 min-width/max-width 明确设置其宽度。

修改 .first 为向左浮动:

1
2
3
4
5
6
7
8
9
10
11
12
13
.container > div {
width: 100px;
height: 100px;
border: 1px solid black;
margin-bottom: 20px;
}
.container .first {
width: auto;
float: left;
}
.container .second {
background: green;
}

可以看出 .first 已经不在文档流中,因此不会再占空间,这导致它浮于上方,遮住了 .second 。

块级格式化上下文

  • display 属性值设置为 inline-block 或 table-cell 之类的元素,可以为内容创建类似块级的上下文
  • float 属性值不是 none 的元素
  • 绝对定位的元素
  • overflow 属性值不是 visible 的元素。

最常见的就是overflow:hidden、float:left/right、position:absolute。也就是说,每次看到这些属性的时候,就代表了该元素以及创建了一个BFC了。

学习 BFC (Block Formatting Context)

第四章 网页排版

字形大小

几乎所有的浏览器中 font-size 的默认大小都是 16 像素,我们不修改默认的 font-size ,而是选择使用 em 单位调整特定元素的大小。

对于 font-size 属性,可用百分比替代 em 。133.3% 和 1.333em 没有区别。

最灵活的方式是使用 rem 单位,它始终基于根元素 html 元素的 font-size 缩放。

当 em 用于计算盒模型大小时,它不是基于继承的 font-size ,而是基于元素自身计算的 font-size。因此,不同级别的标题对应的 font-size 是不一样的。所以下面的代码推荐使用 rem。

1
2
3
4
h1,h2,h3,h4,h5,h6 {
margin-top: 1.5rem;
/* 24px = 16px * 1.5 */
}

不推荐使用绝对物理长度,如mm cm in pt。

RN-《Promise迷你书》

发表于 2019-08-29 更新于 2020-02-06 分类于 读书笔记

Promise workflow

1
2
3
4
5
6
7
8
9
10
11
12
13
function asyncFunction() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('Async Hello world');
}, 16);
});
}

asyncFunction().then(function (value) {
console.log(value); // => 'Async Hello world'
}).catch(function (error) {
console.log(error);
});

asyncFunction 这个函数会返回promise对象, 对于这个promise对象,我们调用它的 then 方法来设置resolve后的回调函数, catch方法来设置发生错误时的回调函数。

该promise对象会在setTimeout之后的 16ms 时被resolve, 这时 then 的回调函数会被调用,并输出 'Async Hello world' 。

在这种情况下 catch 的回调函数并不会被执行(因为promise返回了resolve), 不过如果运行环境没有提供 setTimeout 函数的话,那么上面代码在执行中就会产生异常,在 catch 中设置的回调函数就会被执行。

new Promise

创建promise对象的流程如下所示。

  1. new Promise(fn) 返回一个promise对象
  2. 在fn 中指定异步等处理
    • 处理结果正常的话,调用resolve(处理结果值)
    • 处理结果错误的话,调用reject(Error对象)

用Promise来通过异步处理方式来获取XMLHttpRequest(XHR)的数据。

创建XHR的promise对象

首先,创建一个用Promise把XHR处理包装起来的名为 getURL 的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function getURL(URL) {
return new Promise(function (resolve, reject) {
let req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
resolve(req.responseText);
} else {
reject(new Error(req.statusText));
}
};
req.onerror = function () {
reject(new Error(req.statusText));
};
req.send();
});
}
// 运行示例
let URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value) {
console.log(value);
}).catch(function onRejected(error) {
console.error(error);
});

getURL 只有在通过XHR取得结果状态为200时才会调用 resolve - 也就是只有数据取得成功时,而其他情况(取得失败)时则会调用 reject 方法。

resolve(req.responseText) 在response的内容中加入了参数。 resolve方法的参数并没有特别的规则,基本上把要传给回调函数参数放进去就可以了。 ( then 方法可以接收到这个参数值)

熟悉Node.js的人,经常会在写回调函数时将 callback(error, response) 的第一个参数设为error对象,而在Promise中resolve/reject则担当了这个职责(处理正常和异常的情况),所以 在resolve方法中只传一个response参数是没有问题的。

接下来我们来看一下reject函数。

XHR中 onerror 事件被触发的时候就是发生错误时,所以理所当然调用reject。 这里我们重点来看一下传给reject的值。

发生错误时要像这样 reject(new Error(req.statusText)); ,创建一个Error对象后再将具体的值传进去。 传给reject 的参数也没有什么特殊的限制,一般只要是Error对象(或者继承自Error对象)就可以。

传给reject 的参数,其中一般是包含了reject原因的Error对象。 本次因为状态值不等于200而被reject,所以reject 中放入的是statusText。 (这个参数的值可以被 then 方法的第二个参数或者 catch 方法中使用)

Promise.resolve

一般情况下我们都会使用 new Promise() 来创建promise对象,但是除此之外我们也可以使用其他方法。

静态方法 Promise.resolve(value) 可以认为是 new Promise() 方法的快捷方式。

比如 Promise.resolve(42); 可以认为是以下代码的语法糖。

1
2
3
4
5
new Promise(function (resolve) {
resolve(42);
}).then(function (value) {
console.log(value);
});

在这段代码中的 resolve(42); 会让这个promise对象立即进入确定(即resolved)状态,并将 42 传递给后面then里所指定的 onFulfilled 函数。

方法 Promise.resolve(value); 的返回值也是一个promise对象,所以我们可以像下面那样接着对其返回值进行 .then 调用。

1
2
3
Promise.resolve(42).then(function (value) {
console.log(value);
});

Promise.resolve 作为 new Promise() 的快捷方式,在进行promise对象的初始化或者编写测试代码的时候都非常方便。

Promise.reject

Promise.reject(error)是和 Promise.resolve(value)类似的静态方法,是 new Promise() 方法的快捷方式。

比如 Promise.reject(new Error("出错了")) 就是下面代码的语法糖形式。

1
2
3
new Promise(function (resolve, reject) {
reject(new Error("出错了"));
});

下面这段代码的功能是调用该promise对象通过then指定的 onRejected 函数,并将错误(Error)对象传递给这个 onRejected 函数。

1
2
3
Promise.reject(new Error("BOOM!")).catch(function (error) {
console.error(error);
});

Promise只能进行异步操作?

在使用Promise.resolve(value) 等方法的时候,如果promise对象立刻就能进入resolve状态的话,那么你是不是觉得 .then 里面指定的方法就是同步调用的呢?

实际上, .then 中指定的方法调用是异步进行的。

1
2
3
4
5
6
7
8
let promise = new Promise(function (resolve) {
console.log("inner promise"); // 1
resolve(42);
});
promise.then(function (value) {
console.log(value); // 3
});
console.log("outer promise"); // 2

执行上面的代码会输出下面的log,从这些log我们清楚地知道了上面代码的执行顺序。

1
2
3
inner promise // 1
outer promise // 2
42 // 3

由于JavaScript代码会按照文件的从上到下的顺序执行,所以最开始 <1> 会执行,然后是 resolve(42); 被执行。这时候 promise 对象的已经变为确定状态,FulFilled被设置为了 42 。

下面的代码 promise.then 注册了 <3> 这个回调函数,这是本专栏的焦点问题。

由于 promise.then 执行的时候promise对象已经是确定状态,从程序上说对回调函数进行同步调用也是行得通的。

但是即使在调用 promise.then 注册回调函数的时候promise对象已经是确定的状态,Promise也会以异步的方式调用该回调函数,这是在Promise设计上的规定方针。

因此 <2> 会最先被调用,最后才会调用回调函数 <3> 。

为了避免同时使用同步、异步调用可能引起的混乱问题,Promise在规范上规定 Promise只能使用异步调用方式 。

promise chain 中传递参数

这时候如果 Task A 想给 Task B 传递一个参数该怎么办呢?

答案非常简单,那就是在 Task A 中 return 的返回值,会在 Task B 执行时传给它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function doubleUp(value) {
return value * 2;
}

function increment(value) {
return value + 1;
}

function output(value) {
console.log(value); // => (1 + 1) * 2
}

let promise = Promise.resolve(1);
promise
.then(increment)
.then(doubleUp)
.then(output)
.catch(function (error) {
// promise chain中出现异常的时候会被调用
console.error(error);
});

这段代码的入口函数是 Promise.resolve(1); ,整体的promise chain执行流程如下所示。

  1. Promise.resolve(1); 传递 1 给 increment 函数
  2. 函数 increment 对接收的参数进行 +1 操作并返回(通过return)
  3. 这时参数变为2,并再次传给 doubleUp 函数
  4. 最后在函数 output 中打印结果

每次调用then都会返回一个新创建的promise对象

每个方法中 return 的值不仅只局限于字符串或者数值类型,也可以是对象或者promise对象等复杂类型。

return的值会由 Promise.resolve(return的返回值); 进行相应的包装处理,因此不管回调函数中会返回一个什么样的值,最终 then 的结果都是返回一个新创建的promise对象。

1
2
3
4
5
6
7
8
9
10
11
12
let aPromise = new Promise(function (resolve) {
resolve(100);
});
let thenPromise = aPromise.then(function (value) {
console.log(value); // 100
});
let catchPromise = thenPromise.catch(function (error) {
console.error(error);
});
console.log(aPromise !== thenPromise); // => true
console.log(thenPromise !== catchPromise); // => true
// 打印顺序为 true true 100

Promise API

Promise.then

promise.then(onFulfilled, onRejected);

1
2
3
4
5
6
7
8
let promise = new Promise(function (resolve, reject) {
resolve("传递给then的值");
});
promise.then(function (value) {
console.log(value); // 传递给then的值
}, function (error) {
console.error(error);
});

这段代码创建一个promise对象,定义了处理onFulfilled和onRejected的函数(handler),然后返回这个promise对象。

这个promise对象会在变为resolve或者reject的时候分别调用相应注册的回调函数。

  • 当handler返回一个正常值的时候,这个值会传递给promise对象的onFulfilled方法。
  • 定义的handler中产生异常的时候,这个值则会传递给promise对象的onRejected方法。

Promise.catch

promise.catch(onRejected);

1
2
3
4
5
6
7
8
let promise = new Promise(function (resolve, reject) {
resolve("传递给then的值");
});
promise.then(function (value) {
console.log(value); // 传递给then的值
}).catch(function (error) {
console.error(error);
});

这是一个等价于promise.then(undefined, onRejected) 的语法糖。

Promise.resolve

Promise.resolve(promise);
Promise.resolve(thenable);
Promise.resolve(object);

1
2
3
4
5
6
7
8
9
10
11
12
let taskName = "task 1";
asyncTask(taskName).then(function (value) {
console.log(value); // Done! task 1
}).catch(function (error) {
console.error(error);
});

function asyncTask(name) {
return Promise.resolve(name).then(function (value) {
return "Done! " + value;
});
}

根据接收到的参数不同,返回不同的promise对象。

虽然每种情况都会返回promise对象,但是大体来说主要分为下面3类。

  • 接收到promise对象参数的时候

    返回的还是接收到的promise对象

  • 接收到thenable类型的对象的时候

    返回一个新的promise对象,这个对象具有一个 then 方法

  • 接收的参数为其他类型的时候(包括JavaScript对或null等)

    返回一个将该对象作为值的新promise对象

Promise.reject

Promise.reject(object)

返回一个使用接收到的值进行了reject的新的promise对象。

而传给Promise.reject的值也应该是一个 Error 类型的对象。

另外,和 Promise.resolve不同的是,即使Promise.reject接收到的参数是一个promise对象,该函数也还是会返回一个全新的promise对象。

1
2
var r = Promise.reject(new Error("error"));
console.log(r === Promise.reject(r));// false

Promise.all

Promise.all(promiseArray);

1
2
3
4
5
6
let p1 = Promise.resolve(1),
p2 = Promise.resolve(2),
p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then(function (results) {
console.log(results); // [1, 2, 3]
});

生成并返回一个新的promise对象。

参数传递promise数组中所有的promise对象都变为resolve的时候,该方法才会返回, 新创建的promise则会使用这些promise的值。

如果参数中的任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的promise对象。

由于参数数组中的每个元素都是由 Promise.resolve 包装(wrap)的,所以Paomise.all可以处理不同类型的promose对象。

Promise.race

Promise.race(promiseArray);

1
2
3
4
5
6
let p1 = Promise.resolve(1),
p2 = Promise.resolve(2),
p3 = Promise.resolve(3);
Promise.race([p1, p2, p3]).then(function (value) {
console.log(value); // 1
});

生成并返回一个新的promise对象。

参数 promise 数组中的任何一个promise对象如果变为resolve或者reject的话, 该函数就会返回,并使用这个promise对象的值进行resolve或者reject。

中间件

发表于 2019-08-21 更新于 2019-08-22 分类于 node.js

koa中的中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const Koa = require('koa');

const app = new Koa();

app.use((ctx, next) => {
// console.log(ctx); 上下文
console.log('first');
next(); // 调用下一个中间件
})

app.use((ctx, next) => {
console.log('second');
})

app.listen(3000, () => {
console.log(`127.0.0.1:3000`)
})

执行下面的代码,然后在浏览器中输入127.0.0.1:3000,终端依次打印出 first second。

洋葱模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const Koa = require('koa');

const app = new Koa();

app.use((ctx, next) => {
console.log(1);
next();
console.log(2)
})

app.use((ctx, next) => {
console.log(3);
next();
console.log(4);
})

app.listen(3000, () => {
console.log(`127.0.0.1:3000`)
})

执行上面的代码,然后在浏览器中输入127.0.0.1:3000,终端会依次打印出 1 3 4 2。

next返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const Koa = require('koa');

const app = new Koa();

app.use((ctx, next) => {
console.log(1);
const a = next();
console.log(a);
console.log(2);
})

app.use((ctx, next) => {
console.log(3);
console.log(4);
return "abc";
})

app.listen(3000, () => {
console.log(`127.0.0.1:3000`)
})

执行上面的代码,然后在浏览器中输入127.0.0.1:3000,终端会依次打印 1 3 4 Promise { 'abc' } 2,如果没有 return "abc";,那么 a 为 Promise { undefined },可以看到,next() 的返回值就是 Promise。

await async

求值

加上 await 和 async 确保按洋葱顺序执行,如果不加上,很难保证按洋葱顺序执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const Koa = require('koa');

const app = new Koa();

app.use(async (ctx, next) => {
console.log(1);
const a = await next(); // 进行求值得到 'abc'
console.log(a);
console.log(2);
})

app.use((ctx, next) => {
console.log(3);
console.log(4);
return "abc";
})

app.listen(3000, () => {
console.log(`127.0.0.1:3000`)
})

执行上面的代码,然后在浏览器中输入127.0.0.1:3000,终端会依次打印 1 3 4 abc 2。

如果使用 .then,那么结果就不一样了,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const Koa = require('koa');

const app = new Koa();

app.use(async (ctx, next) => {
console.log(1);
const a = next();
a.then((res) => {
console.log(res);
})
console.log(2);
})

app.use((ctx, next) => {
console.log(3);
console.log(4);
return "abc";
})

app.listen(3000, () => {
console.log(`127.0.0.1:3000`)
})

执行上面的代码,在浏览器中输入127.0.0.1:3000,终端会依次打印 1 3 4 2 abc

阻塞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Koa = require('koa');

const app = new Koa();

app.use((ctx, next) => {
const axios = require('axios');
const start = Date.now();
const res = axios.get(`https://www.baidu.com`);
const end = Date.now();
console.log(end-start); // 0
})

app.listen(3000, () => {
console.log(`127.0.0.1:3000`)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Koa = require('koa');

const app = new Koa();

app.use(async (ctx, next) => {
const axios = require('axios');
const start = Date.now();
const res = await axios.get(`https://www.baidu.com`);
const end = Date.now();
console.log(end-start); // 79
})

app.listen(3000, () => {
console.log(`127.0.0.1:3000`)
})

比较上面的代码,第二段代码把异步变成了同步,所以 end-start 会变得很大

ctx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const Koa = require('koa');

const app = new Koa();

app.use(async (ctx, next) => {
await next();
// 按照洋葱模型才能保证 r 是有值的,如果不加 await async,那么不一定是按照洋葱模型来执行
const r = ctx.r;
console.log(r);
})

app.use(async (ctx, next) => {
const axios = require('axios');
const res = await axios.get("https://www.baidu.com");
ctx.r = res;
})

app.listen(3000, () => {
console.log(`127.0.0.1:3000`)
})

express中的中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const express = require('express');

const app = express();

app.use((req, res, next) => {
console.log('1');
next();
})

app.use((req, res, next) => {
console.log('2');
next();
})

app.use((req, res, next) => {
console.log('3');
})

app.listen(3000, () => {
console.log(`127.0.0.1:3000`);
})

执行上面的代码,然后在浏览器中输入127.0.0.1:3000,终端会依次打印出 1 2 3。

分类

应用级中间件
路由级中间件
错误处理中间件
内置中间件
第三方中间件

举例

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
const express = require('express');

const app = express();

app.use((req, res, next) => {
console.log('1');
next();
})

app.use('/a', (req, res, next) => {
console.log('2');
next();
})

app.get('/a', (req, res, next) => {
console.log('a');
})

app.use('/b', (req, res, next) => {
console.log('3');
})

app.listen(3000, () => {
console.log(`127.0.0.1:3000`);
})

上面这段代码,在浏览器中输入 http://127.0.0.1:3000 只会打印出 1,输入 http://127.0.0.1:3000/a 会依次打印出 1 2 a,输入 http://127.0.0.1:3000/b 回依次打印出 1 3。

1…151617…32

gzl

96 日志
14 分类
37 标签
© 2020 gzl
由 Hexo 强力驱动 v3.7.1
|
主题 – NexT.Pisces v7.2.0