原文:http://www.sencha.com/learn/architecting-your-app-in-ext-js-4-part-2/

在《ExtJS应用架构设计》一文,我们探讨了如何使用ExtJS构建一个潘多拉风格的应用程序。我们采用了MVC架构,并将它应用到一个比较复杂的用户界面,应用中带有多个视图和模型。在这篇文章中,我们将在架构的基础上继续探讨控制和模型的设计与代码问题,并开始使用Ext.application和Viewprot类。

现在,让我们开始编写应用。

定义应用

在ExtJS 3,Ext.onReady方法是应用程序和开发人员开始编写应用架构的入口。在ExtJS 4,我们推出了类似MVC模式,该模式可以让你在创建应用程序时遵循最佳做法。

新的MVC包要求使用Ext.application方法作为入口,该方法将创建一个Ext.app.Application实例,并在页面准备好以后触发launch方法。它取代了在Ext.onReady内添加诸如创建Viewport和设置命名空间等功能的这种写法。

app/Application.js

  1. Ext.application({
  2. name: 'Panda',
  3. autoCreateViewport: true,
  4. launch: function() {
  5. // This is fired as soon as the page is ready
  6. }
  7. });

配置项name将会创建一个命名空间。所有视图、模型和Store和控制器都会以该命名空间为命名。设置autoCreateViewport为true,框架将字段加载app/view/Viewport.js文件。在该文件内,将会定义一个名称为Panda.view.Viewport的类,类名必须使用应用中name指定的命名空间。

The Viewport class

在UI中的视图,都是独立的部件,因而需要使用Viewport将它们粘合起来。它会加载要求的视图及它们的定义需要的配置项,以实现应用的整体布局。我们发现,通过定义视图并将它们加载到viewprot,是创建UI基本结构最快的方法。

重要的是,这个过程的重点在搭建视图,而不是单个视图本身,就如雕刻一样,先开始创建视图的粗糙形状,然后细化它们。

创建构造块

利用前文中的工作,我们现在可以开始定义视图了。

app/view/NewStation.js

  1. Ext.define('Panda.view.NewStation', {
  2. extend: 'Ext.form.field.ComboBox',
  3. alias: 'widget.newstation',
  4. store: 'SearchResults',
  5. ... more configuration ...
  6. });

app/view/SongControls.js

  1. Ext.define('Panda.view.SongControls', {
  2. extend: 'Ext.Container',
  3. alias: 'widget.songcontrols',
  4. ... more configuration ...
  5. });

app/view/StationsList

  1. Ext.define('Panda.view.StationsList', {
  2. extend: 'Ext.grid.Panel',
  3. alias: 'widget.stationslist',
  4. store: 'Stations',
  5. ... more configuration ...
  6. });

app/view/RecentlyPlayedScroller.js

  1. Ext.define('Panda.view.RecentlyPlayedScroller', {
  2. extend: 'Ext.view.View',
  3. alias: 'widget.recentlyplayedscroller',
  4. itemTpl: '<div></div>',
  5. store: 'RecentSongs',
  6. ... more configuration ...
  7. });

app/view/SongInfo.js

  1. Ext.define('Panda.view.SongInfo', {
  2. extend: 'Ext.panel.Panel',
  3. alias: 'widget.songinfo',
  4. tpl: '<h1>About </h1><p></p>',
  5. ... more configuration ...
  6. });

我们只是列了一些定义,组件的具体配置将不在本文进行讨论。

在上述配置,可看到使用了三个Store,这些Store的名称都是上一篇文章定义的。现在,我们开始创建Store。

模型和Store

通常,在初始阶段,以包含数据的静的json文件作为服务器端是相当有用。以后,这些静态文件可以作为实际的服务器端动态文件的参考。

在当前应用,要使用Station和Song两个模型,还需要定义使用这两个模型并绑定到数据组件的Store。每个Store都会从服务器端加载数据,模拟的数据文件格式如下:

静态数据

data/songs.json

  1. {
  2. 'success': true,
  3. 'results': [
  4. {
  5. 'name': 'Blues At Sunrise (Live)',
  6. 'artist': 'Stevie Ray Vaughan',
  7. 'album': 'Blues At Sunrise',
  8. 'description': 'Description for Stevie',
  9. 'played_date': '1',
  10. 'station': 1
  11. },
  12. ...
  13. ]
  14. }

data/stations.json

  1. {
  2. 'success': true,
  3. 'results': [
  4. {'id': 1, 'played_date': 4, 'name': 'Led Zeppelin'},
  5. {'id': 2, 'played_date': 3, 'name': 'The Rolling Stones'},
  6. {'id': 3, 'played_date': 2, 'name': 'Daft Punk'}
  7. ]
  8. }

data/searchresults.json

  1. {
  2. 'success': true,
  3. 'results': [
  4. {'id': 1, 'name': 'Led Zeppelin'},
  5. {'id': 2, 'name': 'The Rolling Stones'},
  6. {'id': 3, 'name': 'Daft Punk'},
  7. {'id': 4, 'name': 'John Mayer'},
  8. {'id': 5, 'name': 'Pete Philly & Perquisite'},
  9. {'id': 6, 'name': 'Black Star'},
  10. {'id': 7, 'name': 'Macy Gray'}
  11. ]
  12. }

模型

ExtJS 4中的模型类似ExtJS 3中的记录,它们主要的区别是,可以在模型上定义代理、验证和关联。应用中的模型Song代码如下:

app/model/Song.js

  1. Ext.define('Panda.model.Song', {
  2. extend: 'Ext.data.Model',
  3. fields: ['id', 'name', 'artist', 'album', 'played_date', 'station'],
  4. proxy: {
  5. type: 'ajax',
  6. url: 'data/recentsongs.json',
  7. reader: {
  8. type: 'json',
  9. root: 'results'
  10. }
  11. }
  12. });

可以看到,在模型中定义了代理,这是一个好的方式,这样,就可以直接在模型加载和保存模型实例,而不需要经过Store。而且,当有多个Store使用该模型的时候,也不需要重新为每个Store定义一次代理。

下面继续定义Station 模型:

app/model/Station.js

  1. Ext.define('Panda.model.Station', {
  2. extend: 'Ext.data.Model',
  3. fields: ['id', 'name', 'played_date'],
  4. proxy: {
  5. type: 'ajax',
  6. url: 'data/stations.json',
  7. reader: {
  8. type: 'json',
  9. root: 'results'
  10. }
  11. }
  12. });

Store

在ExtJS 4,多个Store可以使用相同的数据模型,即使Store需要从不同的来源加载数据。在实例中,模型Station将会在SearchResults和Stations这两个Store中使用,而它们会从不同的位置加载数据。其中,SearchResults将返回返回搜索结果,而另一个则返回用户收藏的Station。为了实现这一点,其中一个Store需要重写模型中定义的代

理。

app/store/SearchResults.js

  1. Ext.define('Panda.store.SearchResults', {
  2. extend: 'Ext.data.Store',
  3. requires: 'Panda.model.Station',
  4. model: 'Panda.model.Station',
  5. // Overriding the model's default proxy
  6. proxy: {
  7. type: 'ajax',
  8. url: 'data/searchresults.json',
  9. reader: {
  10. type: 'json',
  11. root: 'results'
  12. }
  13. }
  14. });

app/store/Stations.js

  1. Ext.define('Panda.store.Stations', {
  2. extend: 'Ext.data.Store',
  3. requires: 'Panda.model.Station',
  4. model: 'Panda.model.Station'
  5. });

在SearchResults的定义,已经重写了Station模型中代理的定义,当调用Store的load方法时,其代理将调用Store的的代理以替换模型中定义的代理。

当然,也可以在服务器端使用相同的接口实现返回搜索结果和用户收藏的station,这样,两个Store就可以使用模型中定义的默认代理了,只是加载数据时请求的参数不同。

接着创建RecentSongs:

app/store/RecentSongs.js

  1. Ext.define('Panda.store.RecentSongs', {
  2. extend: 'Ext.data.Store',
  3. model: 'Panda.model.Song',
  4. // Make sure to require your model if you are
  5. // not using Ext JS 4.0.5
  6. requires: 'Panda.model.Song'
  7. });

要注意,在当前版本的ExtJS中,在Store中的model属性还不能自动创建一个依赖,因而需要通过requires配置项指定模型以便能够实现动态加载模型。

另外,根据约定,Store的类名要使用复数,而模型的类名要使用单数。

增加Store和模型到应用

定义好模型和Store后,现在可以把它们加到应用里。打开Application.js并添加如下代码:

app/Application.js

  1. Ext.application({
  2. ...
  3. models: ['Station', 'Song'],
  4. stores: ['Stations', 'RecentSongs', 'SearchResults']
  5. ...
  6. });

使用ExtJS 4 MVC包的另外一个好处是,应用会自动加载在stores和models中定义的Store和模型。当Store加载后,会为每个Store创建一个实例,并将其名称作为storeId。这样,就可以使用该名称将其绑定到视图中数据组件,例如“SearchResults”。

应用粘合剂

在开始的时候,可以将视图逐一加到Viewport,这样比较容易调试出视图错误的配置。先在Panda应用中构建Viewport:

  1. Ext.define('Panda.view.Viewport', {
  2. extend: 'Ext.container.Viewport',

通常,应用的Viewport类扩展自Ext.container.Viewport,这会让应用把浏览器的可用空间作为应用的空间。

  1. requires: [
  2. 'Panda.view.NewStation',
  3. 'Panda.view.SongControls',
  4. 'Panda.view.StationsList',
  5. 'Panda.view.RecentlyPlayedScroller',
  6. 'Panda.view.SongInfo'
  7. ],

这里要设置viewprot依赖的视图, 这样,就可以使用视图中通过alias属性定义的名称作为xtype配置项的值来定义视图。

  1. layout: 'fit',
  2. initComponent: function() {
  3. this.items = {
  4. xtype: 'panel',
  5. dockedItems: [{
  6. dock: 'top',
  7. xtype: 'toolbar',
  8. height: 80,
  9. items: [{
  10. xtype: 'newstation',
  11. width: 150
  12. }, {
  13. xtype: 'songcontrols',
  14. height: 70,
  15. flex: 1
  16. }, {
  17. xtype: 'component',
  18. html: 'Panda<br>Internet Radio'
  19. }]
  20. }],
  21. layout: {
  22. type: 'hbox',
  23. align: 'stretch'
  24. },
  25. items: [{
  26. width: 250,
  27. xtype: 'panel',
  28. layout: {
  29. type: 'vbox',
  30. align: 'stretch'
  31. },
  32. items: [{
  33. xtype: 'stationslist',
  34. flex: 1
  35. }, {
  36. html: 'Ad',
  37. height: 250,
  38. xtype: 'panel'
  39. }]
  40. }, {
  41. xtype: 'container',
  42. flex: 1,
  43. layout: {
  44. type: 'vbox',
  45. align: 'stretch'
  46. },
  47. items: [{
  48. xtype: 'recentlyplayedscroller',
  49. height: 250
  50. }, {
  51. xtype: 'songinfo',
  52. flex: 1
  53. }]
  54. }]
  55. };
  56. this.callParent();
  57. }

因为Viewport扩展自Container,而Container没有停靠项,因而必须添加一个面板作为viewport的第一个子组件,并让使用fit布局让面板与viewprot有相同的大小。

在架构方面,需要特别注意的是,不要在实际视图中定义有具体配置的布局,例如,不要定义flex、width或height等配置项,这样,就可以很容易的调整其在应用程序中某个位置的整体布局,从而增加架构的可维护性和灵活性。

应用逻辑

在ExtJS 3,通常会使用按钮的句柄、绑定监听时间到子组件或扩展时重写其方法以实现应用逻辑。然后,就像不应该在HTML标记中使用内联CSS样式一样,不应该在视图中定义应用逻辑。在ExtJS 4,通过MVC包提供的控制器,可响应视图或其它控制器中的监听事件,并执行这些事件对应的应用逻辑。这样的设计有及格好处。

第一个好处是,应用逻辑不用绑定到视图实例,这意味着在需要时,可以在视图实例化或销毁时,应用逻辑可以继续处理其它事情,如同步数据。

另外,在ExtJS 3,会有许多嵌套视图,而每一个都会添加一层应用逻辑。当将应用逻辑移动到控制器,将它们集中起来,这样就易于维护和修改。

最后,控制器基类会提供一些功能,让控制器易于执行应用逻辑。

创建控制器

现在,已经有了UI的基本架构、模型和Store的配置,是时候为应用定义控制器了。我们计划定义两个控制器,Station和Song,定义如下:

app/controller/Station.js

  1. Ext.define('Panda.controller.Station', {
  2. extend: 'Ext.app.Controller',
  3. init: function() {
  4. ...
  5. },
  6. ...
  7. });

app/controller/Song.js

  1. Ext.define('Panda.controller.Song', {
  2. extend: 'Ext.app.Controller',
  3. init: function() {
  4. ...
  5. },
  6. ...
  7. });

在应用中包含控制器后,框架会在调用init方法的时候字段加载控制器。在init方法内,可以定义监听视图和应用的事件。在大型应用,有时需要在运行时加载额外的控制器,可以使用getController方法加载:

  1. someAction: function() {
  2. var controller = this.getController('AnotherController');
  3. // Remember to call the init method manually
  4. controller.init();
  5. }

当在运行时加载额外的控制器时,一定要记得手动调用加载的控制器的init方法。

在实例应用,只要将控制器加到controllers配置项中的数组,让框架加载和初始化控制器就可以了。

app/Application.js

  1. Ext.application({
  2. ...
  3. controllers: ['Station', 'Song']
  4. });

定义监听

现在要在控制器的init方法内调用其control方法控制UI部件:

app/controller/Station.js

  1. ...
  2. <SPAN class=me1>init</SPAN><SPAN class=sy0>:</SPAN> <SPAN class=kw2>function</SPAN><SPAN class=br0>(</SPAN><SPAN class=br0>)</SPAN> <SPAN class=br0>{</SPAN>
  3. <SPAN class=kw1>this</SPAN>.<SPAN class=me1>control</SPAN><SPAN class=br0>(</SPAN><SPAN class=br0>{</SPAN>
  4. <SPAN class=st0>'stationslist'</SPAN><SPAN class=sy0>:</SPAN> <SPAN class=br0>{</SPAN>
  5. selectionchange<SPAN class=sy0>:</SPAN> <SPAN class=kw1>this</SPAN>.<SPAN class=me1>onStationSelect</SPAN>
  6. <SPAN class=br0>}</SPAN><SPAN class=sy0>,</SPAN>
  7. <SPAN class=st0>'newstation'</SPAN><SPAN class=sy0>:</SPAN> <SPAN class=br0>{</SPAN>
  8. select<SPAN class=sy0>:</SPAN> <SPAN class=kw1>this</SPAN>.<SPAN class=me1>onNewStationSelect</SPAN>
  9. <SPAN class=br0>}</SPAN>
  10. <SPAN class=br0>}</SPAN><SPAN class=br0>)</SPAN><SPAN class=sy0>;</SPAN>
  11. <SPAN class=br0>}</SPAN>
  12. ...

方法control会通过对象的关键字查询组件。在示例中,会通过视图的xtypes配置项查询组件。然而,使用组件查询,必须确保能指向UI中的知道部件。想了解更多有关组件查询的信息,可访问这里。

每一个查询都会绑定一个监听配置对象,在每个监听配置对象内,事件名就是监听对象的关键字,而事件是由查询的目标组件提供的。在示例中,将使用Grid(在StationsList视图扩展出的)提供的selectionchange事件,及ComboBox(从NewStation视图中扩展出的)提供的select事件。要找到特定组件有那些事件,可以在API文档中查看组件的事件部分。

监听配置对象中的值是事件触发后执行的函数。函数的作用将是控制器本身。

现在为Song控制器添加一些监听:

app/controller/Song.js

  1. ...
  2. init: function() {
  3. this.control({
  4. 'recentlyplayedscroller': {
  5. selectionchange: this.onSongSelect
  6. }
  7. });
  8. this.application.on({
  9. stationstart: this.onStationStart,
  10. scope: this
  11. });
  12. }
  13. ..

除了监听RecentlyPlayedScroller视图的selectionchange事件外,这里还需要监听应用事件。这需要使用application实例的on方法。每一个控制都可以使用this.application直接访问application实例。

当在应用中有许多控制器都需要监听同一个事件的时候,应用事件这时候非常有用。与在每一个控制器中监听相同的视图事件一样,只需要在一个控制器中监听视图的事件,就可以触发一个应用范围的事件,这样,等于其它控制器也监听了该事件。这样,就实现了控制器之间进行通信,而无需了解或依赖于对方是否存在。

控制器Song需要知道新的station是否已开始,以便更新song滚动条和song信息。

现在在Station控制器内,编写当应用事件stationstart触发时的响应代码:

app/controller/Station.js

  1. ...
  2. onStationSelect: function(selModel, selection) {
  3. this.application.fireEvent('stationstart', selection[0]);
  4. }
  5. ...

这里只是简单在触发stationstart事件时,将selectionchange事件中的第一个选择项作为唯一参数传递给事件。

小结

在这篇文章中,可以了解到应用架构的基本技术。当然,这只是其中的一小部分,在该系列的下一部分,将可以看到一些更高级的控制器技术,并继续在控制器中实现操作,以及在视图中添加细节,以完成Panda应用。

作者: Tommy Maintz
Tommy Maintz is the original lead of Sencha Touch. With extensive knowledge of Object Oriented JavaScript and mobile browser idiosyncracies, he pushes the boundaries of what is possible within mobile browsers. Tommy brings a unique view point and an ambitious philosophy to creating engaging user interfaces. His attention to detail drives his desire to make the perfect framework for developers to enjoy.

转载于:https://blog.51cto.com/dqhuang/938968

ExtJS应用架构设计(二)相关推荐

  1. 阿里云域名解析网络和服务架构设计(二) 之云解析DNS-全局流量管理

    一.回顾 阿里云域名解析网络和服务架构设计总概览(一)_飞鸽FlyGo的博客-CSDN博客https://flygo.blog.csdn.net/article/details/123604615 二 ...

  2. 关于游戏架构设计(二)

    架构设计目录二 UI架构 基类BaseWindow Control控制类 State状态接口 窗口管理类 状态管理类 角色系统 角色系统设计的框架图 角色实体类父类 IEntity 有限状态机接口En ...

  3. Unity3D快速实现UI架构设计二

    笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D ...

  4. MySQL性能调优与架构设计(二)—— MySQL存储引擎简介

    什么是存储引擎 mysql中的数据用各种不同的技术存储在文件(或者内存)中. 这些技术中每一种技术都使用不同的存储机制.索引技巧.锁定水平并且最终提供广泛的不同的功能和能力. 通过选择不同的技术,你能 ...

  5. [开源 .NET 跨平台 Crawler 数据采集 爬虫框架: DotnetSpider] [一] 初衷与架构设计

    [DotnetSpider 系列目录] 一.初衷与架构设计 二.基本使用 三.配置式爬虫 四.JSON数据解析与配置系统 五.如何做全站采集 为什么要造轮子 同学们可以去各大招聘网站查看一下爬虫工程师 ...

  6. Spark Streaming ReceiverTracker架构设计

    本节的主要内容: 一.ReceiverTracker的架构设计 二.消息循环系统 三.ReceiverTracker具体实现 Spark Streaming作为Spark Core基础 架构之上的一个 ...

  7. 软考高级系统架构设计师:Web架构设计

    软考高级系统架构设计师:Web架构设计 一.Web架构设计 二.负载均衡技术 1.应用层负载均衡 2.传输层负载均衡 三.有状态与无状态 四.数据库读写分离化 五.缓存技术 六.CDN内容分发网络 七 ...

  8. 阿里云域名解析网络和服务架构设计(三) 之阿里云CLB负载均衡

    一.回顾 阿里云域名解析网络和服务架构设计总概览(一)_飞鸽FlyGo的博客-CSDN博客云解析DNS.负载均衡SLB.阿里云ECS服务器.阿里云ECS服务器Nginx代理https://flygo. ...

  9. 阿里云域名解析网络和服务架构设计(四) 之阿里云ECS服务器Nginx代理实践

    一.回顾 阿里云域名解析网络和服务架构设计总概览(一)_飞鸽FlyGo的博客-CSDN博客云解析DNS.负载均衡SLB.阿里云ECS服务器.阿里云ECS服务器Nginx代理https://flygo. ...

  10. Android 架构设计(四):组件化?

    同系列传送门 Android 架构设计(一):设计模式分析_赵星海的博客-CSDN博客 Android 架构设计(二):分包和文件结构_赵星海的博客-CSDN博客_android 分包结构 Andro ...

最新文章

  1. VMware 虚拟机
  2. The way of Webpack learning (II.) -- Extract common code(多页面提取公共代码)
  3. [转]matlab 函数三种定义方式
  4. delphi tclientsocket接收不到返回数据_RS—485中教你主站发送报文结构、从站返回报文结构?系列11...
  5. linux下文件合并、分割、去重
  6. 获2017中国最佳创业投资机构百强,西高投二次创业实现超越
  7. emacs .emacs_谁在乎Emacs?
  8. 用python计算矩形面积结果四舍五入保留两位小数_你还在计算器开平方?太low了吧,学霸考场都用这种方法开平方...
  9. 湖科大计算机科学与技术,湖南科技大学计算机科学与工程学院前来我院进行访问与交流...
  10. OOP之C#设计及其UML(反向工程)
  11. for循环后面的分号
  12. ArcGIS教程:山地风景区景观规划中的可视性分析
  13. 五一入哪个牌子蓝牙耳机好?无线主动降噪蓝牙耳机排行榜
  14. sqldeveloper的安装及其使用教程
  15. 企业视频会议系统EasyRTC-SFU之mediasoup-demo在 Windows上的编译安装
  16. 秋招攻略—如何成为一名图像算法攻城狮(上篇)—知识学习篇
  17. python测试开发培训怎么样_合肥Python测试开发全栈核心课程
  18. pidgin连接 gtalk
  19. Pyth-Solana链上联通现实的桥梁
  20. Reverse Influence Sampling in Python(译文)

热门文章

  1. paip.版本控件svn删除文件或目录后的恢复
  2. Rust: tokio,异步代码与运行速度初探
  3. (转)比特币有了定价模型?过去四年94%的价格波动可由此解释
  4. (转)券商IT研发现状:一年最多花5亿 中小公司靠外包
  5. 开源,阿里千万实例可观测采集器 iLogtail
  6. 阿里38号元老:管理要轻,文化要浓
  7. 开源中国开源世界高峰论坛如期将至
  8. c语言编程简单好玩的图,几个有趣的画图编程
  9. 【火灾检测】基于matlab GUI火灾检测(带面板)【含Matlab源码 1646期】
  10. 【协同任务】基于matlab多无人机目标搜索与围捕【含Matlab源码 1205期】