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。
這個特性會造成升級後相關載入失效,要注意。
懶人包:
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/
http://stackoverflow.com/questions/33505992/babel-6-changes-how-it-exports-default