# 如何在Github“尋找靈感(fork)”
> 重造輪子是重新創造一個已有的或是已被其他人優化的基本方法。
[TOC=2,3]
最近萌發了一個想法寫游戲引擎,之前想著做一個JavaScript前端框架。看看,這個思路是怎么來的。
## [Lettuce](https://github.com/phodal/lettuce)構建過程
> Lettuce是一個簡約的移動開發框架。
故事的出發點是這樣的:`寫了很多代碼,用的都是框架,最后不知道收獲什么了`?事實也是如此,當自己做了一些項目之后,發現最后什么也沒有收獲到。于是,就想著做一個框架。
### 需求
有這樣的幾個前提
- 為什么我只需要jQuery里的選擇器、Ajax要引入那么重的庫呢?
- 為什么我只需要一個Template,卻想著用Mustache
- 為什么我需要一個Router,卻要用Backbone呢?
- 為什么我需要的是一個isObject函數,卻要用到整個Underscore?
我想要的只是一個簡單的功能,而我不想引入一個龐大的庫。換句話說,我只需要不同庫里面的一小部分功能,而不是一個庫。
實際上想要的是:
> 構建一個庫,里面從不同的庫里面抽取出不同的函數。
### 計劃
這時候我參考了一本電子書《Build JavaScript FrameWork》,加上一些平時的需求,于是很快的就知道自己需要什么樣的功能:
- Promise 支持
- Class類(ps:沒有一個好的類使用的方式)
- Template 一個簡單的模板引擎
- Router 用來控制頁面的路由
- Ajax 基本的Ajax Get/Post請求
在做一些實際的項目中,還遇到了這樣的一些功能支持:
- Effect 簡單的一些頁面效果
- AMD支持
而我們有一個前提是要保持這個庫盡可能的小、同時我們還需要有測試。
### 實現第一個需求
簡單說說是如何實現一個簡單的需求。
#### 生成框架
因為Yeoman可以生成一個簡單的輪廓,所以我們可以用它來生成這個項目的骨架。
- Gulp
- Jasmine
#### 尋找
在Github上搜索了一個看到了下面的幾個結果:
- [https://github.com/then/promise](https://github.com/then/promise)
- [https://github.com/reactphp/promise](https://github.com/reactphp/promise)
- [https://github.com/kriskowal/q](https://github.com/kriskowal/q)
- [https://github.com/petkaantonov/bluebird](https://github.com/petkaantonov/bluebird)
- [https://github.com/cujojs/when](https://github.com/cujojs/when)
但是顯然,他們都太重了。事實上,對于一個庫來說,80%的人只需要其中20%的代碼。于是,找到了[https://github.com/stackp/promisejs](https://github.com/stackp/promisejs),看了看用法,這就是我們需要的功能:
~~~
function late(n) {
var p = new promise.Promise();
setTimeout(function() {
p.done(null, n);
}, n);
return p;
}
late(100).then(
function(err, n) {
return late(n + 200);
}
).then(
function(err, n) {
return late(n + 300);
}
).then(
function(err, n) {
return late(n + 400);
}
).then(
function(err, n) {
alert(n);
}
);
~~~
接著打開看看Promise對象,有我們需要的功能,但是又有一些功能超出我的需求。接著把自己不需要的需求去掉,這里函數最后就變成了
~~~
function Promise() {
this._callbacks = [];
}
Promise.prototype.then = function(func, context) {
var p;
if (this._isdone) {
p = func.apply(context, this.result);
} else {
p = new Promise();
this._callbacks.push(function () {
var res = func.apply(context, arguments);
if (res && typeof res.then === 'function') {
res.then(p.done, p);
}
});
}
return p;
};
Promise.prototype.done = function() {
this.result = arguments;
this._isdone = true;
for (var i = 0; i < this._callbacks.length; i++) {
this._callbacks[i].apply(null, arguments);
}
this._callbacks = [];
};
var promise = {
Promise: Promise
};
~~~
需要注意的是: `License`,不同的軟件有不同的License,如MIT、GPL等等。最好能在遵循協議的情況下,使用別人的代碼。
### 實現第二個需求
由于,現有的一些Ajax庫都比較,最后只好參照著別人的代碼自己實現。
~~~
Lettuce.get = function (url, callback) {
Lettuce.send(url, 'GET', callback);
};
Lettuce.load = function (url, callback) {
Lettuce.send(url, 'GET', callback);
};
Lettuce.post = function (url, data, callback) {
Lettuce.send(url, 'POST', callback, data);
};
Lettuce.send = function (url, method, callback, data) {
data = data || null;
var request = new XMLHttpRequest();
if (callback instanceof Function) {
request.onreadystatechange = function () {
if (request.readyState === 4 && (request.status === 200 || request.status === 0)) {
callback(request.responseText);
}
};
}
request.open(method, url, true);
if (data instanceof Object) {
data = JSON.stringify(data);
request.setRequestHeader('Content-Type', 'application/json');
}
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
request.send(data);
};
~~~