Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chassis视图使用指南 #41

Open
miller opened this issue Jun 6, 2013 · 0 comments
Open

Chassis视图使用指南 #41

miller opened this issue Jun 6, 2013 · 0 comments
Assignees
Labels

Comments

@miller
Copy link
Collaborator

miller commented Jun 6, 2013

<<返回目录

View是Chassis中非常重要的组成部分,APP中可以没有Model但是不会没有View。View在Chassis中担当的职责包括:数据读写、UI渲染、页面切换以及用户交互。由此可见,Chassis中的View并不是传统MVC中的 V ,而是相当于 Controller 的角色。

分类

在Chassis中,按功能划分View被分为PageView、SubView以及GlobalView:

  • PageView: 页面视图,代表的是一个完整的逻辑页面,是APP路由的对象;
  • SubView: 子视图,代表的是逻辑页面中的某个子模块,PageView可以包含零个或多个子视图;子视图可以被定义为子页面(SubPage)并进行子页面路由处理;
  • GlobalView: 全局视图,代表的是APP中全局固定的部分,例如全局导航栏等;全局视图可以监听路由事件,也可以向指定的PageView派发事件,但是本身不参与任何路由逻辑;

在Chassis的内部实现中,PageView、SubView以及GlobalView都继承于Chassis.View类,而且绝大部分实现代码也都位于基类中,因此接下来我们主要会针对Chassis.View进行分析。

生命周期管理

在View的基类中有三个类方法以及一个实例方法直接参与着View的生命周期管理。需要注意的是,基类是一个虚类,不要直接使用基类创建类实例或者自定义视图类。

自定义视图类

在使用Chassis开发APP时,通常都是通过继承方式来自定义视图类,这里会用到基类中的define方法。

使用define方法定义一个PageView类:

Chassis.PageView.define( 'home', {
    el: '#home',
    events: {},
    init: function(){}
} );

define执行时实际上是在Chassis.PageView命名空间下新增了一个key为home的Chassis.View子类。

实际上它等同于:

Chassis.PageView.home = Chassis.PageView.extend( {
    el: '#home',
    events: {},
    init: function(){}
} );

而在实际开发中,你确实也可以使用第二种方法来自定义类,但是我们还是强烈建议你使用define。一方面代码会更简洁,另一方面如果key中包含特殊字符时也会更便利,此外对于PageView而言,key就是路由中的action,因此必须得小写,通过define的方式可以避免大小写不一致的问题。

此外,SubView和GlobalView都有define方法,因此通过相同的方式自定义视图。

实例化视图

通过前面的介绍大家知道了,视图的定义实际上是通过继承的方式得到一个新的视图类,并放在了对应视图类的命名空间下,那么实例化视图可以简单的使用这种方式:

var homeView = new Chassis.PageView.home( {} );

不过,我们还是强烈推荐使用以下方式来创建实例:

var homeView = Chassis.PageView.create( 'home', {} );

获取视图类

通过define( id, definition );可以定义一个视图类,相应的通过get( id ); 接口可以从相应的视图空间中获得类定义。

var HomeView = Chassis.PageView.get( 'home' );

当然也可以直接获取:

var HomeView = Chassis.PageView.home;

但是同样会有大小写问题,或者有特殊字符需要使用引号访问的问题。

销毁视图

当你需要销毁某个视图时,务必记得使用视图实例上的destroy接口。调用该接口不仅仅会销毁视图对应的DOM结点,还包括所有通过events对象注册的事件以及当前视图中的所有子视图。

var homeView = Chassis.PageView.create( 'home', {} );
homeView.destroy();

层级管理

Chassis的视图支持多级嵌套,这样便于开发者灵活的拆分模块。为了实现上的简洁性,层级管理的接口也是在基类中,包括以下几个接口:

  • append(view): 将目标视图的DOM结点append到当前视图的DOM结点中,同时建立视图层级关系;
  • prepend(view): 将目标视图的DOM结点prepend到当前视图的DOM结点中,同时建立视图层级关系;
  • setup(view): 仅建立视图层级关系;

这里的视图关系包括:

  • 将目标视图的parent属性指向当前视图;
  • 将目标视图放入当前视图的children属性中;

如果你的SubView是新创建的DOM结点,而并不是在已有DOM上操作时,创建层级关系需要调用appendprepend方法,否则可以使用setup方法。

这里需要注意,虽然接口在基类中,但是实际上PageView是不允许相互嵌套的,同时PageView和GlobalView也不应该相互嵌套。除此之外,PageView和GlobalView都可以可以嵌套多个SubView,而SubView中可以继续嵌套SubView。

GlobalView PageView
    |          |
 SubView    SubView
               |
            SubView

事件管理

events对象

当你自定义视图类或者创建视图类的实例时都可以定义一个events对象,例如:

Chassis.PageView.define( 'home', {
    el: '#home',
    events: {
        'click .btn': 'onBtnClick' 
    },
    init: function(){},
    onBtnClick: function(){}
} );

Chassis.PageView.define( 'home', {
    el: '#home',
    init: function(){},
    onBtnClick: function(){}
} );

var homeView = Chassis.PageView.create( 'home', {
    events: {
        'click .btn': 'onBtnClick' 
    }
} );

在视图中,通过events对象几乎可以将所有的事件注册都通过配置的方式来实现,像上面的例子所述,events对象的格式如下:

{ 'type target': 'handler' || handler }

其中handler表示的是事件的callback函数,可以是字符串,也可以是直接的函数定义。如果是字符串的话会通过this[handler]来查找callback。callback被调用时的参数为事件参数,执行context为当前视图;

type表示事件名,根据不同的target而不同。

target为需要监听的对象,这里包括多种类型:

  • dom selector: 表示监听指定的DOM结点;这类事件注册时,会将当前视图元素中selector所选中DOM结点的事件通过代理的方式注册到当前视图的元素中;即当前视图所在元素会代理一切此类DOM事件;例如'click .btn',会将this.$el中所有class为.btn的元素的click事件代理到this.$el上;此外,如果selector为空则表示直接注册到this.$el上;
  • view: 表示监听当前视图所触发的事件,例如beforepagein view
  • model: 表示监听当前视图中的model所触发的事件,例如change model
  • window: 表示监听window上的事件;
  • document:表示监听document上的事件;

例如:

{
    'mousedown .title': 'edit',
    'click .button': 'save',
    'click .open': function( e ){},
    'orientationchange window': 'refresh',
    'click document': 'close',
    'beforepagein view': 'onBeforePageIn',
    'change model': 'render'
}

在定义视图时配置events对象以及在实例化时传入events对象都能使视图自动进行事件注册,这种方式是强烈推荐的,因为在视图销毁时会自动解除这一过程中绑定的事件,如果使用其他方法注册则无法保证能自动解除。

delegateEvents方法

如果需要在视图运行中注册事件,同样建议调用delegateEvents(events)方法来处理,因为该方法会用相同的方法来注册事件,在视图销毁时可以确保事件被解除。

触发的事件

视图本身在不同的状态下也会触发一些事件。

beforepagein

在视图切换中,当前视图即将进入可视区之前会触发该事件,如果要监听可以使用以下方式:

Chassis.PageView.define( 'home', {
    events: {
        'beforepagein view': 'onBeforePageIn' 
    },
    onBeforePageIn: function(){}
} );

但实际上,由于该事件监听比较频繁,Chassis对此做了简化,无需在events对象再进行配置,可以直接定义onBeforePageIn方法。即上述代码等同于:

Chassis.PageView.define( 'home', {
    onBeforePageIn: function(){}
} );

afterpagein

beforepagein对应,在视图已经完全进入可视区时触发,同样可以省略events对象的配置,可以直接定义onAfterPageIn方法。

beforedestroyafterdestroy

在视图销毁之前以及销毁完成后触发。
如果要响应beforedestroy事件可以直接定义onDestroy方法,而无需配置events对象。

实例化参数

在实例化视图时可以配置的参数包括:modelelidattributesclassNametagNameevents

  • model: 视图对应的Model实例;
  • events: 事件注册配置;

其他的都与视图所在的DOM结点相关:

  • el: 视图对应的DOM元素,可以是$对象,也可以是html代码;
  • id: DOM ID;
  • attributes: DOM属性;
  • className: DOM CSS class;
  • tagName: 自动创建DOM时的tagName;

这里需要注意的是,其中el参数和其他参数是互斥的,如果设置了el参数则会直接将el转换成this.$el而不会进行DOM属性的设置;如果未设置el则视图会自动创建DOM并设置DOM属性。

其他方法与属性

  • $el属性: 通过this.$el可以获取到当前视图对应的DOM结点;
  • $方法: 通过this.$方法可以在当前DOM结点中进行元素查找;

<<返回目录

@ghost ghost assigned miller Jun 6, 2013
@xspider xspider mentioned this issue Jun 6, 2013
@miller miller mentioned this issue Jun 7, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant