使用MEAN進行現代化WEB開發

積木村の研究所發表於2016-05-11

縱觀世界歷史,生產工具的進步一次次地推動著人類社會的發展。在Web領域,最近幾年也出現了一種新的生產工具棧,MEAN(MongoDB,Express,AngularJS,NodeJS),雖然其無法完全取代LAMP(Linux,Apache,MySql,PHP)/LNMP(Linux,Nginx,MySql,PHP),但確實是一種更先進的生產方式。

在MEAN技術棧中,NoSql優等生MongoDB取代了傳統的關係型資料庫勞模MySql。MongoDB官網上有其與Mysql的功能對比。相比MySql,MongoDB的唯一缺點是沒法支援複雜的事務,在現實的網際網路應用場景中,我們面對更多的是高併發、非事務性的業務;而對於一些簡單事務場景,MongoDB也是可以應對的,請參考關於mongo原子操作的探討。MongoDB天生是分散式的,支援自動分片(Auto-Sharding)、複製集(Replication Set),使得其可以很好的滿足大資料時代背景下,超高併發、自動容錯的儲存要求。

傳統WEB技術(LAMP/LNMP)中的PHP,被NodeJS取代。本質上講,PHP並不算後端語言,其主要目的是為了從各個資料來源獲取資料,拼接處理完成後,返回給頁面,因此我們也經常將PHP稱為“膠水語言”。PHP語言特性決定了其只能序列地從各個資料來源獲取資料,因此其響應時間為各個資料來源的響應時間之和。而NodeJS的非同步特性,使得其可以併發的請求多個資料來源,其響應時間為各個資料來源的響應時間之中最長的。相比PHP,NodeJS更適合作為“膠水語言”

MEAN中的Express,AngularJS分別為後端WEB框架和前端SPA(Single Page Application)開發框架。現代化的WEB生產方式需要有清晰的架構、高效的除錯方法、規範的測試方法、以及工程化的構建方法。下面,我們就從這些方面瞭解基於MEAN開發的WEB開發方式,本文要求讀者有傳統LAMP/LNMP開發經驗,同時熟悉NodeJS,並瞭解MongoDB。

1.MEAN基本程式碼框架

NodeJS是一個強社群驅動的技術方案,除了核心模組外,大部分功能需要第三方模組支援,這就導致一個WEB應用可能有幾十個模組,對於初學者,有較高的學習門檻,而一個標準的專案模板是一個比較好的入門方式。《MEAN WEB開發》一書從無到有構建了一個規範的MEAN專案,非常適合作為種子專案,該書的原始碼請從github上下載,本文也可以看作該書的讀書筆記。讓我們先從程式碼組織架構入手。

(1). 服務端MVC模型

NodeJS實現了CommonJS規範,其天生就支援現代化的模組化開發理念;而基於Express的中介軟體的架構,我們可以很方便地將常用功能(比如使用者許可權驗證、日誌模組)抽象為中介軟體模組。我們將服務端的程式碼組織為:

app/
    controllers/
    models/
    views/
    routes/
    config/
    tests/

這裡使用ejs作為view層的模板引擎,使用Mongoose作為Model層的ORM(物件關係對映)框架,每一個controller對應的獨立express路由檔案放在routes資料夾中,config資料夾存放各個中介軟體(passport、Mongoose)的配置,服務端單元測試指令碼在tests資料夾中。

(2). 客戶端MVVM模型

客戶端的基本框架是AngularJS,其本身也是將模組化理念作為其設計之本。我們將客戶端程式碼組織如下:

public/
    articles/
        aritcle.client.modules.js
        controllers/
        views/
        config/
        services/
        test/
            unit/
            e2e/
    users/
    lib/
    application.js

所有前端程式碼儲存在public中,對應服務端的每一個controller,客戶端都有一個相應的模組,每個模組放在單獨的資料夾中,比如articles和users資料夾。以articles模組為例,其中包含模組定義檔案aritcle.client.modules.js、控制器controllers、檢視模板views、路由檔案config、以及需要提供給其他模組使用的service,其中test資料夾用來存放單元測試指令碼和端到端測試指令碼。application.js是前端檔案的入口,用於載入各個模組(比如articles和users)。

2.高效的除錯

只有高效的除錯方式才能適應網際網路web產品的快速迭代的需求。下面我們分別介紹基於MEAN開發WEB應用過程中的前端和後端除錯方法。

(1). 使用node-inspector除錯服務端程式碼

藉助於node-inspector,我們可以像除錯瀏覽器端JS程式碼一樣除錯服務端程式碼。首先安裝node-inspector。

npm install -g node-inspector

然後啟動node-inspecor,並在啟動WEB應用時使用--debug引數

$: node-inspector
Visit http://127.0.0.1:8080/?port=5858 to start debugging.
$: node --debug server

這裡的埠是可以修改的。使用chrome開啟http://127.0.0.1:8080/?port=5858,我們就可以像除錯常規前端JS檔案一樣除錯服務端程式碼了。

(2). 使用Batarang除錯AngularJS

在除錯前端JS程式碼時,我們經常需要對AngularJS內部進行除錯,而由於AngularJS的過度封裝,對其內部除錯往往很棘手。為此,AngularJS團隊開發了一款名為Batarang的Chrome外掛。安裝完Batarang外掛後,用chrome開啟基於AngularJS框架的頁面時,使用開發者工具皮膚,就會出現了一個新的AngularJS標籤頁,使用該標籤可以結構化的預覽AngularJS各個Scope的變數,並可以檢視AngularJS的效能。

chrome-angular.png

3.規範的測試

規範化的測試是專案持續健康發展的根基,這裡我們主要介紹服務端單元測試。對於服務端,我們採用Mocha測試框架,Mocha提供了簡潔的介面,可以方便的實現單元測試:

describle(description,callback); //描述和封裝測試集,callback用於封裝測試邏輯
it(description,callback); //描述測試指標,callback用於封裝測試邏輯
before、after、beforeEach、afterEach等4個鉤子函式用來在測試的不同階段執行

Mocha測試框架並沒有提供斷言庫,因此缺少對測試結果進行邏輯判斷的功能。通過使用Should.js提供的語義化介面,我們可以方便對驗證程式碼邏輯的結果:

user.should.be.an.Object.and.have.property('name');

Should.js可以非常方便的測試物件,但卻無法對HTTP結果進行測試,我們需要SuperTest斷言庫來測試HTTP請求的結果。SuperTest斷言庫也提供了語義化的介面:

request(app).get('/user')
    .set('Accept','application/json')
    .expect('Content-type',/json/)
    .expect(200,done);

通過使用Mocha、Should.js和SuperTest組成的工具集,我們可以方便的實現服務端的單元測試,一個單元測試的例子如下:

//建立Article Model的測試集
describe('Article Model Unit Tests:', function() {    
    //每次測試前執行的勾子函式
    beforeEach(function(done) {
        //建立測試過程中所需要的資料    
        done();
    });

    //測試article是否能夠儲存成功
    describe('Testing the save method', function() {
        //測試正常文章儲存邏輯
        it('Should be able to save without problems', function() {
            article.save(function(err) {
                should.not.exist(err);
            });
        });

        //測試異常文章儲存邏輯
        it('Should not be able to save an article without a title', function() {
            article.title = '';
            article.save(function(err) {
                should.exist(err);
            });
        });
    });

    // 每次測試後執行的勾子函式
    afterEach(function(done) {
        //清除測試過程中產生的資料
        done();
    }
}

執行測試用例:

mocha --reporter spec app/tests

正常情況下,該測試的結果是:

 Article Model Unit Tests:
    Testing the save method
        √ Should be able to save without problems
        √ Should not be able to save an article without a title
 2 passing (135ms)

```

對於前端,主要採用我們熟悉的AngularJS單元測試和e2e測試,請參考這篇部落格

4.自動化的專案流程

前端工作的特點決定了其工程問題多於技術問題,因此在開始實際的專案程式碼開發之前,應該首先做好專案流程的自動化。前端專案構建工具很多,包括Grunt、Gulp、WebPack以及國內的fis等。一個完善的自動化專案流程主要包括:

自動化構建:js、css程式碼檢查,前端js、css合併、md5戳生成、雪碧圖生成、上傳的CDN等重複性操作
自動化除錯:通過新增特定的引數,比如--debug,可以方便的除錯應用
自動化測試:每次執行應用運用之前,將單元測試集執行一遍,確保功能正常
自動化執行:以上三個步驟完成後,就可以執行應用了

本文同時發表在我的部落格積木村の研究所http://foio.github.io/mean-web/

相關文章