我的web开发最强组合:Play1+angularjs+bootstrap ++ (idea + livereload)

时间 2012-12-26 20:57:26  Freewind.me原文  http://freewind.me/blog/20121226/1167.html

首先说明我开发web的情况:

  • 个人开发
  • 前后端全部自己搞定
  • 网站类型多为传统多页面程序
  • 注重开发效率
  • Javascritp能力不强
  • 美术细胞很少

由于每个人情况不同,选择技术及方案的重点也不同,所以内容仅供参考。对于我来说,这一套不论开发效率还是开发感受都是很好的。

编辑器

我选用的是idea Ultimate,它除了超强的java编辑功能之外,还提供了功能完善的play1和play2插件,angularjs插件,javascript/css编辑功能,让人开发时事半功倍。这里截几个图说明一下:

Play1支持  
模板标签的提示与补全
href属性中,可提示controller及action
routes文件中的提示
html中play标签的高亮和格式化
从action中快速跳转到view
点击红框后自动打开: 
Play2支持 因为我不使用play2,所以未实际测试过。不过play2支持作为idea12的卖点之一,其支持程度应该不会低于play1。详情可参看idea官网上的介绍。
对于play2中scala模板的支持应该是很多人想要的
Angularjs支持  
html属性提示
Bootstrap支持 idea没有对bootstrap有特别的支持,不过因为它本身对css的支持比较好,所以也可以方便的得到提示
class提示
Javascript支持 idea ultimate对javascript的支持非常强大,是我用过的编辑器中,对js支持最好的。不论是js文件,还是在html中嵌入的js代码,高亮、格式化、查错、提示等功能,都是一流
css支持 idea ultimate对css的支持也是非常强大

需要注意的是,上面提示的各功能,基本上都是idea ultimate才提供的(idea社区版中基本上没有上述功能)。如果你经济能力足够并且喜欢idea,不妨购买license对它进行支持;否则的话,自行google解决。

Live Reload

LiveReload是指当我们修改了项目中的文件时,浏览器会自动刷新,显示修改之后的效果。

在开发网站时,这个功能非常有用。想想你面前开着两个显示器,中间这个是编辑器,旁边的那个是浏览器。每当修改了java/html/css/js代码时,手不离键盘、光标不离编辑区,浏览器就自动刷新了!只需要眼睛轻轻一瞟,脖子都不用动,就能看到修改之后的效果,这种感觉何等舒畅!钛合金的F5都不需要了!

这种方式对于web框架有要求,首先是修改文件后不需要重启服务器。像纯html/php/rails都天生支持,java中某些框架支持,而play1的支持相当优秀。不论修改html/css/javascript,还是java源代码,甚至是配置文件,都不用重启,直接刷新浏览器了。刷新时间大约为1秒到4秒。

另一点是:因为livereload检查到文件修改后,只会触发浏览器刷新一次,所以要保证一次刷新就可以看到修改后的效果。某些框架利用tomcat/jetty的自动重启,无法很好的配合livereload。因为还没有重启完时,livereload就刷新了,取得的可能还是修改前的页面,必须手动刷新多次才能确定看到的是修改后的效果。对于这种情况,livereload几乎没用。而play在刷新过程中,会阻塞http请求,可以保证一次刷新就拿到修改之后的页面。

Livereload的官网是 http://livereload.com ,它支持mac/linux/windows,同时还有chrome/firefox的浏览器插件。它对windows的支持比较差,很容易崩溃,而且是收费的。所以我们只需要用它的浏览器插件就可以了(免费的),然后再找一个免费的替代器换掉服务器端。

我选择的是: https://github.com/lepture/python-livereload ,它是一个python程序,以命令行方式启动,可以跟livereload的浏览器插件通信,效果不错。注意最好从github中下载源代码安装,因为通过pip或easy_install安装的版本有点旧,使用过程中有问题。(不清楚现在是否已经更新)

使用如下:

cd myproject

cd app

livereload

然后启用浏览器的livereload插件即可。

来段gif演示一下(注意每次刷新都是我按了ctrl+s后自动触发的):

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的使用方式。先看一段代码:

在这段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为例:

样子如上,其示例在: 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可供选择:

Play1+angularjs+bootstrap ++ (idea + livereload)的更多相关文章

  1. AngularJS Bootstrap

    AngularJS 的首选样式表是 Bootstrap. 可以在 AngularJS 应用中加入 Twitter Bootstrap,你可以在你的 <head>元素中添加如下代码: < ...

  2. AngularJS -- Bootstrap(启动器)(转载)

    AngularJS -- Bootstrap(启动器)   点击查看AngularJS系列目录 转载请注明出处:http://www.cnblogs.com/leosx/ Bootstrap(初始化) ...

  3. AngularJS进阶(六)AngularJS+BootStrap实现弹出对话框

    AngularJS+BootStrap实现弹出对话框 参考资料: http://angular-ui.github.io/bootstrap/#/modal https://www.zybuluo.c ...

  4. 【17】AngularJS Bootstrap

    AngularJS Bootstrap AngularJS 的首选样式表是 Twitter Bootstrap, Twitter Bootstrap 是目前最受欢迎的前端框架. Bootstrap 你 ...

  5. Salesforce + AngularJS + Bootstrap

    也可以分成三步: 1. 添加css和js引用: <apex:stylesheet value="https://maxcdn.bootstrapcdn.com/bootstrap/3. ...

  6. 前端MVC学习总结(四)——NodeJS+MongoDB+AngularJS+Bootstrap书店示例

    这章的目的是为了把前面所学习的内容整合一下,这个示例完成一个简单图书管理模块,因为中间需要使用到Bootstrap这里先介绍Bootstrap. 示例名称:天狗书店 功能:完成前后端分离的图书管理功能 ...

  7. AngularJs+bootstrap搭载前台框架——准备工作

    1.关于什么是AngularJs以及什么是bootstrap我就不多说了,简单说下,AngularJs是一个比较强大前台MVC框架,bootstrap是Twitter推出的一个用于前端开发的开源工具包 ...

  8. 前端MVC学习笔记(四)——NodeJS+MongoDB+AngularJS+Bootstrap书店示例

    这章的目的是为了把前面所学习的内容整合一下,这个示例完成一个简单图书管理模块,因为中间需要使用到Bootstrap这里先介绍Bootstrap. 示例名称:天狗书店 功能:完成前后端分离的图书管理功能 ...

  9. AngularJS -- Bootstrap(启动器)

    点击查看AngularJS系列目录 转载请注明出处:http://www.cnblogs.com/leosx/ Bootstrap(初始化) 这章介绍了Angular的初始化过程,以及如何在必要的时候 ...

随机推荐

  1. .net网站发布到局域网流程

    将.net网站发布到局域网的服务器上,会遇到一些版本问题,下面把发布的流程简单说一下 一:发布网站 1.首先把需要的引用程序集都重新生成一下 2.程序集都重新生成之后,右击网站项目,选择发布选项 3. ...

  2. 数据库MySQL基本语法思维导图

  3. IIS 7.0 部署MVC

    开发的MVC 3.0 项目,在部署服务上还是与需要花一点功夫,这里把遇到的问题罗列出来. 本文主要介绍IIS 7.5中安装配置MVC 3.0的具体办法! 部署必备: Microsoft .net Fr ...

  4. ASP.NET中控件命名规则

    控件名 简写 控件名 简写 Web 窗体 Label lbl TextBox tb Button btn LinkButton lb HyperLink hl Repeator rpt ImageBu ...

  5. 作品-系统-[原生开发]新蓝景CRM管理系统

    基于ThinkPHP开发 项目网址:http://www.xljchina.com.cn:8839/Admin/Login/login.html 开发性质:原生开发 系统类型:CRM

  6. A New Beginning

    不知不觉中,接触前端已经快两个月了,从一开始的懵懂无知,到现在的--依旧不是很懂,似乎浪费了很多时间,一直都有记纸质笔记.写总结的习惯,写满一本又换一本,却在不知不觉中忽略了自己的实践能力,花费了太多 ...

  7. Thunderbird扩展

    FromToGMAP 1.4 Mail Redirect 0.7.8 MinisizeToTray revived 1.1.2 Nightly Tester Tools 3.7 Thunderbrid ...

  8. LeetCode 169. Majority Element

    Given an array of size n, find the majority element. The majority element is the element that appear ...

  9. adb 命令集合

    1. adb shell 2. adb version 查看 adb 安装版本 3. adb start-server 启动服务 4. adb kill-server 杀死服务 5. adb get- ...

  10. Winmail.dat(TNEF) issue on outlook

    http://www.dwheeler.com/essays/microsoft-outlook-tnef.html