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::

EventSource

Polling

轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。
优点:后端程序编写比较容易。
缺点:请求中有大半是无用,浪费带宽和服务器资源。
实例:适于小型应用。

Long Polling

https://www.pubnub.com/blog/2014-12-01-http-long-polling/

长轮询:客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。
优点:在无消息的情况下不会频繁的请求,耗费资源小。
缺点:服务器hold连接会消耗资源,返回数据顺序无保证,难于管理维护。
实例:WebQQ、Hi网页版、Facebook IM。

WebSocket

HTML5 的 WebSocket 是一種建立在單一 TCP 連線上的全雙工(full-duplex)通訊管道,可以讓網頁應用程式與伺服器之間做即時性、雙向的資料傳遞。

WebSocket 跟以往實作全雙工的技術比起來,改進了非常多,不但減低網路頻寬的使用,有降低了網路延遲的時間。(關於網路的頻寬與延遲可參考這裡)

非HTTP协议,无法自动穿越防火墙

EF::

https://developer.mozilla.org/en-US/docs/Web/API/EventSource
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
SSE:服务器发送事件

http://stackoverflow.com/questions/12555043/my-understanding-of-http-polling-long-polling-http-streaming-and-websockets

Modern JavaScript - Arrow Functions

ES6中引入了一种编写函数的新语法

简单函数

1
2
3
4
5
6
7
// ES5
var selected = allJobs.filter(function (job) {
return job.isSelected();
});

// ES6
var selected = allJobs.filter(job => job.isSelected());

当你只需要一个只有一个参数的简单函数时,可以使用新标准中的箭头函数,它的语法非常简单:标识符 => 表达式。你无需输入 function 和 return,一些小括号、大括号以及分号也可以省略。

1
2
3
4
5
6
7
// ES5
var total = values.reduce(function (a, b) {
return a + b;
}, 0);

// ES6
var total = values.reduce((a, b) => a + b, 0);

命名函数定义

1
2
3
4
5
// ES5
const getExtensives = async({}, start, limit) => {};

// ES2015
async function getExtensives({}, start, limit) {};

this

箭头函数没有它自己的this值,箭头函数内的this值继承自外围作用域。

Modern JavaScript - Async Functions

  1. 1. Callback Hell
  2. 2. Bluebrid
  3. 3. Syntax
  4. 4. DEMO
  5. 5. co
  • REF::
  • http://node.green/#async-functions

    Node 的最大特性之一就是 Single Thread, Event-Drive, Asynchronous I/O。

    因为是单线程,注定是不能长时间空等占用线程,JS 的解决方案是 Asynchronous Event,当有事件通知到时做响应,需要等待时,将控制权交出。

    对于 Asynchronous,那真是有太多的方案了,我们从头开始看:


    ## Callback

    最早通过 Callback 来实现 Event-Drive。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function f1(callback){
    setTimeout(function () {
    // f1的任务代码
    callback();
    }, 1000);
    }

    // 调用
    f1(f2);

    callback 的原理就是我向事件中心注册这么一个事件,当执行到我时,事件中心来调用我这个事件。

    Callback Hell

    当然 Callback 有着最大的天然缺陷,不符合人类思维,当项目变大,嵌套变深时,callback 的写法,将是一个灾难:


    ## Promise

    Promise 对象可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

    Promise 是一个有限状态机(有 Pending, Resolved, Rejected 三种状态),一旦创建后立即执行,可以由 Pending 到另外两种,状态一旦改变后就不能再次更改,会一直保持。

    和 Callback 不同的是,状态会一直保持,在 Promise 执行完成后,依然可以添加回调 (then)。

    原理就是有一个队列,回调时从里面取出,放到 Event Queue 中。

    将传统的 Callback 封装成 Promise 来使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    const asyncStringify = (content) => {
    return new Promise(function promiser(resolve, reject) {
    stringify(content, function (err, output) {
    if (err) {
    return reject(err);
    }

    return resolve(output);
    });
    });
    };

    Promise 创建后立即执行,然后可以用 then 方法分别指定 Resolved 状态和 Reject 状态的回调函数。

    1
    2
    3
    4
    5
    6
    7
    8
    promise.then(function(value) {
    // success
    }, function(error) {
    // failure
    });

    # Arrow function looks beatul
    promise.then((value) => { ... });

    Bluebrid

    Bluebrid 提供了方法,可以直接将 所有的 Callback 直接返回全新的 promise。

    这里我们的 系统的 FileStream 模块的所有函数给转成 Promise 函数。

    1
    2
    3
    4
    import bluebird from 'bluebird';
    import fs from 'fs';

    let asyncFs = bluebird.promisifyAll(fs);

    Promise 的写法只是回调函数的改进,使用then方法以后,异步任务的两段执行看得更清楚了,除此以外,并无新意。

    Promise 的最大问题是代码冗余,原来的任务被Promise 包装了一下,不管什么操作,一眼看去都是一堆 then,原来的语义变得很不清楚。


    ## Generator

    Generator 是一个无限状态机。

    Syntax

    1
    2
    3
    function *foo() {
    // ..
    }

    DEMO

    1
    2
    3
    4
    5
    6
    7
    8
    function *helloWorldGenerator() {
    console.log("hi");
    yield 'hello';
    yield 'world';
    return 'ending';
    }

    var hw = helloWorldGenerator();

    和普通 function 有点不同的是,这个函数并不会执行,返回的也不是结果,而是指向内部状态的指针对象(遍历器),普通函数中,是不能指用 yield 的。

    然后通过遍历器对象的 next 方法,使指针从函数头部向下移一个状态。直到遇到下一个 yield 语句。

    Generator 函数是分段执行的,yield 语句是暂停执行的标记,而 next 方法可以恢复执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    hw.next();
    // hi
    // Object {value: "hello", done: false}

    hw.next();
    // Object {value: "world", done: false}

    hw.next();
    // Object {value: "ending", done: true}

    hw.next();
    // Object {value: undefined, done: true}

    next 方法的参数

    yield 句本身没有返回值,或者说总是返回 undefined。next 方法可以带一个参数,该参数就会被当作上一个 yield 语句的返回值。

    co

    Generator 经常被用做顺序执行异步流程。

    但 next 函数要一次次的写,很烦,于是 有了各种方案。

    • Thunk
    • co

    co 函数接收一个 generator 为参数,然后就会自动执行 generator。

    同时,co 函数返回一个 Promise 对象,因此可以用 then 方法添加回调函数。


    ## Async Functions

    Async Fuctions 其实只是 Generator 的语法糖,外带一个类 co 的自动执行器。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var gen = function* (){
    var f1 = yield readFile('/etc/fstab');
    console.log(f1.toString());
    };

    // 写成async函数,就是下面这样。
    var asyncReadFile = async function (){
    var f1 = await readFile('/etc/fstab');
    console.log(f1.toString());
    };
    • 语法上,将 * 换成了 async,yield 换成了 await。
    • 语义上更清晰,一看就知道这是一个异步函数。需要 await 等待结果。
    • 内置执行器,不用依赖 co。
    • 返回值是Promise。

    ## 常用库的 Promise 版

    REF::

    Modern JavaScript - Collection

    1. 1. Object
    2. 2. Array
      1. 2.1. 三种创建 Array 的语法:
      2. 2.2. Accessor
      3. 2.3. Loop over
      4. 2.4. Index Operation
      5. 2.5. ES2015
      6. 2.6. Builder
      7. 2.7. Accessor
      8. 2.8. Loop over
      9. 2.9. Builder
      10. 2.10. Has
      11. 2.11. Accessor
      12. 2.12. Clear
      13. 2.13. Loop over
      14. 2.14. Other

    Array 是最传统的一个集合型的数据结构了,[1,2,3] ES 2015 对它进行了扩充。

    Set 不是一个数据,它是一个对象 {1,2,3},不提供 index 查找,也就意味着不能向指定位置插入和查找指定位置,可通过 for 遍历和解构。

    Map 好理解,是一个 key-value Hash {a: 1, b: 2, c:3},在 E2015 之前,都是通过对象来做 Hash 的。

    Object

    Array

    Array 是一个有序数组,提供 Index 操作。

    三种创建 Array 的语法:

    1
    2
    3
    [element0, element1, ..., elementN]
    new Array(element0, element1[, ...[, elementN]])
    new Array(arrayLength)

    Accessor

    Get by Index

    1
    2
    3
    4
    5
    6
    7
    let fruits = ["Apple", "Banana"];

    let first = fruits[0];
    // Apple

    let last = fruits[fruits.length - 1];
    // Banana

    Add

    1
    2
    3
    4
    5
    6
    7
    // Add to the end of an Array
    let newLength = fruits.push("Orange");
    // ["Apple", "Banana", "Orange"]

    // Add to the front of an Array
    let newLength = fruits.unshift("Strawberry") // add to the front
    // ["Strawberry", "Banana"];

    Remove

    1
    2
    3
    4
    5
    6
    7
    // Remove from the end of an Array
    let last = fruits.pop(); // remove Orange (from the end)
    // ["Apple", "Banana"];

    // Remove from the front of an Array
    let first = fruits.shift(); // remove Apple from the front
    // ["Banana"];

    Loop over

    1
    2
    3
    4
    5
    fruits.forEach(function (item, index, array) {
    console.log(item, index);
    });
    // Apple 0
    // Banana 1

    ES2015 for.of 语法

    JavaScript原有的for…in循环,只能获得对象的键名,不能直接获取键值。ES6提供for…of循环,允许遍历获得键值。

    1
    2
    3
    4
    5
    let index = 0;
    for (let f of fruits) {
    console.log(f, index);
    index++;
    }

    Index Operation

    Array 没有 has 用法,要通过 indexof 来取值,看返回的 index 判断是否存在。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Find the index of an item in the Array
    let pos = fruits.indexOf("Banana");

    // Remove an item by Index Position
    let removedItem = fruits.splice(pos, 1); // this is how to remove an item
    // ["Strawberry", "Mango"]

    // Insert item into arr at the specified index
    let insertedItem = fruits.splice(1, 0, 'Orange');
    // ["Strawberry", "Orange", "Mango"]

    ES2015

    ES 2015,扩充了一些新的类方法:


    ## Set

    Set 没有索引,数据不能重复,是一个无序数据,不能进行 sort。

    以下是 Set 支持的所有操作:

    Builder

    • new Set:创建一个新的、空的 Set。
    • new Set(iterable):从任何可遍历数据中提取元素,构造出一个新的集合。
    1
    2
    3
    4
    5
    Set 可以用一个数组来做初始化
    var set = new Set([1, 2, 3, 4, 4]);

    [...set]
    // [1, 2, 3, 4]

    Accessor

    • set.add(value):添加元素。如果与已有重复,则不产生效果。
    • set.delete(value):删除元素。如果并不存在,则不产生效果。

    .add() 和 .delete() 都会返回集合自身,所以我们可以用链式语法。

    高性能的 Array.indexOf()

    • set.has(value):判定集合中是否含有指定元素,返回一个布尔值。

    Other

    • set.clear():清空集合。
    • set.size:获取集合的大小,即其中元素的个数。

    Loop over

    • set.forEach(f)
    • for (let value of set)

    setSymbol.iterator:返回一个新的遍历整个集合的迭代器。一般这个方法
    不会被直接调用,因为实际上就是它使集合能够被遍历,也就是说,我们可以
    直接写 for (v of set) {…}等等。

    set.keys()、set.values()和 set.entries() 返回各种迭代器。这里的返回值是一样的,没意义,只是为了兼容 Map。

    遍历顺序就是插入顺序

    1
    2
    3
    let set = new Set([1, 2, 3]);
    set.forEach((value, key) => console.log(value * 2) )

    如果想对一个 Array 去重,可以这样写

    1
    [...new Set(array)]

    支持 map, filter 方法:

    1
    2
    3
    4
    5
    6
    7
    let set = new Set([1, 2, 3, 4, 5]);
    let set1 = set.map(x => x * 2);
    // 返回Set结构:{2, 4, 6, 8, 10}


    let set2 = set.filter(x => (x % 2) == 0);
    // 返回Set结构:{2, 4}

    ## Map

    Map 好理解,是一个 key-value Hash {a: 1, b: 2, c:3},在 ES2015 之前,都是通过对象来做 Hash 的,Map 提供了数据遍历等更强大的数据处理函数。

    注意区分Object和Map,只有模拟现实世界的实体对象时,才使用Object。如果只是需要key: value的数据结构,使用Map结构。因为Map有内建的遍历机制。

    一个 Map 对象由若干键值对组成,支持:

    Builder

    • new Map:返回一个新的、空的 Map。
    • new Map(pairs):根据所含元素形如[key, value]的数组 pairs 来创建一个新的
      Map。这里提供的 pairs 可以是一个已有的 Map 对象,可以是一个由二元数组
      组成的数组,也可以是逐个生成二元数组的一个生成器,等等。

    Has

    • map.has(key):测试一个键名是否存在,类似 key in obj。

    Accessor

    • map.get(key):返回一个键名对应的值,若键名不存在则返回 undefined,类似
      obj[key]。
    • map.set(key, value):添加一对新的键值对,如果键名已存在就覆盖。
    • map.delete(key):按键名删除一项,类似 delete obj[key]。

    Clear

    • map.clear():清空 Map。
    • mapSymbol.iterator:返回遍历所有项的迭代器,每项用一个键和值组成的
      二元数组表示。

    Loop over

    • map.forEach(f)
    • for (let [key, value] of map) { f(value, key,
      map); } 。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    for (let [key, value] of map) {
    console.log(key + " is " + value);
    }

    // 只想获取键名
    for (let [key] of map) { ... }

    // 只想获取键值
    for (let [,value] of map) { ... }
    • map.keys():返回遍历所有键的迭代器。
    • map.values():返回遍历所有值的迭代器。
    • map.entries():返回遍历所有项的迭代器,就像 mapSymbol.iterator。实
      际上,它们就是同一个方法,不同名字。

    Other

    • map.size:返回 Map 中项目的个数。

    http://node.green/#Map

    Modern JavaScript - ES2015 Modules

    1. 1. CommonJS Module
    2. 2. ES Modules (ESM)
    3. 3. Named exports
    4. 4. Default exports (one per module)
    5. 5. Mix exports

    自 ES2015 开始,Javascript 终于有了像样的模块机制,来应对大型应用。

    • 模块的一个作用就是隔离作用域,之前的作法会污染全局变量 (Global Space)。
    • 另一作用是更好的重用。


    ## Back & forward

    CommonJS Module

    1
    2
    3
    4
    const fs = require('fs')
    const { networkInterfaces } = require('os')
    const msg = 'Hello'
    module.exports = { msg }

    ES Modules (ESM)

    看起来简单、清爽、好用。

    1
    2
    3
    import fs from 'fs'
    import os, { networkInterfaces } from 'os'
    export const msg = 'Hello'

    ## export 用法

    首先,先看下常见的两种 export 用法:

    Named exports

    1
    2
    3
    4
    5
    6
    # export 一个类
    export class Logger { ... }
    # export 一个值
    export const ID = 123456;
    # export 一个函数
    export function sum() { ... }

    这些类,方法,属性,在 import 时,方式如下:

    1
    2
    3
    4
    5
    6
    # import 单个
    import { Logger } from './utils';
    # import 多个
    import { ID, sum } from './utils';
    # alias
    import { ID, Logger as TheLogger } from './utils';

    Default exports (one per module)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # export 一个类
    export default class Logger { ... }
    # export 一个值
    export default const id = 123456;
    # export 一个函数
    export default function sum() { ... }

    # Default exports support anonymous function declarations
    export default function () {}
    export default class {}

    这些类,方法,属性,在 import 时,方式如下:

    1
    2
    3
    4
    # Default 的 export 可以直接引用
    import Logger from "./logger";
    # 可以起别名
    import TheLogger from "./logger";

    Mix exports

    Default 只能有一个。Named export 可以有多个,但二都可以同时使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 以 React.js 为例(这里 export 不是 ES2015 的写法):
    var React = {
    Component: ReactComponent
    };
    module.exports = React;

    # ES 2015 的写法:
    let React = { ... }
    React.Component = ReactComponent;
    export default React;

    然后使用时,就可以这样 import 会很方便。

    1
    import React, { Component } from 'react';

    ## Namespace imports

    模块对象

    有时候,一个模块中有太多这样的小的方法,属性,一个个写的话比较繁琐,好在 ES2015 Modules 提供了一种优雅的方案:

    1
    2
    import React, * as ReactUtils from 'react';
    ## ReactUtils used as a namespace

    import * 时,导入的其实是一个模块命名空间对象,模块将它的所有属性都导出了。

    然后就可以这样调用函数了: ReactUtils. Component


    ## Static module structure

    聚合模块

    这是个人认为较好的一种实践,程序包中主模块的代码比较多,为了简化使用者更方便 import。可以用一种统一的方式将其它模块中的内容聚合 (Mix exports) 在一起导出。

    一般会在根目录下建立一个 index.js 文件,然后在其中,整理好对应的 export。

    可以参见 AntDesign

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 总 component 的 export 写法
    import Affix from './affix';
    export { Affix };


    # 这是其中一个 component 的 index.js 写法
    import Dropdown from './dropdown';
    import DropdownButton from './dropdown-button';

    Dropdown.Button = DropdownButton;
    export default Dropdown;

    ## Practice

    如果模块默认输出一个函数,函数名的首字母应该小写。

    1
    2
    3
    4
    function makeStyleGuide() {
    }

    export default makeStyleGuide;

    如果模块默认输出一个对象,对象名的首字母应该大写。

    1
    2
    3
    4
    5
    6
    const StyleGuide = {
    es6: {
    }
    };

    export default StyleGuide;

    如果模块只有一个输出值,就使用export default,如果模块有多个输出值,就不使用export default,不要export default与普通的export同时使用。


    ## ESM with Node (node.mjs)

    Node 的官方讨论,如何在 node 中支持 ES2015 Modules。目前有三种方案:

    • ‘use module’
    • myModuleName.mjs
    • Automatic Detection (is import/export exist)
    • Info in package.json

    看 ESP 规范,最后的方案是判断文本中是否含有 importexport,备选方案是 .mjs 后缀。


    ## Path Finder

    查找路径有三种方式,

    • 相对路径 import Menu from ./menu
    • 绝对路径 import Menu from /menu
    • npm_module import Menu from menu

    ## import 所有语法

    作为备注,这里记下 import 所有的用法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import defaultMember from "module-name";
    import * as name from "module-name";
    import { member } from "module-name";
    import { member as alias } from "module-name";
    import { member1 , member2 } from "module-name";
    import { member1 , member2 as alias2 , [...] } from "module-name";
    import defaultMember, { member [ , [...] ] } from "module-name";
    import defaultMember, * as name from "module-name";
    import "module-name";

    ## REF::