用“MEAN”技術棧開發web應用(一)AngularJs前端架構

發表於2015-09-09

前言

不知何時突然冒出“MEAN技術棧”這個新詞,聽起來很牛逼的樣子,其實就是我們已經熟悉了的近兩年在前端比較流行的技術,mongodb、express、angularjs、nodejs,由於這幾項技術涵蓋了從前端到後端再到資料庫,可以用他們完整的開發一個web應用了,所以成了一個非常牛逼的組合,頗有當年LAMP的氣勢。前端要從切圖仔邁向全棧的路上,這幾門技術必須得有所涉獵。本系列文章利用自己虛構的一個小專案為例,對“使用MEAN技術棧開發web應用”做一個入門級的介紹。

AngularJs的爭議

angular,簡稱ng,是google出品的優秀框架,在2013~2014年大紅大紫,但是國內好像慢一拍,我從2015年才看到使用ng的專案大量出現。ng自出現伊始就有人詬病太難上手了,完全不同的開發方式,團隊開發更是不知道如何組織程式碼。不過隨著jquery這位老大哥逐漸被拋棄,大家開始慢慢接受mvvm這樣的程式設計思維。然而一個不好的訊息是,ng團隊打算重構的angular2.0版本要發生重大變革,與1.0不能同日而語,雖然官方有1.0向2.0遷移的方案,但額外的工作總是不太好的,而且使用2.0還要付出更多的學習成本。
再加上今年又有react這個實力派雄起,ng的風頭頓時被搶過去了,人們又開始研究react下的程式設計方式。不過我估計react的真正實用也得等到一兩年後。眼下angular1.x也仍然是一個不錯的選擇。儘管有2.0的變革,但是1.4還是一個穩定版本,我們使用穩定版本肯定是不會有問題的。
所以我的結論是,但用無妨,不會存在白學了這種事情,就算將來angular1.x廢棄了,你學到的程式設計思維還是在的。
本文討論如何使用AngularJs進行前端的架構,對於ng的基礎知識不做講解,需要了解的同學可以看我之前寫過的一個系列http://www.cnblogs.com/lvdabao/tag/AngularJs/

練手專案簡介

為了系統的學習“MEAN”技術棧,我虛構了一個小專案,先做一個介紹。
QuestionMaker,是一個用於生成調查問卷的系統,使用者可以編輯試題(選擇題、填空題),並可以實時預覽編輯結果。然後還可以編輯一份試卷,為試卷新增試題,然後儲存為一分完整的調查問卷。有點類似於調查派。先上一張截圖吧:
專案的功能主要是CRUD操作,所以非常適合angular的應用場景,雙向繫結對於實現實時預覽這樣的功能簡直是信手拈來。
專案的前後端是完全分離的,後端不渲染頁面,只提供資料介面,前端使用ng的動態模板來渲染頁面,通過ajax請求來獲取所需資料。
專案我已經開源到github,有興趣的同學可以檢視:https://github.com/Double-Lv/QuestionMaker

前端目錄結構

用ng來構建一個專案應該如何安排目錄結構呢?為了不人工增加複雜度,我這裡沒有用bower來管理依賴庫,也沒有其他文章中介紹的那樣用yeoman來生成專案,只是單純的手動來建立目錄,這樣可以把我們的注意力集中到專案的核心上,目錄結構是這樣的:
前端的程式碼都在src目錄下,包括入口檔案index.html,這樣方便我們後續做合併壓縮等編譯工作,編譯後的檔案可以一併放入dist目錄下。

首頁index.html

這是專案的入口頁面,其實就是一個大容器,在這裡載入所有的js和css檔案,然後提供一個檢視容器就夠了,因為從這個頁面以後,我們頁面就不再會有跳轉,全部是通過前端路由來做區域性重新整理,首頁的程式碼非常精簡:

入口檔案app.js

有了入口頁面,還得有一個js的啟動入口,就是這個app.js了,在這裡它只做了兩件事情:
1. 啟動angular,程式碼只有一行:

我們擁有了一個名為app的全域性模組。這裡把ui.router給注入了,因為我們整個應用都用ui-router來做路由,後面會做詳細介紹。
2. 把ui-router的$state和$stateParams服務掛到$rootScope上,這樣我們在後面所有的模組中,都能夠訪問到路由引數,不必在每個地方都注入一次了。程式碼也是相當簡單:

控制器合集controllers.js

controller.js裡面是所有的controller定義,由於這個專案比較小,而且反正最後都要合併,所以就都放在一個檔案裡了,這樣可以使用鏈式寫法app.controller(‘a’, …).controller(‘b’, …), 一口氣將所有的controller都定義好。如果專案比較大,controller多,可以把controllers建為一個資料夾,然後在裡面放各個controller。
controller裡面就是跟業務相關的一些程式碼了,如試題資料的初始化,新增答案、刪除選項等操作。
但是當我們需要發起ajax請求的時候,如儲存試題,就不宜在controller裡面直接寫了,這樣會造成邏輯混雜程式碼混亂。所有需要請求服務端的操作,我們可以抽象為一個個服務,進行“分層”,通過ng提供的service機制來做呼叫。

服務合集services.js

接上面,所有和試題相關的服務端請求,我們可以封裝成一個QuestionService,這個服務提供:提交試題、刪除試題、更新試題等服務,這樣層次就很清晰了。
所以,在services.js中,我們定義所有和服務相關的東西,在本專案中,我們的服務全都是ajax請求,可以用ng提供的$http服務來很方便的完成。事實上service中並不是必須寫ajax請求,凡是可以抽象理解為“公共服務”的東西,都可以定義在這裡,可以被其他模組隨意呼叫。

指令合集directives.js

瞭解過ng的同學應該對指令不會陌生,通過指令我們可以用擴充套件html標籤的方式來很容易的實現一些UI效果,使用方便、可被多個地方公共使用,就像過去我們寫jquery外掛一樣。所有的指令都定義在這個檔案中,同樣可以使用鏈式寫法,很爽。
在我們的專案中,有一些功能是通用的,例如列表的分頁,那麼就可以把分頁功能做成一個指令。我定義了一個名為pagenav的指令,然後在所有需要用分頁的地方就可以呼叫了,程式碼如下:

只需一個標籤,然後通過屬性指定分頁資料和翻頁函式即可。

過濾器合集filters.js

我們的專案使用ng提供的動態模板,服務端不渲染頁面,只提供資料介面。有些資料我們需要進行格式化後進行輸出,這就用到filter了,所有的filter都放在這裡。filter的定義和使用的非常簡單,此不不多述了。

前端路由定義routes.js

本專案使用ui-router來做前端路由,這個目測也是現在最流行的做法。ui-router是一個第三方外掛,由於ng內建的ngRouter功能較弱,無法實現巢狀路由和多檢視路由,而ui-router引入了“狀態”這個概念來控制檢視,從而實現這些功能,所以ui-router成了最好的選擇。它是angular-ui專案(http://angular-ui.github.io/)中的一個模組,該專案還提供了很多基於ng的ui,像日期選擇器什麼的。ui-router貌似是最受歡迎的一個。
用ui-router可以實現巢狀路由和同一頁面多檢視,具體使用方法可以參考我部落格中轉載的幾篇文章:http://www.cnblogs.com/lvdabao/articles/4657235.html
本專案中,由於整站無重新整理,所以路徑的層級會比較深,巢狀路由就派上了用場。在入口頁面index.html中,用一個div來做父容器,加上ui-view屬性,就可以在裡面載入別的模板了。從試題列表到試題編輯頁面的切換,就都在這個父容器中載入。
而在試題編輯頁面,又有對應的題型編輯和試題預覽檢視,通過給ui-view賦予名字,就可以載入各自對應的模板,這裡就是多檢視的應用。程式碼片段如下:

在試卷預覽頁面,我們也需要對試題進行展示,只需在頁面上在定義一個ui-view,然後在路由中進行配置,就可以載入試題預覽模版,很容易的實現了模板的複用。
頁面中沒有任何邏輯,只需在route.js中配置好路由規則,整站無重新整理跳轉就這麼輕而易舉的實現了。

tpl目錄

利用ui-router做了前端路由後,除了入口頁面index.html外,其他所有頁面就都變成模板了(被ui-router動態載入)。所有的模板都放在tpl目錄下。如果業務的模組較多,可以在此目錄下再新建資料夾,本專案比較簡單,所以就只有一層。不論有多少層目錄,在routers.js中配置好就OK啦。利用ui-router可以注入模板對應的控制器,所以程式碼中我們也不必在加ng-controller,模板檔案中就是很乾淨的ng模板。

lib目錄

這裡放置的是專案所需的外部庫。有angular、ui-router、jquery、bootstrap。你可以看到我只是把程式碼檔案給直接放裡面了,沒有用當下流行的bower進行管理。是因為我不想再人為的增加複雜度,萬一有人的機器上bower安裝失敗或者git環境有問題,或者github無法訪問,都會令人十分沮喪。
反正就這幾個穩定版本,不如直接下載過來。如果需要壓縮我後期用gulp來搞一下就行了。

總結

這個小專案的前端結構就是這個樣子啦。從上面我們可以看出,用ng來做前端的架構還是很有條理的。controller、service、directive這些概念,本質上還是“模組”,所以我們可以以模組開發的方式來很爽快的寫程式碼,檔案與檔案之間沒有任何耦合和依賴。模組所需的依賴,我們通過ng的注入機制來給注入。所以在index.html中引入這些檔案的時候,沒有順序要求,任意順序引入皆可。
順便說一句,前端程式碼的後處理,我已經用gulp寫好了指令碼,用npm安裝所需的包後,執行gulp就可以編譯生成dist目錄。
本文只做angular前端架構入門級別的介紹,關於文中提到的一些具體技術細節,可以檢視我寫的angular系列文章http://www.cnblogs.com/lvdabao/tag/AngularJs/
這個示例專案我已開源到github(https://github.com/Double-Lv/QuestionMaker),目前已經實現了基本功能。後續我會擴充套件更多的功能,到時候也必然會涉及到更多的技術問題,angular進行前端架構的路才剛剛開始。

相關文章