首先說明我開發web的狀況:javascript
因爲每一個人狀況不一樣,選擇技術及方案的重點也不一樣,因此內容僅供參考。對於我來講,這一套不論開發效率仍是開發感覺都是很好的。php
我選用的是idea Ultimate,它除了超強的java編輯功能以外,還提供了功能完善的play1和play2插件,angularjs插件,javascript/css編輯功能,讓人開發時事半功倍。這裏截幾個圖說明一下:css
須要注意的是,上面提示的各功能,基本上都是idea ultimate才提供的(idea社區版中基本上沒有上述功能)。若是你經濟能力足夠而且喜歡idea,不妨購買license對它進行支持;不然的話,自行google解決。html
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後自動觸發的):
參考我寫的另外一個日誌: http://freewind.me/blog/20120728/965.html
曾經有一段時間,我對前端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的使用方式。先看一段代碼:
在這段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呢?
最開始的時候,我想採用單頁面的方式來作,按照前面所說的流程。可是很快遇到了很多麻煩:
這幾個問題我想了好久也沒好辦法,甚至打算放棄angularjs,仍是採用之前徹底由服務端生成頁面的方式來作。好在最後改變了思路,「 之後端框架爲主,前端爲輔 」,找到了比較好的辦法:
這樣就把先後端的優勢結合起來了:前端專一於頁面顯示及效果,後端處理其它的瑣事。不追求無刷新,而追求開發效率與寫代碼時的溫馨性。
這種方式更注重後端的功能,對前端的美工要求較低。與前面單頁面程序的開發正好相反。我以爲應該根據程序的狀況採用不一樣的方式,不要硬套。
在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’
有時候須要用一些第三方插件,好比datepicker,slider,或者tree等。之前的作法是直接經過jquery取得某個元素,而後調用某個方法便可。但在angularjs中,不能直接這麼寫,必須寫在directive中。
有一個叫augular-ui的項目: https://github.com/angular-ui/angular-ui ,已經集成了一些經常使用的插件(來自jqueryui),很方便。但若是仍是須要本身定義,該怎麼作呢?
基本的思路就是,建立一個directive,把調用jquery插件的代碼放在它裏面。這裏以jqueryui的slider爲例:
樣子如上,其示例在: 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 ,能夠直接感覺。
最後須要補充的兩點:
Bootstrap是像咱們這樣缺乏藝術細胞的程序員的福音。只須要記一些css class和某些js component的用法,就能夠作出看起來比較美觀、專業的頁面效果出來。雖然過不了多久,人們也許就會對它出現審美疲勞,但好在它的社區已經造成,已經有很多基於它的模板出來,相信之後會有更多更美觀的bootstrap theme可供選擇: