Play1+angularjs+bootstrap ++ (idea + livereload)

個人web開發最強組合:Play1+angularjs+bootstrap ++ (idea + livereload)

首先說明我開發web的狀況:javascript

  • 我的開發
  • 先後端所有本身搞定
  • 網站類型多爲傳統多頁面程序
  • 注重開發效率
  • Javascritp能力不強
  • 美術細胞不多

因爲每一個人狀況不一樣,選擇技術及方案的重點也不一樣,因此內容僅供參考。對於我來講,這一套不論開發效率仍是開發感覺都是很好的。php

編輯器

我選用的是idea Ultimate,它除了超強的java編輯功能以外,還提供了功能完善的play1和play2插件,angularjs插件,javascript/css編輯功能,讓人開發時事半功倍。這裏截幾個圖說明一下:css

 

Play1支持  
模板標籤的提示與補全 image
href屬性中,可提示controller及action image
routes文件中的提示 image
html中play標籤的高亮和格式化 image
從action中快速跳轉到view image
點擊紅框後自動打開: 
image
Play2支持 由於我不使用play2,因此未實際測試過。不過play2支持做爲idea12的賣點之一,其支持程度應該不會低於play1。詳情可參看idea官網上的介紹。
對於play2中scala模板的支持應該是不少人想要的 image
Angularjs支持  
html屬性提示 image
Bootstrap支持 idea沒有對bootstrap有特別的支持,不過由於它自己對css的支持比較好,因此也能夠方便的獲得提示
class提示 image
Javascript支持 idea ultimate對javascript的支持很是強大,是我用過的編輯器中,對js支持最好的。不管是js文件,仍是在html中嵌入的js代碼,高亮、格式化、查錯、提示等功能,都是一流
css支持 idea ultimate對css的支持也是很是強大

須要注意的是,上面提示的各功能,基本上都是idea ultimate才提供的(idea社區版中基本上沒有上述功能)。若是你經濟能力足夠而且喜歡idea,不妨購買license對它進行支持;不然的話,自行google解決。html

Live Reload

LiveReload是指當咱們修改了項目中的文件時,瀏覽器會自動刷新,顯示修改以後的效果。前端

在開發網站時,這個功能很是有用。想一想你面前開着兩個顯示器,中間這個是編輯器,旁邊的那個是瀏覽器。每當修改了java/html/css/js代碼時,手不離鍵盤、光標不離編輯區,瀏覽器就自動刷新了!只須要眼睛輕輕一瞟,脖子都不用動,就能看到修改以後的效果,這種感受何等舒暢!鈦合金的F5都不須要了!java

這種方式對於web框架有要求,首先是修改文件後不須要重啓服務器。像純html/php/rails都天生支持,java中某些框架支持,而play1的支持至關優秀。不論修改html/css/javascript,仍是java源代碼,甚至是配置文件,都不用重啓,直接刷新瀏覽器了。刷新時間大約爲1秒到4秒。node

另外一點是:由於livereload檢查到文件修改後,只會觸發瀏覽器刷新一次,因此要保證一次刷新就能夠看到修改後的效果。某些框架利用tomcat/jetty的自動重啓,沒法很好的配合livereload。由於尚未重啓完時,livereload就刷新了,取得的可能仍是修改前的頁面,必須手動刷新屢次才能肯定看到的是修改後的效果。對於這種狀況,livereload幾乎沒用。而play在刷新過程當中,會阻塞http請求,能夠保證一次刷新就拿到修改以後的頁面。python

Livereload的官網是 http://livereload.com ,它支持mac/linux/windows,同時還有chrome/firefox的瀏覽器插件。它對windows的支持比較差,很容易崩潰,並且是收費的。因此咱們只須要用它的瀏覽器插件就能夠了(免費的),而後再找一個免費的替代器換掉服務器端。jquery

我選擇的是: https://github.com/lepture/python-livereload ,它是一個python程序,以命令行方式啓動,能夠跟livereload的瀏覽器插件通訊,效果不錯。注意最好從github中下載源代碼安裝,由於經過pip或easy_install安裝的版本有點舊,使用過程當中有問題。(不清楚如今是否已經更新)linux

使用以下:

cd myproject

cd app

livereload

而後啓用瀏覽器的livereload插件便可。

來段gif演示一下(注意每次刷新都是我按了ctrl+s後自動觸發的):

xxx

Why play, and why play1

參考我寫的另外一個日誌: http://freewind.me/blog/20120728/965.html

Why angularjs

曾經有一段時間,我對前端javascript框架很感興趣,試用了不少,好比backbone/knockout/knockback/angularjs/…(以及一大堆已經忘了名字的),其中有兩個讓我印象深入。一個是backbone,一個是angularjs。

Backbone的優勢在於學習成本很低,與jquery的思路接近,偏向於底層及手動控制。採用backbone的項目比較多,資料也比較多,社區也比較大,還有一些基於backbone發展起來的高一級的框架(如 https://github.com/backbone-boilerplate/grunt-bbb )。這個bbb我沒有用過,只是在js羣中有人說使用它以後,開發效率比以前純backbone有很大的提升,因此這裏提一下。對於技術較普通的團隊來講,使用backbone可能會比較保險一些。

而angularjs則是一個讓我感到驚豔的框架,相對於同類無數個mv**框架,它的優點達到了數量級。若是用幾個詞來形容它,應該是:學習成本高,開發效率高,寫代碼時思路流暢。

它擁有雙向綁定、directive、直接改寫html標籤等特性,使用它你能夠對現有的html標籤進行改進和加強,甚至還能夠重寫一套徹底屬於本身的html標籤。也許你會以爲像「雙向綁定」這種爛大街的特性有什麼值得拿出來講的,關鍵在於anguarljs的設計很是統一,各個功能搭配得很流暢、一鼓作氣。這種感受就像是eclipse與idea在操做上的區別:idea雖然有不少功能eclipse也提供了,可是用起來總不像idea中那麼流暢 — 不論編輯什麼類型的文件,在idea中均可以使用很是相似的操做,獲得很是相似的界面反饋。

Angularjs的學習成本比較高,主要緣由是其設計思路與咱們之前寫jquery代碼時有很大的不一樣,不能套用。Angularjs的核心思想就是「複用」,它的「複用」體如今"directive"上。Directive既是angularjs的核心,也是它的重點、難點和殺手級特性。簡單的說,directive能夠是一個自定義的html標籤、或者屬性、或者註釋,它的背後是一些函數,能夠對所在的html標籤進行加強,好比修改html的dom內容,加強它的功能(如用第三方js插件包裝它)。

編寫Directive比較複雜,須要理解它的內部原理才能定義出本身的directive。在掌握它之前,之前一些很簡單的事情可能都沒辦法作,容易讓人沮喪。好比在使用jquery時,常常會這樣操做:

$("#mydiv").dialog();

但這種寫法在使用angularjs的html頁面中,是沒法使用的。你必須把它寫成一個directive(好比ui-dialog),而後在它的postLink()方法中,對傳入的element元素操做:

element.dialog()

若是不理解postLink的各參數以及它是如何被angularjs使用的話,很難寫出來。因此在使用angularjs的前期,很容易被卡住。

在學習angularjs時,必定要細讀官網提供的develop guide (http://docs.angularjs.org/guide/ ),把各章節讀懂,知道angularjs的內部運行原理。千萬別按jquery的方式學習,光看示例是絕對不夠的。

Angularjs的另外一個殺手級特性,就是把流程控制、事件綁定等代碼,直接寫在html標籤上。這其實就是前面所說的directive的使用方式。先看一段代碼:

image

在這段html代碼中,你能夠看到那些位於html標籤上由藍色背影標出來的內容,都是angularjs提供的directive。有的是綁定事件(如ng-click,ng-submit),有的是控制流程(如ng-repeat)。這種方式我很是喜歡,簡單直接,可讀性又很好。固然有人不喜歡這種方式,認爲html就應該乾乾淨淨,應該把這些東西分享到javascript中,就像下面這樣:

$("form").submit(function() { … });

其實對於這種狀況,angularjs也有類似的作法,即爲該form定義一個directive,好比my-add-form,而後把那些邏輯代碼放到它裏面:

<form my-add-form>…</form>

// js code

module.directive("myAddForm", function() {

    // the logic

});

不過這種方式對於一個不那麼通用的邏輯來講有點重。因此咱們一般仍是採用在html標籤上寫控制,在controller中寫邏輯的方式來作,經過合理的分配,在可讀性與方便性之間取得平衡。

直接在標籤上綁定事情處理函數,能夠減小大量的命名,減小無謂代碼,並且閱讀起來更直觀。想當初看backbone代碼時,發現有二分之一的代碼,都是通地css selector來獲取元素,再將其某個事件與某個函數綁定起來。這樣的代碼一旦寫完,html就不敢隨便動了。由於若更改了html結構、id或者css class,這邊的js代碼均可能沒法正常執行。

熟悉angularjs之後,會發現實現前端效果時,開發效率很高。在寫html的同時,基本上就能夠把大部分的交互效果寫出來。同時,angularjs以model爲中心,在編碼時只須要考慮model。當改變了model的內容時,view就會自動更新,這可讓咱們須要關注的東西更少。使用了angularjs後,你會發現html標籤的表現力變強了,之前須要一些js插件實現的功能(好比簡單的tab、tree等),使用angularjs幾行代碼就能夠實現,並且全部的東西都是可定製的。

好比一個tab:

<div>

<a ng-click="tab=1">Tab1</a>

<a ng-click="tab=2">Tab2</a>

<a ng-click="tab=3">Tab3</a>

</div>

<div ng-show="tab==1">This is tab1</div>

<div ng-show="tab==2">This is tab2</div>

<div ng-show="tab==3">This is tab3</div>

好比一個tree

<script type="ng/template" id="’node.html’">

{{node.name}}

<ul>

<li ng-repeat="node in node.children" ng-include="’node.html’"></li>

</ul>

</script>

<ul>

<li ng-repeat="node in rootNodes" ng-include="’node.html’"></li>

</ul>

我在作某個網站後臺時,開始打算用angularjs,後來感受功能比較簡單,就直接採用jquery,少引用一個庫。開始還好,很快就發現只要增長一點複雜的功能時,所花的時間就大大增長。若是一樣的功能使用angularjs來寫,會很是簡單。

單頁面程序以及先後端交互

通常認爲前端mv**框架適合於單頁面程序,無刷新、局部更新的那種。先後端徹底分離,之間以restful api交互,使用json交換數據。在前端作好router,當點擊了某個按鈕須要展現新內容時,直接由前端獲取並顯示另外一個局部html頁面,同時調用某個restful api獲取json數據填充。這種程序,一般前端的功能比較複雜,而對後端要求較少。採用這類mv**框架,前端程序員們能夠充分發揮本身的才智,徹底使用javascript/css/html來實現功能。而對於後臺,只需知道restful api接口便可。這是前端mv**推薦的方式,也是目前來講比較好的方式。其特色是「 之前端js框架爲主,後端爲輔 」。

Anguarljs對於這種方式,有着很是好的支持。它除了提供前端router外,還提供了一些與後臺交互的service,如與ajax相關的$http,與restful相關的$resource;對於cookie與本地存儲支持也很好,基本上使用angularjs就能夠把程序作完。後臺可使用各類語言、各類框架來提供restful api。好比,我嘗試過couchdb這個數據庫,它直接在數據庫層面提供了restful api做爲外界操做數據庫的接口,angularjs與它配合起來,連服務端程序都不用了。

在開發android程序時,我也嘗試過將phonegap與angularjs結合起來,直接使用angularjs來實現程序。與後臺之間經過restful api交互。最後雖然由於性能要求改用了android原生方式,但對於普通的安卓或ios應用來講,這種方式是一種很好的選擇,開發效率很高。

傳統多頁面程序

對於我來講,大部分的網站仍是傳統多頁面的。好比一個信息管理系統的後臺。這種狀況下可否使用angularjs呢?

最開始的時候,我想採用單頁面的方式來作,按照前面所說的流程。可是很快遇到了很多麻煩:

  1. 頁面跳轉:這種網站頁面不少,在前端定義比較麻煩,由於須要寫長長的url,容易出錯,檢查也不方便。而使用play的模板引擎,能夠直接寫@{Controller.action(params)},play會自動把它變爲url,而且會檢查是否有拼寫錯誤,很是方便。
  2. 權限:頁面上某些按鈕的顯示與隱藏,取決於當前用戶的角色。因此每次顯示某個新頁面時,都須要後臺傳過來一些json數據,例如:{ buttons: [{show: true}, {edit: false}]},來告訴前臺顯示或隱藏哪些按鈕。有的時候這個操做很是繁瑣。
  3. 屢次請求:當顯示一個新頁面時,可能須要屢次請求。首先html模板一次,而後取json數據一次(或屢次)。這樣給人的感受就有點慢。雖然可經過一些手段(如緩存html模板,爲每個請求返回一個大的合併過的json數據),但因爲模板顯示的時間與取得json數據的時間之間總有一些間隔,有時候仍是會讓人以爲不太流暢,卡。

這幾個問題我想了好久也沒好辦法,甚至打算放棄angularjs,仍是採用之前徹底由服務端生成頁面的方式來作。好在最後改變了思路,「 之後端框架爲主,前端爲輔 」,找到了比較好的辦法:

  1. 後端能作的事情儘可能在後端作,好比頁面跳轉,模板引擎,權限處理等,顯而後端有優點
  2. 由於在angularjs中,各數據都要以model的方式由angularjs管理,因此在html中,將原來直接顯示的數據以json方式嵌入,賦給angularjs。這樣打開一個新頁面時,angularjs所需的所有數據都有了,不須要再次請求。
  3. 後端不提供restful接口,相反生成一個js文件,把各action以函數的方式暴露出來,讓angularjs直接調用,而不須要寫長長的url。既方便又不容易出錯。
  4. 前端徹底不使用router,仍是按照傳統的方式跳轉,整頁刷新

這樣就把先後端的優勢結合起來了:前端專一於頁面顯示及效果,後端處理其它的瑣事。不追求無刷新,而追求開發效率與寫代碼時的溫馨性。

這種方式更注重後端的功能,對前端的美工要求較低。與前面單頁面程序的開發正好相反。我以爲應該根據程序的狀況採用不一樣的方式,不要硬套。

關於angularjs的$resource

在angularjs中提供了一個service叫$resource:http://docs.angularjs.org/api/ngResource.$resource ,它能夠經過一個url和一些參數,定義一個resouce對象。經過調用該對象的某些方法,能夠與後臺交互,而且直接獲得通過它處理以後的數據。使用的感受有點像咱們在後端經常使用的dao。

這個$resource服務對於「單頁面,以restful api交互」的狀況比較合適。它要求所給出來的url能夠按restful api的方式調用,正好知足適合這種狀況。

但對於「傳統多頁面程序」很差用,特別是那種信息管理系統。由於它們的url形式並不重要,是否restful也不重要,只要提供get/post兩種方式,能把參數傳過去就好了。若是在這裏使用$resource,按它的規定來套,須要花不少心思來設計url,很是痛苦。我在這裏卡了很長時間,由於我想不明白,爲何這個$resource看起來很好,但用起來就是不對勁呢。最後終於想通,原來個人狀況不須要restful api。

最後個人作法是,在服務端把各action收集起來,生成一個js文件,在這個文件裏把action以js函數的方式暴露出來,供angularjs直接調用(內部使用了angularjs提供的$http服務,而沒有用$resource)。在本例中,這個js文件中定義了一個叫JsRoutes的object供使用。

Angularjs調用它的方式是這樣的:

function Ctrl($scope, JsRoutes) {

    $scope.submit = function() {

        JsRoutes.Users.create.post({

              username: username,

              password: password

        }, function(res) {

              alert("ok");

        });

    }

}

這樣,當我須要向後臺傳user相關的數據時,直接調用預約義的JsRoutes.Users.create便可,而不須要關注它對應的url究竟是什麼。

這個js文件的代碼是這樣的:

angular.module('JsRoutes', []).factory('JsRoutes', function ($http) { var defaultErrorHandler = function (data, status, headers, config) { alert('Sorry, server responses ' + status + ' error: ' + data); }; // angular post json by default, change it to key value pairs var keyValuesTransformFn = function (d) { return jQuery.param(d); }; var commonHandler = function (method, url, params, data, success, error, config) { config = config || {}; config.method = config.method || method; config.params = config.params || params; config.data = config.data || data; config.url = config.url || url; config.timeout = config.timeout || 120 * 1000; var postType = config.postType || 'form'; if (postType === 'form') { config.transformRequest = keyValuesTransformFn; config.headers = config.headers || {}; config.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'; // config.headers['Accept'] = "application/json, text/html, text/plain, */*"; } $http(config).success(success).error(error || defaultErrorHandler); }; var jsRoutesHandler = function (path) { return { get: function (params, success, error, config) { commonHandler('get', path, params, {}, success, error, config); }, post: function (data, success, error, config) { commonHandler('post', path, {}, data, success, error, config); } } }; return { Application: { "index": jsRoutesHandler('/Application/index'), "toAction": jsRoutesHandler('/Application/toAction'), "showViewSource": jsRoutesHandler('/Application/showViewSource') }, Admins: { "index": jsRoutesHandler('/Admins/index') } }; });

採用這種方式後,在前端js代碼中徹底不須要跟url打交道,由於它們都是在服務器端根據routes文件生成的。之後url有什麼變化,js這裏不須要修改一行代碼。

另外須要注意的是,angularjs默認會向服務端發送json格式的數據,而play對key-value形式的數據處理的比較好,因此我就把它默認值改成了’application/x-www-form-urlencoded’

Angularjs中如何集成第三方js插件

有時候須要用一些第三方插件,好比datepicker,slider,或者tree等。之前的作法是直接經過jquery取得某個元素,而後調用某個方法便可。但在angularjs中,不能直接這麼寫,必須寫在directive中。

有一個叫augular-ui的項目: https://github.com/angular-ui/angular-ui ,已經集成了一些經常使用的插件(來自jqueryui),很方便。但若是仍是須要本身定義,該怎麼作呢?

基本的思路就是,建立一個directive,把調用jquery插件的代碼放在它裏面。這裏以jqueryui的slider爲例:

image

樣子如上,其示例在: http://jqueryui.com/slider/

在jquery中,它的調用方式是這樣的:

<div id="slider"></div>

$("#slider" ).slider({

    min: 0, 
    max: 100,

    value: 10,

    step: 5    
});

 

而在angularjs中,咱們須要定義一個directive,假設是ui-slider:

app.directive('slider', function () { return { require: '?ngModel', restrict: 'A', link: function (scope, element, attrs, ngModel) { var opts; opts = angular.extend({}, scope.$eval(attrs.slider)); var slider = element.slider({ min: opts.min || 0, max: opts.max || 100, step: opts.step || 10, value: attrs.ngModel && scope.$eval(attrs.ngModel) || 50, slide: function (event, ui) { if (ngModel) { scope.$apply(function () { ngModel.$setViewValue(ui.value); }) } } }); scope.$watch(attrs.ngModel, function (v) { slider.slider({ value: v }); }); } }; });

在html中的調用方式是:

<div slider="{min:0,max:500,step:5}" ng-model="row.width"></div>

在directive中的代碼比jquery的多了很多,不過主要是增長了與雙向綁定有關的兩個函數。例如在"slide: function(event, ui)「中,是把sider的值傳回給某個model(本例中爲row.width)。在後面的scope.$watch中,是把model的值傳給slider。這樣當拖動slider上的刻度時,row.witdh的值會自動改變;或者改變了row.width的值以後,slider的刻度也會自動變化。

基本的思路就是這樣,更詳細的須要看相關文檔。與angularjs相關的就講到這裏,在這裏你能夠看到不少angularjs的可執行的例子:https://github.com/angular/angular.js/wiki/JsFiddle-Examples ,能夠直接感覺。

最後須要補充的兩點:

  1. Angularjs的社區氣氛很好。其google group人氣很旺,有問題很快就能獲得詳細的回覆
  2. Angularjs對IE6/7支持很差。若是必定要支持ie6/7,可考慮backbone或其它框架。

Bootstrap

Bootstrap是像咱們這樣缺乏藝術細胞的程序員的福音。只須要記一些css class和某些js component的用法,就能夠作出看起來比較美觀、專業的頁面效果出來。雖然過不了多久,人們也許就會對它出現審美疲勞,但好在它的社區已經造成,已經有很多基於它的模板出來,相信之後會有更多更美觀的bootstrap theme可供選擇:

相關文章
相關標籤/搜索