升級 koa 2.0 及 babel 設置與注意事項

koa 是什麼這邊就不多作解釋,koa 1.0 跟 koa 2.0 升級基本上沒有想像中的簡單,年前進行了一次,以失敗告吹,今年又進行了一次,總算革命成功。

其中 koa 1.0 跟 2.0 最大的差異就是原本使用 function*/yield 改為使用 async/await 因此我們需要 babel 的幫忙以便讓我們使用該特性。

babel 設置

koa 2.0 可以搭配最新的 babal 我們需要下面相關套件

    "babel-core": "^6.4.0",
    "babel-polyfill": "^6.3.14",
    "babel-preset-es2015": "^6.3.13",
    "babel-preset-stage-0": "^6.3.13",

安裝相關套件後,我們就可以在啟動 koa 之前設置

require("babel-core/register")({  
     presets: ['es2015', 'stage-0']
});
require("babel-polyfill");

module.exports = require('./koa.js');  

如此就可以搭配 koa 使用 async/await,接著因為新舊的差異,第一個影響到的就是 koa 相關的 middleware package,畢竟不是所有的套件都有跟著升版到新的機制,這也是升級最痛苦的地方,還好有 koa-convert

koa-convert 將舊版轉換為新版處理機制

使用方法如下:

import convert from 'koa-convert';

app.use(convert(function *(next){  
  const start = new Date;
  yield next;
  const ms = new Date - start;
  console.log(`${this.method} ${this.url} - ${ms}ms`);
}));

若是 middleware 可以這樣用

import responseTime from 'koa-response-time';  
import convert from 'koa-convert';  
app.use(convert(responseTime()));  

過渡時期可以讓尚未升級的套件運作正常,接著升級後程式碼會有幾個寫法上的改變

語法變更

function*/yield change to async/await

看 diff 最快 :)

Babel v6.4 Requires semicolons after class properties

https://github.com/feross/standard/issues/372

新的 Babel class 的 properties 強制需要 ;

await* 廢用

await* 之 *Promise.all 取代。

export default 使用 require 多了一層 default

另外一個雷,在 babel 6 若你用 export default 務必使用 import,若使用 require 將會多一層 default。

這個特性會造成升級後相關載入失效,要注意。

詳細解釋請參考: https://medium.com/@kentcdodds/misunderstanding-es6-modules-upgrading-babel-tears-and-a-solution-ad2d5ab93ce0#.k4730osf2

懶人包:

Option 1: require with default

// add.js
export default (x, y) => x + y  
// bar.js
const three = require('./add').default(1, 2  

Option 2: ES6 modules 100%

// add.js
export default (x, y) => x + y  
// bar.js
import add from './add'  
const three = add(1, 2)  

Option 3: CommonJS 100%

// add.js
module.exports = (x, y) => x + y  
// bar.js
const three = require('./add')(1, 2)  

若你想要在 babel6 保持 babel5 的 export 的特性可以參考

http://stackoverflow.com/a/33725725

之所以 babel6 規格會這樣要求,主要是因為 Tree-shaking 的特性,所以長遠來看,建議是使用 Option 2: ES6 modules 100% 的選項。

詭異的 this

有 this: 使用 async + fat arrow 但沒定義函式參數

export default class Test {  
  hello = async () => {
    console.log(this); // 這裡有東西
    let hello = 'yes!';
    return {hello};
  }
}
describe("Test this", () => {

  it.only("test", async done => {
    try {
      let test = new Test();
      let result = await test.hello();
      done();
    } catch (e) {
      done(e);  
    }
  });
}

沒 this: 使用 async + fat arrow 並且定義函式參數

export default class Test {  
  hello = async (config) => {
    console.log(this); // 這裡沒東西
    let hello = 'yes!';
    return {hello};
  }
}
describe("Test this", () => {

  it.only("test", async done => {
    try {
      let test = new Test({});
      let result = await test.hello();
      done();
    } catch (e) {
      done(e);  
    }
  });
}

有 this: 使用 fat arrow 不用 async

export default class Test {  
  hello = (config) => {
    console.log(this); // 這裡有東西
    let hello = 'yes!';
    return {hello};
  }
}
describe("Test this", () => {

  it.only("test", done => {
    try {
      let test = new Test();
      let result = test.hello({});
      done();
    } catch (e) {
      done(e);  
    }
  });
}

有 this: 使用 async 並且使用 class 的方式宣告(不用 fat arrow)

export default class Test {  
  async hello (config) {
    console.log(this); // 這裡有東西
    let hello = 'yes!';
    return {hello};
  }
}
describe("Test this", () => {

  it.only("test", async done => {
    try {
      let test = new Test();
      let result = await test.hello({});
      done();
    } catch (e) {
      done(e);  
    }
  });
}

目前還沒有辦法知道詳細原因,先做筆記,只知道使用 fat arrow + async + 參數會造成 this undefined,怪哉!

結論

上述就是目前有遇到的差異以及需要注意的地方,給大家參考,一旦升級之後,統一都使用 async/await,在也不會搞混了。

若有其他新的發現會再陸續補上,若有沒有注意到的也請不吝告知 :)

參考

http://n.thepana.com/2015/11/25/koa2-0/

https://medium.com/@kentcdodds/misunderstanding-es6-modules-upgrading-babel-tears-and-a-solution-ad2d5ab93ce0#.k4730osf2

http://stackoverflow.com/questions/33505992/babel-6-changes-how-it-exports-default

http://www.2ality.com/2015/12/webpack-tree-shaking.html