介绍javascript MVC框架:ember框架的基本概念
来源:互联网 发布:大兴区区长辞职 知乎 编辑:程序博客网 时间:2024/05/31 19:44
write by yinmingjun,引用请注明。
在看过knockout和angular之后,有些意犹未尽,感觉在web领域内对SPA的探索不应该止步于此,于是开始翻看ember.js框架,希望ember.js能给我带来惊喜。ember.js在web上的资源较少,官方文档覆盖度和深度不够,很多细节需要到代码中寻找答案。不过回归到ember.js框架本身,这个框架的确带给我很多惊喜,让我会有写文字介绍它的欲望。本文只从框架和概念的角度来解读ember.js,对于ember.js各个部分的深入研究会写在后续的文章之中。
在最初看ember的核心概念解释的时候,看到router这个词这让我有些诧异,难道。。。。。。
在进一步了解ember之后,证实了我的怀疑:ember确实是通过router作为核心概念来组织其MVC体系架构的。这种方式与目前在服务器端流行的一些MVC框架(ASP.NET MVC、Django或express等)的处理方式相似,当router的概念出现在面向客户端的ember之中的时候,我们基本上可以猜测出来,ember已经将页面间的状态迁移纳入自己的问题领域。
此外,ember详细的规划了model、view和controller的职责划分,并支持双向的数据绑定。
ember的另外一个让我意外的地方是ember.data。客户端的js框架很少会涉足实体关系的领域,笔者曾经在所在的公司开做过类似的JS实体关系映射体系,深知其复杂度和价值。ember将实体的定义和实体关系纳入其问题领域,是众多JS开发者的福音,ember.data有助于我们从数据管理的细节中解放出来。不过ember.data不是本文的重点,后面笔者会写专文来介绍它,本文只是简单的介绍其在ember.js体系中的角色。
在读到这里的时候,大家会不会有这样的感觉:ember会不会太重了?ember的js尺寸(v1.0.0版本)压缩过的有200K,未压缩过的有800K,的确惊人,还不包括ember.data(v0.13版本,250K,70K尺寸)和handlebar的js尺寸。这些讯息可以给我们一些启示,ember是面向未来的、重型的web应用支撑框架,使用的时候需要良好的设计和规划,如果不清楚这个定位也许会导致ember使用上的失败经历,这点请大家明白。
接下来进入正题,我们会逐步介绍ember里面的核心概念,了解ember的应用程序的架构。
一、从DEMO开始
下面是来自ember官网的一个DEMO,我们看一下代码。
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ember Starter Kit</title>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
<ul> {{#each item in model}}
<li>{{item}}</li>
{{/each}}
</ul>
</script>
<script src="js/libs/jquery-1.9.1.js"></script>
<script src="js/libs/handlebars-1.0.0-rc.4.js"></script>
<script src="js/libs/ember-1.0.0-rc.5.js"></script>
<script src="js/app.js"></script>
</body>
</html>
app.js:
App = Ember.Application.create();
App.Router.map( function() {
// put your routes here
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return ['red', 'yellow', 'blue'];
}
});
例子很简单,只是创建一个ember的appliation,然后将model中的数据依次的绑定到li之中。不过应用架构的角度来看,这个例子覆盖了ember中的主要的功能。需要注意,script标记的类型是text/x-handlebars。Ember在加载页面时,会抓取这些内容。
在这个例子中,我们基本上可以看到ember应用架构模式的主要脉络。
1、默认的'/'到App.IndexRoute的映射
省略的等效代码:
App.Router.map( function() { this.resource( 'index', { path: '/' } ); });
2、默认的创建IndexRoute的controller
省略的代码:
App.IndexRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('content', ['red', 'yellow', 'blue']);
}
});
3、默认的application template
省略了applicaion的template名称:
<script type="text/x-handlebars" data-template-name="application">
<h1>Application Template</h1>
{{outlet}}
</script>
4、App.IndexRoute对名字为'index'的template的引用
5、程序启动后对{{outlet}}和'index'的template的绑定
这种name mapping的方式可以大幅度减少配置的工作量,在现代的MVC体系中广泛采用,ember也引入了这种处理方式。后面我们会详细的阐述ember的name mapping的方式。
实际上,如果最简单的创建一个ember应用,仅需要一行代码:
App = Ember.Application.create({});
而ember会在后面默认的加上下面的代码:
// Create the application namespace
App = Ember.Application.create({});
// Create the global router to manage page state via URLs
App.Router.map( function() {});
// Create the default application route to set application-level state properties
App.ApplicationRoute = Ember.Route.extend({});
// Create the default application template
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
从这个demo中,我们能看到Application,Router,Template,Controller,Model等概念,这些概念我们会在下一节简单的阐述。
二、ember的基本概念
为了容易理解,这几个概念会尽可能简单的描述,尽量不引入复杂的因素。
1、application的概念
在ember中,创建applicaion非常的简单,只需要一行代码:
window.App = Ember.Application.create();
App的变量名字是什么都可以,创建后的app中可以作为应用级别状态和数据的载体。例如,创建了一个App中的view,可以这么写:
App.MyView = Ember.View.extend();
默认的情况下,调用Ember.Application.create()方法会自动的触发Ember.Application.initialize()方法的调用,也可以通过配置来调整application的默认的行为。
在调用Ember.Application.create()方法(注1)的时候,可以传递一个object进去,以配置&调整应用创建的默认的行为。一般会在传递给create方法的中的obect中指定rootElement、ready事件的handler等。
注1:说明一下,create方法是ember的对象体系中的基础服务
2、router的概念
router是Ember.Router类的实例,通过application实例的Router成员访问。
在router中,有两个概念,一个是resource,代表资源定位;另外一个是route,表示特定的页面路由分发;
从简单的开始,先看看如何添加route:
App.Router.map(function() {
this.route("about", { path: "/about" });
this.route("favorites", { path: "/favs" });
});
这段代码实际上映射了以下的对应关系:
URL Route Name Controller Route Template
/ index IndexConroller IndexRoute index
/about about AboutController AboutRoute about
/favs favorites FavoritesController FavoritesRoute favorites
其中,index的映射是默认的。
再看看resource是怎么维护的:
App.Router.map(function() {
this.resource('posts', { path: '/posts' }, function() {
this.route('new');
});
});
其实如果resource的名字和path的名字是相同的,可以省略path部分的参数描述,下面是更简单的写法:
App.Router.map(function() {
this.resource('posts', function() {
this.route('new');
});
});
上面的代码实际上会产生下面的映射关系:
URL Route Name Controller Route Template
/ index IndexConroller IndexRoute index
N/A posts PostsController PostsRoute posts
/posts posts.index PostsController PostsRoute posts
->PostsIndexController ->PostsIndexRoute ->posts/index
/posts/new posts.new PostsController PostsRoute posts
->postsNewController ->PostsNewController ->posts/new
route和resource两个概念,route用于处理具体的URL参数,resource用于做资源的重新定位。使用resource有助于将应用分解成多个小的区域,每个区域独立的处理页面的状态迁移,这种结构是对大项目的任务分解和团队协作有利的。
3、controller的概念
在ember中,controller是template和model之间的桥梁,将model中的数据转换成面向template的显示的数据。总的来说,model是面向server的,而controller是面向template的,拥有template需要显示的数据,也包含template需要执行的操作。
ember的controller和ViewModel的概念有些相似。
如果一个controller是一个ArrayController,就可以在template中使用{{#each controller}}的语法。
在application层面上也存在controller,不过ember会给application提供默认的实例。如果需要处理application层面上的数据绑定和响应用户操作,可以这么做:
HTML:
<!-- application.handlebars -->
<header>
{{view Ember.TextField valueBinding="search" action="query"}}
</header>
{{outlet}}
JS:
App.ApplicationController = Ember.Controller.extend({
// the initial value of the `search` property
search: '',
query: function() {
// the current value of the text field
var query = this.get('search');
this.transitionToRoute('search', { query: query });
}
});
上门的代码为application提供了一个ApplicationController,响应application层面上的search数据请求和query请求操作。
注2:extend方法是ember对象体系提供的基础服务
4、model的概念
在ember中,model的概念模糊而抽象,不容易理解。ember的model是纯数据的载体,从简单的层面上来看,model就是一个JSON的数据结构;从ember框架定义的角度上看,model对应后端的server上的数据,是实体和实体关系在客户端的表达。为了更好的处理数据方面的数据管理的需求,ember.js专门开发了ember.data来处理更高层次的数据服务。
在ember的应用体系中,model的初值来自提供给route的model方法的返回值,并通过route的deserialize方法将获取到的model数据填充到route的currentModel成员之中(currentModel可以理解为model的快照),而在route对数据的维护上来看currentModel是context最初的数据来源和最终的数据载体,某种意义上的等价物(特点的时间点上等价),而route的currentModel(或其context)成员最终会作为controller的content成员投放到controller之中。conroller中,model成员是其content的别名。
其同步的顺序如下:
route.model
--->route.currentModel && route.context
--->controller.content && controller.model
另外,route的currentModel中数据是数组还是对象,将会决定ember产生的controller的类型,对于array产生Ember.ArrayController的实例;对于object产生Ember.ObjectController的实例;其他会以Ember.Controller作为模版。
5、templat的概念
ember的template是建立在handlebars的基础上的,也就是说,在ember的template中可以使用handlebars支持的语法来书写template。真正属于ember领域的问题是template和数据提供者controller之间的对应关系,其通过template的script标记的data-template-name属性来指定:
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each item in model}}
<li>{{item}}</li>
{{/each}}
</ul>
</script>
上面定义的template和名字是index的controller之间建立了对应关系。
如果需要操纵ArrayController中的内容,可以按下面的方式来书写代码。
HTML:
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each item in model}}
<li>{{item}} {{controller.postFix}}</li>
{{/each}}
</ul>
<button {{action doIncrease}}> doIncrease </button>
</script>
JS:
App.IndexController = Ember.Controller.extend(
{
postFix: 'post',
'content': ['aaa', 'bbb'],
doIncrease: function() {
var content = this.get('content');
content.pushObject('demo');
}
});
ember对属性是通过get、set包装器和观察者模式来管理的,对controller的数据变化,需要通过对应的方法来发布变更通知,这里是ember包装的数组的pushObject方法来触发。ember包装的数组还有其他类似的方法,详细内容参考ember的API文档。
注解:
需要主要的是ArrayController的特殊性,如果route的model中的数据是数组,那么ember的each helper会针对each块内的内容创建子view,并将当前枚举项的内容作为子view的content属性;所在的索引作为view的contentIndex属性。ember的each helper也会针对数组中的每个元素建立一个数据绑定的上下文,并将枚举的元素(这里是item)、controller和view作为子view(就是each块内的模版内容)的数据访问的上下文。如果ArrayController中的数组元素不是Controller的实例,那么上下文中的controller就是父view上下文中的controller(这里是App.IndexController);否则子view的controller是ArrayController的content中的数组元素中的controller。
handlebars的语法和数据定位的方式,我转了一篇文章,被小编屏蔽了,不过web上很多,大家可以自己找找。
6、view的概念
ember的view是基于handlebars的扩展,其背后是有两种推动力:
1、是对特定事件的监听和过滤需要(支持双向的数据绑定的需求);
2、是避免大量重复的书写template中的内容;
接下来,我们还是通过例子来了解ember中view的概念吧。
例子1:
通过view的append方法来将view添加到DOM。
HTML:
<script type="text/x-handlebars" data-template-name="say-hello">
Hello, <b>{{view.name}}</b>
</script>
JS:
App.MyView = Ember.View.create({
templateName: 'say-hello',
name: "YMJ"
});
App.MyView.append();
上面这段代码中,view通过templateName指定了使用的DOM模版,然后在模版中访问view中的数据,然后通过其append方法将view发布到document.body的元素的最后。view还有类似的appendTo方法(通过jquery的appendTo实现),将view发布到指定的位置。
上面是使用view的一种方式。
例子2:
另外一直使用view的方式是在template中使用view。
HTML:
<script type="text/x-handlebars" data-template-name="say-hello">
Hello, <b>{{view.name}}</b>
</script>
<script type="text/x-handlebars" data-template-name="index">
{{view App.MyView}}
<ul>
{{#each item in model}}
<li>{{item}} {{controller.postFix}}</li>
{{/each}}
</ul>
<button {{action doIncrease}}> doIncrease </button>
</script>
JS:
App.MyView = Ember.View.create({
templateName: 'say-hello',
name: "YMJ"
});
上面的JS部分只给出了必要的代码。view和其DOM的template之间的关系还是通过其templateName的成员来指出,关键是在index的template中,我们通过view helper来引入了App.MyView的实例,这样在template中输出了App.MyView的内容。
ember内置了很多view,实际上ember的双向的数据绑定就是基于view的概念实现的。
ember内置的view如下:
> Ember.Checkbox
> Ember.TextField
> Ember.Button
> Ember.TextArea
> Ember.SelectOption
> Ember.Select
详细的API的说明可以参考ember的官方文档,我们这里只看一个小例子,看看如何做数据绑定:
<label>
{{view Ember.CheckboxcheckedBinding="model.isDone"}}
{{model.title}}
</label>
view相关的深入研究,我们在其他文档中来探讨,作为view的使用者,大概了解到上面这些内容,基本上就可以动手写东西了。
三、小结
上面,我们在概念的层面和ember.js做了一个亲密的接触,将ember.js框架中所有的核心概念阐述了一遍,希望读者能对ember.js有一个整体上的认识,知道这是一个什么框架,可以做那些事情。
在接触到的javascript MVC框架中,ember可能是最野心勃勃的一个,从目前我看到的事实是它试图接管客户端的一切,对MVC体系的有全方位的支持,相比angular来说更全面、更复杂,对应小组工作提供更多的概念支持,是架构师比较乐于采用的前端框架。ember.js的文档支持方面,很难让人满意。在线文档的深度和广度都不够,很多东西需要到代码中找答案,ember在文档层面落后于angular等其他框架。
在框架的选择上来看,很难说那个框架更优秀,结合应用领域或问题领域来选择,可能会有更明确的结果。对应大型&复杂的应用,我本人会倾向于选择ember.js,因为我对ember足够的了解,其他人或许应考虑ember的文档方面的不足。
后面会写一些东西,对ember的各个分支领域做相对深入的分析。
- 介绍javascript MVC框架:ember框架的基本概念
- 介绍javascript的ember.js框架的几篇文章
- JavaScript MVC框架PK:Angular、Backbone、CanJS与Ember
- JavaScript MVC框架PK:Angular、Backbone、CanJS与Ember
- JavaScript MVC框架PK:Angular、Backbone、CanJS与Ember
- JavaScript MVC框架PK:Angular、Backbone、CanJS与Ember
- JavaScript MVC框架PK:Angular、Backbone、CanJS与Ember
- JavaScript MVC框架PK:Angular、Backbone、CanJS与Ember
- 介绍javascript MVC框架 ---- angular
- Castle.MVC框架介绍
- spring MVC框架介绍
- MVC框架介绍
- MVC框架介绍
- Spring MVC框架介绍
- Spring MVC - 框架介绍
- MVC框架模式介绍
- spring mvc 框架介绍
- Castle.MVC框架介绍
- 我的2015
- 在ubuntu上安装zeppelin
- Tomcat 7.0安装与配置
- HDU2005
- 类似开关灯问题
- 介绍javascript MVC框架:ember框架的基本概念
- 假期简单算法--快排
- codeforces 588 B Duff in Love
- 深入解读ember.js的对象体系
- 人员角色管理
- 开灯问题
- 栈
- ember.js提供的基础服务介绍
- 解读ember的应用模型