前端工程師也可以淡定的開發網站應用!RedTea Web Framework!

還記得,尚未投入 Node.js 前,一直覺得 Node.js 帶來了未來,讓我們可以用 JavaScript 同時開發 Web 前端(Front-end)和後端(Back-end),等到真的投入 Node.js 後,發現雖然事實的確是如此,但由於前端和後端應用所需要的背景知識不盡相同,開發模式和概念更是大異其趣,所以,雖然同樣是使用大家熟悉的 JavaScript 語言,但前端開發者仍然不見得能夠如願地來開發後端應用。

這樣的情況讓我想起有位來台灣工作的外國朋友,曾告訴過我一個多年前發生在他身上的笑話。故事是:
他原本是個美術方面的設計師,然後,但有一天老闆對他說:『你從現在起,去做開發程式的工作吧。』

他問:『為什麼?』

老闆回答:『寫程式是用英文寫,而你又會英文,不就可以寫嗎?』

是的,同樣道理,雖然對前端工程師而言,JavaScript 是最熟悉的程式語言,而 Node.js 又可以讓你使用 JavaScript 寫整個 Web 應用,但這不代表對這些人而言,就可以輕易上手 Web service 的後端開發。要真正讓前後端開發合而為一,不只是語言要統一,開發經驗也要相同才是,這才是所有 Web 開發者最期盼的事。

於是筆者基於 Node.js,開發了『紅茶(RedTea)』,這是一個和現有的網站框架(Web Framework)所不一樣的全新的嘗試。以前端開發者視角和經驗為出發點,專門設計給前端工程師上手使用的網站框架,讓前端工程師也可以『淡定』的開發網站應用。由於 RedTea 不是傳統 MVC 模型的 Framework(至於 RedTea 採用的是哪種開發模型,筆者一時也說不上來。),所以,如果你是一個已經被 MVC 涂毒已深的開發者,可能要先花點時間重新理解一下。:-)

此外,RedTea 有一個最大的特點,就是支援了在瀏覽器環境下,呼叫後端 JavaScript Class/Function 的功能,就像在使用本地端的 JavaScript 物件般。因此,前後台交換資料,不用再以 GET/POST、URL Path Routing 或 Ajax 相關的方法實作,只要學會怎麼使用 JavaScript Class 即可。重點是,即使你沒有任何 HTTP 通訊協定的知識,或後前後端資料處理的經驗,依然可以開發出網站程式。

當然,設計 RedTea 的目的,除了是為前端工程師著想之外,也是因為長久以來在思考 MVC 模型的問題後,所嘗試提出的解決方案。至於是什麼問題,可以從專案開發的流程探討,一個基於 MVC 模型的 Web 專案開發流程大致上如下(問題點也將補充在後面):
*註:『程式架構和演算法:Model』、『視覺部份和 UI:View』、『控制機制:Controller』
  1. 設計 Model(問題:當你一開始根本不完全清楚功能需求時,你如何能設計一個完善的架構去容下一切?)
  2. 設計 View
  3. 設計 Controller,用來連接程式邏輯架構和前端 UI (問題:有太多種交換方式,GET/POST Routing stuffs, Ajax APIs 等)
  4. 為了更完整的功能需求,一次又一次修改和重構 Model
  5. 為了配合程式架構和演算法的修改,一次又一次修改和重構 View
  6. Controller 零亂又噁心不已

說穿了,這種來來回回式的開發方法,若是在前後端是不同語言的情況下,將會是不得已的情況。畢竟,前後端需要共同定出一個雙方可以接受的資料交換格式,然後在開發過程中逐漸磨合。不過,當前後端都是 JavaScript 的情況下,是否可以簡化這部份的開發流程,就是一個可以思考的地方。

所以,RedTea 的設計,主要是為了達成這樣的 Web 開發流程:
  1. 設計你眼前第一時間想看到的東西
  2. 在所看到的 UI 上,實作功能需求
  3. 在後端(Server-side)處理使用者因需求產生的資料

由於前後端都是 JavaScript,很容易做到前後端邏輯上的同步,再加上簡單的轉換器和 RPC 設計,就可以讓前端程式直接呼叫後端的 Class 和 Function。後端的 API,就彷彿是你前端程式的一部份。

說了這麼多,到底如何使用 RedTea 開發出一個網站呢?如果你參考 Github 上的 RedTea 範例,會發現主要有四個目錄,分別說明如下:
  1. ui - Layout Template
  2. runner - Browser-side Script
  3. routes - URL Path Routing
  4. apis - Server-side APIs

若你有摸過其他的 MVC Web Framework,對 routes 的功用應該不陌生,而 RedTea 當然也可以像其他框架一樣,讓你隨意自定 URL 的橋接。但若要善用 RedTea 的優勢,routes 的主要目的應該只是決定 UI 和 Runner 的組合,如下:
module.exports = {
    '/': index
};

function index(app, req, res) {
    /* Using index.jade (UI) and index.js (Runner) */
    res.render('index', { title: 'RedTea' });
};

RedTea 在回應瀏覽器的要求時,會合併指定的 UI 和 Runner,並自動代入 RedTea Caller 的機制,讓 runner 被輸出到前端時,有能力呼叫 Server-side APIs。

一個 runner 程式大概長的是這個樣子(examples/runner/index.js):
RedTea.import();

RedTea.main(function() {
    var chat = new RedTea.API.Chat;

    chat.say('Fred', 'Hello World!');
    chat.getConversation(function(err, data) {
        for (var i in data) {
            var lineObj = data[0];
            var dom = document.getElementById('conversation');
            dom.innerHTML += lineObj.name + ':' + lineObj.content + '<br>';
        }
    });
});

從範例中,你會看到瀏覽器上才有的 document 物件出現,事實上 runner 就是一支跑在瀏覽器上的 JavaScript 程式。 RedTea.import() 會去 Server 載入目前已經被定義的 API,等初始化完成,會以 RedTea.main() 為程式進入點開始跑。這時,你就可以用 RedTea.API 去建立 Server API 定義的物件,然後呼叫在 Server-side 的函數方法。進一步,你也可以用得到的資料,去更新網頁上的 DOM。

至於 Server-side APIs 的定義與寫一般 JavaScript 無異,你可以定義 Class、Function 或是變數,RedTea 會自動將這些 API 轉換成前端可以跑的形式,而原始的 API 定義長這個樣子(examples/apis/chat.js):
module.exports = {
    Chat: Chat
};

function Chat(externalData) {
    this.externalData = externalData;
    this.publicData = externalData.userdata;
}

Chat.prototype.getUserList = function(callback) {

    callback(null, this.publicData.users);
};

Chat.prototype.getConversation = function(callback) {

    callback(null, this.publicData.conversation);
};

Chat.prototype.say = function(name, content) {
    var line = {
        name: name,
        content: content
    };

    this.publicData.conversation.push(line);
};

然後和一般的 Node.js 程式一樣,你會需要一支主程式(examples/app.js):
var RedTea = require('redtea');

var app = new RedTea;

app.routeDirs.push(__dirname + '/routes');
app.uiDirs.push(__dirname + '/ui');
app.runnerDirs.push(__dirname + '/runner');
app.apiDirs.push(__dirname + '/apis');

var publicData = {
    users: [],
    conversation: []
};

app
    .initRoute()
    .initRender()
    .initAPI(publicData)
    .listen(9876);


只會 JavaScript 嗎?開始享受單純用 JavaScript 開發網站應用程式的人生吧!卡關了?就喝杯紅茶淡定一下! :-D

後記

RedTea 目前只是原型,還不是相當完整,像是 static file 的功能都尚未實作,很多功能也還只是堪用,歡迎各界一同補齊它。 :-)

留言

  1. great idea!

    但還有個小問題,紅茶其實叫做 blacktea 。

    回覆刪除
    回覆
    1. 哈哈,對不起!我英文不好。 orz

      沒有啦,其實是故意的,用 Red 比較有 Kuso 的感覺。

      接下來還會有 DamnDing 和 BlueTea :-P

      刪除
    2. DamnDing 聽起來很酷~ XD

      刪除
  2. 你做的這個遠端程序呼叫 RPC,十年前就有人做了

    通用的規格叫做 JSON-RPC

    NodeJs 的實作則是 node-jsonrpc

    https://github.com/ericflo/node-jsonrpc

    回覆刪除
    回覆
    1. 謝謝提醒,文章內有說明我實作了簡單的 RPC,所以我想我應該沒有說錯。 :-)

      由於現有的 RPC 實作,在一般的使用上,你還是無法像使用一般本地端 API 的方式去使用和定義它。

      所以我另外設計了轉換器,讓他變成一般形式的 JavaScript 物件,讓開發者在使用上根本感覺不出來他是遠端的 API。

      這是為什麼設計成一個一體成形的框架。目的是隱藏複雜的開發過程,而不只是單純的 RPC 而已。

      刪除
    2. 如果是這樣的話,同樣的概念在 meteor 就有了,你直接用 meteor 不就好了 ?

      meteor.js http://meteor.com

      刪除
    3. 重新發明輪子是一個自由軟體開發者最愛做的事,尤其是當原本的輪子根本不好用時。:-)

      我想可能是我眼拙,看到 Meteor 和 RedTea 其實差異相當大,Meteor 把一切事情搞複雜了。

      有幾點最大不一樣,也是我認為 Meteor 的缺點:

      1. 他創造了自己的開發習慣,而且非常不一樣,不能延用大多數現有 MVC Framework 的概念,所以一般人入門上是有一定困難。
      2. 與 Database 綁的太死,Meteor 的概念是以 data 流向處理為導向,所以目前綁死 Database。
      3. 也因為如此,和 UI 綁的太死,總是試圖直接將 data 導到前端後,用自己的 API 直接因應 data 變化控制去 UI。因此和 UI 根本密不可分,所以你會看它的程式,好像在看過去『無 Framework 式』的網站設計,總是和 UI 元件混雜分不開。事實上,這件事對那群身為 JavaScript/jQuery 專家的前端工程師來說,無疑是多此一舉,他們有太多方式或更好途徑可以玩弄 DOM,根本不需要 Web Framework 來為他們處理。

      4. 他的前後端 API 操作,仍然『不直覺』。和一般的 RPC 一樣, Meteor 只是提供一些 API 去 定義/呼叫 server-side methods。
      5. 在一支程式裡混雜撰寫前後端程式,以 Meteor.is_server/Meteor.is_client 這種方式判斷該在哪裡跑是相當愚蠢的。除了增加程式複雜度,更是讓開發者難以維護程式。

      所以,至少我個人不認為初入後端開發的前端工程師,能容易上手或理解 Meteor 的概念,更難以寫出一手容易維護的 Code。

      問題點並不在於可不可以直接在前端呼叫後端的 methods 或是 binding event,而是怎麼去做這件事。還有做這件事時,是不是符合一般人開發前端 JavaScript/HTML 程式的習慣。

      因此我覺得,最好的方式就是將後端 Class 直接原封不動的搬到前端,讓這些 Class 在使用上,就像是 Browser 原生提供的一樣。從我提供的 RedTea 範例就可以看到,對開發者而言,其實根本只是在前端使用 new 方法,就可以去得到後端的 Class。而且概念上不只是方法(method),還可以取得該 Class 裡的各種成員(member)資料,甚至是進一步做到直接修改成員資料就回寫到 Server,已經不是單純 method/function 等級的呼叫。

      BTW, 在 RedTea 之下,你可以保有原本 MVC 的開發模式,可以把 UIs, Client-side Script(Runner) 和 Server APIs 各部份分開獨立開發。也因為是這樣,其實 RedTea 是能以 Middleware 的形式,整合到 connect/express 這類的 MVC Framework 之中,而且完全相容。這就是我下一步想要做的事,目前只是確定 RedTea 這樣的開發模型是可行的。

      我認為,Node.js 最大的優點在於 JavaScript 的適用性和普及性,如果不用學太多前後端的概念,只要學會一般的前端網頁上的 JavaScript 後,就能開發網站程式,這是多麼美好。而且,這肯定只有 Node.js 才可以做到,但是前提是我們不能把這件事搞複雜了。

      刪除
    4. 你的東西... 看起來沒有比較簡單好用阿 ^^|||||

      刪除

張貼留言

這個網誌中的熱門文章

Web 技術中的 Session 是什麼?

上手使用 JavaScript 的 Map、Reduce 吧!

淺談 USB 通訊架構之定義(二)

JavaScript 好用的 async 異步函數!

淺談 USB 通訊架構之定義(一)