文章出處
View Code
文章列表
前言
不知何時突然冒出“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文件,然后提供一個視圖容器就夠了,因為從這個頁面以后,我們頁面就不再會有跳轉,全部是通過前端路由來做局部刷新,首頁的代碼非常精簡:

<!doctype html> <html ng-app="QMaker"> <head> <meta charset="utf-8"> <title>Question Maker</title> <link rel="stylesheet" href="/src/lib/bootstrap-3.3.5/css/bootstrap.min.css"> <script src="/src/lib/jquery-1.9.1.min.js"></script> <script src="/src/lib/angular-1.4.1.min.js"></script> <script src="/src/lib/angular-ui-router.min.js"></script> <link rel="stylesheet" href="/src/css/base.css"> <!--{main}--> <script src="/src/js/app.js"></script> <script src="/src/js/controllers.js"></script> <script src="/src/js/directives.js"></script> <script src="/src/js/filters.js"></script> <script src="/src/js/routes.js"></script> <script src="/src/js/services.js"></script> <!--{endmain}--> </head> <body> <div class="navbar navbar-default"> <div class="container-fluid"> <div class="navbar-header"> <a href="#/" class="navbar-brand">Question Maker</a> </div> </div> </div> <div class="maincontent container-fluid" ui-view> </div> </body> </html>
入口文件app.js
有了入口頁面,還得有一個js的啟動入口,就是這個app.js了,在這里它只做了兩件事情:
1. 啟動angular,代碼只有一行:
var app = angular.module('QMaker', ['ui.router']);
我們擁有了一個名為app的全局模塊。這里把ui.router給注入了,因為我們整個應用都用ui-router來做路由,后面會做詳細介紹。
2. 把ui-router的$state和$stateParams服務掛到$rootScope上,這樣我們在后面所有的模塊中,都能夠訪問到路由參數,不必在每個地方都注入一次了。代碼也是相當簡單:
app.run(function($rootScope, $state, $stateParams) { $rootScope.$state = $state; $rootScope.$stateParams = $stateParams; });
控制器合集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的指令,然后在所有需要用分頁的地方就可以調用了,代碼如下:
<pagenav pageobj="pageObject" pagefunc="pageFunction"></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賦予名字,就可以加載各自對應的模板,這里就是多視圖的應用。代碼片段如下:
<!--試題編輯視圖--> <div ui-view="editArea"></div> <!--試題預覽視圖--> <div ui-view="previewArea"></div>
在試卷預覽頁面,我們也需要對試題進行展示,只需在頁面上在定義一個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進行前端架構的路才剛剛開始。
文章列表
全站熱搜