8384

又到了一个不写点东西会有罪恶感的间隔了。

今天看到一个好消息,3系 2017 换装 B48 发动机后,还取消了 320i,只给 320iM,指导价从 36.7 直接降到 32,wo,好划算的即视感。要从 5 系换回 3 系了吗?就是这么善变。

话说好久不看车了,因为忙,最近太忙了,晚上躺到床上都有种感动。

最近也好久不开车了,一个人开车堵在那里的感觉让人更心烦,就改地铁了。有时还能碰到同事,顺带保卫下萝卜(为了全部三颗星才唯一一个没卸载的游戏)。

除了上海高架的永堵,还让人闹心的是体检结果出来了,这个用词,恩,中央型肥胖,后来经过我鉴定,团队里只有一个人不是,因为他刚因营养不良进了医院。哎,一场中年的通病,我正在变成那个最不喜欢的自己。

胖带来了很多问题,之前 6.8K 最好成绩是 0.26,半马是 1.35,全马没跑过。但现在最远只能跑 8K 了,而且跑步的感觉很不一样,有种 Golf 到 Magotan 的感觉,一点都不好。

说到 Running,最近还报了上马全马赛事,虽然不至于担心跑不完,但还是有些顾虑的,比如到底报上名没,充了 2500 块,西贝也不回个信,是的,当我知道上马开始报名的消息时,社会报名通道已经关闭了,后来赶紧找各大赞助商的名额,西贝是第一个发现的。

同事买了一个 DJI 无人机,由于我太喜欢了,于是给他改名叫大疆了,顺带把另一个全身小米的 iOS 开发者改名叫小米无脑粉

DJI Osmo,GoPro Hero 5 好多想买 😂

提到数码,iPhone 7 发布了,最后悔的就是手里的 6 Plus 升到了 iOS 10,因为 Apple 又解决了老设备过于流畅的 Bug,明显的卡顿,明显的耗电。7 完全没有要买的欲望。

Mac 家族要等 10 月份了,本来期待新款 iMac 的,但现在也淡了,平时都没有开机的机会的。

当然,花钱的地方还是有的,比如把家里装修下,不过目前形式下只能装院子,铺上新地砖,换把绿色的伞,放个投影仪,再弄两把躺椅,生活也可以更好的。

生活当然也在细节,美丽华下开了家新的面包房,一直没尝试,这不,昨晚没买到其它家,去尝试了下,碰巧买到一款海盐蜂蜜迷你可颂,早上烤箱加热后,蜂蜜融化,配上一点点的盐,口感实在太好了,于是早上我又给自己做了一杯咖啡。

生活,就这样,随人,你想过的更好,那就过的更好。

是的,我最近过的很好,因为 …

React Boilerplate

  1. 1. 概念
    1. 1.1. Component
    2. 1.2. Virtual DOM
    3. 1.3. 单向数据流。
  2. 2. Choice
  3. 3. React Prictice

Learn Once, Write Anywhere

概念

  • Component
  • Virtual DOM
  • Oneway flow

Component

首先,组件化的优势是可以组合,复用性更强。

一个组件包含完整的内容, JavaScript, CSS, HTML。这和之前的 JS Framework 有所不同, 之前像 Angular,都是将模板、样式和事件分开处理的。但在 React 中,这些的边界已经不再那么清晰了。

组件有生命周期,且有一个 render() 方法,用于渲染。

Virtual DOM

就是系统会帮你找到你的改动,改的地方重新 render,性能高的不要不要的。

之前 Ajax 是要你自己指定哪一块改了的,然后进行替换,React 则是全自动,方便、高效。

单向数据流。

这个不得了。所有的数据事件都是单向传递的。View 依赖于 Data(prop, status),只要这两个一改,view 自动渲染。why? virtual dom啊

Choice

Angular 现在明显弱于 React 了。

Angular 比较重,更像是一个后端的 Framework。所以深得后端喜爱。

Angular 2 用了 Typescript 增加了入门难度,所以接受度也不高。

React.js 风头太强了。这种东西。谁比谁先进不重要,重要的是生态。生态赢了,就胜了。

React.js 也有不好的地方,会完全改变你写page的方式。

React Prictice

1
fasd

koa 2.0 - Middleware

  1. 1. 官方 middlewares 列表
  2. 2. 基本用法
  3. 3. middleware 原理
    1. 3.1. U型 嵌套 (洋葱嵌套)
    2. 3.2. 原理解析
  4. 4. V1 的支持方案
  5. 5. Middleware 能做的事
  6. 6. REF::

koa 2.0 系列:

Middleware 是 koa 里很重要的点,那关于 Middleware 的基本用法,实现原理和最终如何写一个 koa2 的中间件,大致的讲一下。


官方 middlewares 列表

这里是所有的 middleware 列表,支持 koa2 的不多,但也够用了,其实很多 middleware 真的就只是几句话的事,自己写也很方便。


基本用法

这个例子是从官方上改造的,加了总执行时长(x-response-time),并用到了以下特性:

  • koa2 context
  • async functions
  • arrow functions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
app.use(async (ctx, next) => {
// 启动 middleware,记录当前时间
let start = new Date;
// 进入下一个 middleware,并等待返回。
try {
await next() // next is now a function
} catch (err) {
ctx.body = { message: err.message }
ctx.status = err.status || 500
}
// 内部中间件执行完成,一般意味着业务执行完成。
// 这时,记录结束时间并输出。
let ms = new Date - start;
ctx.set('X-Response-Time', ms + 'ms');
})
// response
app.use(async ctx => {
// 这里处理业务,也就是最后的一个 middleware,所以不再调用 next()
const user = await User.getById(1) // await instead of yield
ctx.body = user // ctx instead of this
})


middleware 原理

U型 嵌套 (洋葱嵌套)

koa 最有特点的就是 U 型 middleware 的实现了,有点像面向切面编程 AOP 从上图可以看出,它顺序执行 middleware,到达最后一个时,又按队列再次顺序执行。

代码上理解就是这样子的:

1
2
3
4
5
6
7
8
9
10
11
12
.middleware1 {
// (1) do some stuff
.middleware2 {
// (2) do some other stuff
.middleware3 {
// (3) NO more middleware !
// ctx.body = 'hello world'
}
// (4) do some other stuff later
}
// (5) do some stuff lastest and return
}

原理解析

其实原理并不难,它是通过一个 compose 的库来实现这种的。


V1 的支持方案

koa2 已经不支持 generator 时代的 middleware,但提供了一个库来做转换 koa-convert,到 v3 时将彻底放弃。

1
2
3
4
import convert from 'koa-convert';
const app = new Koa();
app.use(convert(cors()));


Middleware 能做的事

  • 在请求到达前,做用户和权限的判断,做 RateLimit,限制 某个可用的 IP …
  • 在请求处理后,可以记录服务器处理时长,对结果统一格式化 ….

Express 的 Middleware 是单向的,就没有它这么方便。


REF::

koa 2.0 - Context

koa 2.0 系列:

在介绍篇时,提到过 Web Framework 的最重要的作为就是处理 request & reponse。

koa 将 node 的 request & reponse 对象封装在一个新的对象中
context,并提供一些更简单的 API 方便调用。

Context 是在每一个 request

Koa1 在调用时,使用的是 this,而 koa2 则是 ctx。

1
2
3
4
5
// Koa2
app.use(async (ctx, next) => {
ctx.body = 'hello world'
await next()
})


API

Context 提供的一些方法和 accessors(访问器)。

ctx.req

node request 对象,被封装到了 ctx.req

ctx.res

node response 对象,被封装到了 ctx.res

ctx.request

koa 的 Request 对象。是对 node 的 request 进一步抽象和封装,提供了日常 HTTP 服务器开发中一些有用的功能。

ctx.response

koa 的 Response 对象,是对 node 的 response 进一步抽象和封装,提供了日常 HTTP 服务器开发中一些有用的功能。

ctx.cookies

1
2
ctx.cookies.get(name, [options])
ctx.cookies.set(name, value, [options])

ctx.state

可以通过 State 向前端 View 传递数据:

1
this.state.user = await User.find(id);

ctx.app

Application 实例的指针。


cxt 访问器

context 提供一些 alias & accessors 以方便更快操作 request & response。

Request aliases

  • ctx.header
  • ctx.method
  • ctx.method=
  • ctx.url
  • ctx.url=
  • ctx.originalUrl
  • ctx.path
  • ctx.path=
  • ctx.query
  • ctx.query=
  • ctx.querystring
  • ctx.querystring=
  • ctx.host
  • ctx.hostname
  • ctx.fresh
  • ctx.stale
  • ctx.socket
  • ctx.protocol
  • ctx.secure
  • ctx.ip
  • ctx.ips
  • ctx.subdomains
  • ctx.is()
  • ctx.accepts()
  • ctx.acceptsEncodings()
  • ctx.acceptsCharsets()
  • ctx.acceptsLanguages()
  • ctx.get()

Response aliases

  • ctx.body
  • ctx.body=
  • ctx.status
  • ctx.status=
  • ctx.length=
  • ctx.length
  • ctx.type=
  • ctx.type
  • ctx.headerSent
  • ctx.redirect()
  • ctx.attachment()
  • ctx.set(field, value) // 设置 response header 字段 field 的值为 value。
  • ctx.remove()
  • ctx.lastModified=
  • ctx.etag=

REF::

https://segmentfault.com/a/1190000006085240
https://segmentfault.com/a/1190000006145114

koa 2.0

  1. 1. Introduction
  2. 2. Why Koa
    1. 2.1. Callback hell
    2. 2.2. ES 2017
    3. 2.3. middleware U型嵌套 机制
    4. 2.4. 轻量化内核
  3. 3. koa 2.0 Roadmap
  4. 4. koa 2.0 Boilerplate
  5. 5. REF::

koa 2.0 系列:

Introduction

在开始 koa 之前,先回想下一个 Web Framework 应该要做的事。

  • 监听请求
  • 处理 request 中的 header/body
  • 返回 response 中的 header/body

统称 request/response 为 context,一般处理 context 不会只有一件事,大多数时间,我们需要权限验证,判断用户来源,RateLimit,日志,自定义返回数据 等等,这些事件为一个链状,context 顺序通过这些事件并对其进行加工,这些的事件在 express/koa 中叫 middleware

当你看完 代码 时,你会发现和我们设想的一样简单:

1
2
3
4
5
lib
├── application.js
├── context.js
├── request.js
└── response.js

所以 koa 内部主要关注两个要点:

  • context(上下文)
  • middleware(中间件)


Why Koa

讲了上述的点后,并没有说明为什么是 koa,而不是 express 或其它。选择 koa 主要是以下几个重要的点:

  • Callback hell
  • ES 2017
  • Middleware U型嵌套
  • 轻量化内核

Callback hell

1.0 中,是通过 Generator 来实现了 coroutine-like,取消了 callback。

Callback 有两大硬伤,一是不可组合,二是异常不可捕获。

ES 2017

当然,选择它的主要原因是还有 支持 ES 的新语法

2.0 中用了 async functions 代替了 1.0 中的 co 的 Generator functions,使语法更优雅。

middleware U型嵌套 机制

这是 koa 很有特色的一个地方,另花笔墨去写。

轻量化内核

和 express 不同的是,它只做了 Web Framework 应该做的,Router,View 都不再提供,更轻量化。

当然也提供了多样性,可以用更适合的 middleware 来做事情。


koa 2.0 Roadmap

koa 2.0 目前并没有正式发布,主要是为了等 V8 的 async functions 集成。但并不意味着不能投入使用,相反很多公司都已经应用于生产了。


koa 2.0 Boilerplate

这是我写的一个 koa2 的最佳实践项目,其中包括:

  • 完整的项目结构
  • koa-router 集成
  • Sequlize & MySQL
  • DI Container 支持
  • Class 语法
  • Babel 配置
  • Multiple Environment 支持

重点说明下,为什么和其它的项目结构有所不同,这里采用的是类 rails 的结构,而不是将所有代码都放置在 src 目录下,原因是 node 很快就会支持所有的新语法,编译不再是必须的。

REF::

RESTful Status in Deep

在 RESTful 中,Status Code 是 Sever 和 Client 通讯中第一个用到的标准。业界有一些很好的总结,这里记下自己的实践。

0x01: 成功类:

成功类的请求就相对简单了,直接返回对象就可以了(Modern Web App 中其实是不需要外面包一层 Envelope)。

200 Success GET, PATCH 表示返回正常 返回对象 { ...}
201 Created POST 表创建成功 会返回创建对象 { ... }
204 No Content DELETE 表删除成功,且不返回数据

POST, PUT, PATCH 都会返回该操作对象,只是 POST 有特殊 Code 201,其它都用 200


1x10: 失败类:

4XX Client Error:

400 Bad Request The request is malformed, such as if the body does not parse。 无效的请求,语义有误,当前请求无法被服务器理解
401 Unauthorized Bad credentials 未验证
403 Forbidden 未授权
404 Not Found 内容不存在,无论请求一个错误的地址,还是指定 id 不存在
409 Conflict 由于和被请求的资源的当前状态之间存在冲突,请求无法完成。这个代码只允许用在这样的情况下才能被使用:用户被认为能够解决冲突,并且会重新提交新的请求。该响应应当包含足够的信息以便用户发现冲突的源头。
410 Gone Indicates that the resource at this end point is no longer available. Useful as a blanket response for old API versions
422 Unprocessable Entity Used for validation errors
423 Locked 当前资源被锁定。
429 Too many request Rate limit exceeded, throttling
451 Unavailable For Legal Reasons (由IETF在2015核准后新增加)该访问因法律的要求而被拒绝

具象化的使用 Status code:

  • The request could not be understood by the server due to malformed syntax
  • 409

5XX Server Error:

500 Internal Server Error 服务器内部错误,不受程序管理
501 Not Implemented 该接口未实现


1x20: 错误的格式 (Error Object)

业界一般的做法是返回 code 和 message 这两个字段

  • code 用于显示出错信息,客户端可定义。
  • message 一般是给 API 调用者看的,对开发友好。

有些需求是会返回一个 Error 数组,好处是,如果 API Body 中有多个值,可以分别校验,返回具体每一个值的出错信息,更友好。

1
2
3
4
5
6
7
8
9
10
11
12
13
# Github
{
"message": "Problems parsing JSON",
"documentation_url": "https://developer.github.com/v3"
}
# Twitter
{
"errors": [{
"code": 135,
"message": "Timestamp out of bounds."
}]
}


1x30: 案例分析

1x31: Client Data Error

服务器无法理解这个请求。如果 request 包括无法解析的数据,应该返回一个 HTTP 400:

  • request 中包含的是一个无效的 JSON
  • request 缺少有效的 Query Parameters
1
2
3
4
5
6
7
8
# Status: 400 Bad Request
{
"errors": [{
"code": 4003,
"message": "Problems parsing JSON"
}]
}

1x32: 无法通过验证

之前通常会用 HTTP 400 错误码来标识用户提交的错误信息,但这个码太通用了,有时无法更具象表述出错信息。

Validation Errors 通过 HTTP 422 (Unprocessable Entity) 会比 HTTP 400 更具象,更合理。

Request matched and met syntactic contract but validation failed

1
2
3
4
5
6
7
8
# Github
# Status: 404 Unprocessable Entity
{
"message": "Problems parsing JSON",
"documentation_url": "https://developer.github.com/v3"
}

1x33: 用户相关的几个实例:

  • 密码错误: 这也是用户上传的数据也合格,但不能够通过 validation ,属于 HTTP 422 的范畴。
  • 但如果去访问一个资源,该资源需要 token,这时就是 HTTP 401。
  • 还有,如果有了 token ,但该资源需要特定的权限,这就是 HTTP 403。

1x34: 案例分析1:优惠券次数

举例:抢优惠券的业务,每个人有 3 次机会,如果用户前 3 次内点,和第 4 次请求,应如何处理。

但在这件事的最后定论是:这是一个业务问题,不应该在 HTTP 协议中来体现,所以最终请求会是这样:

1
2
3
4
5
6
{
"data": {
"success": true,
"prize": 1
}
}

事过情迁,我再去写 RESTful 时,发现这样并不好。首先先谈下好的方案:

Rate Limit Solution

HTTP 是有这种情况的方案的,Rate Limit,在 Header 中显示该接口的可调用情况:

X-RateLimit-Limit -
X-RateLimit-Remaining -
X-RateLimit-Reset -

超过三次后的请求

返回 403 Forbidden

1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 403 Forbidden
Date: Tue, 20 Aug 2013 14:50:41 GMT
Status: 403 Forbidden
X-RateLimit-Limit: 3
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1377013266
{
"code": 88,
"message": "API rate limit exceeded for xxx.xxx.xxx.xxx or username."
}

1x35: 案例分析1:兑奖

兑奖码无效

HTTP 404

未中奖

理论上未中奖是不能进入的,前端会限制的,但如果被绕开了,还是会有请求到达。

  • HTTP 400 Bad Request
  • HTTP 422 Unprocessable Entity

该码已兑换过

跟上一个一样

  • HTTP 409 Gone 忆兑换过,所以是用过了,Gone
  • HTTP 400 Bad Request 客户端为什么还要发这样的请求?所以是 Bad request
  • HTTP 422 Unprocessable Entity 因为后端验证出了问题。
  • HTTP 423 Locked 因为兑换过就被锁定了。

用户输入兑奖信息不完整 (unpassed validation) ( HTTP 409)

更多 Rate Limit 参见:


REF::

Modern JavaScript - Class

  1. 1. 世界是属于对象的。
  2. 2. REF::

世界是属于对象的。

所有接触的语言都是面向对象的。遇到 js 这种异类,基于 prototype 原型链继承,就崩溃了。

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
class Circle {
constructor(radius) {
this.radius = radius;
Circle.circlesMade++;
};
static draw(circle, canvas) {
// Canvas绘制代码
};
static get circlesMade() {
return !this._count ? 0 : this._count;
};
static set circlesMade(val) {
this._count = val;
};
area() {
return Math.pow(this.radius, 2) * Math.PI;
};
get radius() {
return this._radius;
};
set radius(radius) {
if (!Number.isInteger(radius))
throw new Error("圆的半径必须为整数。");
this._radius = radius;
};
}

REF::