Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Nov 12, 2024. It is now read-only.
This repository was archived by the owner on Nov 12, 2024. It is now read-only.

view的mixin设计 #32

@xinglie

Description

@xinglie

为什么要有mixins

参考 react 的 mixins https://segmentfault.com/a/1190000002704788

mixins的定位

依附在view上,增强或提供某些功能。

与组件的区别

  1. 组件更多的是讲究独立性,拥有完整的输入与输出
  2. mixin需要依赖或依附或需要view的支撑才能完成某功能

与继承的区别

我们举例说明

项目中常见数据列表页面,某些数据列表页面带checkbox供用户选择多条数据,而某些带checkbox的列表页需要保存用户选择的数据,即翻页不丢失选中的数据。比如在第1页选择了第1条和第2条数据,第1条和第2条的checkbox处于选中状态,翻到第2页,然后再翻回第1页,第1条和第2条数据前的checkbox要处于选中状态。

功能实现

  1. 我们需要实现一个普通列表页面
  2. 我们需要实现一个带checkbox的页面,带checkbox的通常会有一个全选的checkbox与每一条数据前的checkobx进行联动
  3. 我们需要实现跨翻页保存用户选择的数据不丢失

在实际运用中我们可能有以下情况

  1. 只要列表页面
  2. 列表带checkbox,但不用跨翻页保存数据
  3. 列表带checkobx,跨翻页保存数据

我们面向未来再考虑以下可能有的情况

  1. 某些接口可能慢,我们在加载列表时显示一个loading动画
  2. 某些接口可能一次返回所有数据,前端做分页

当我们用继承实现时,会发现很难根据当前列表的需求只使用某些功能,比如列表要带checkobx并且显示loading动画。只能提供一个大而全的列表类型的基类,把所有功能在基类实现,不管子类需不需要。
并且在后期维护的时候很难改动,因为作为基类是不能放心大胆的修改的。

mixins是把上述的每个功能点单独实现,然后view要某个功能就把相应的功能点mix进来。

  1. mixins采用组合的思路
  2. view可以自由选择某些mixins
  3. 便于查看view应用的功能,继承在稍微多级的情况下不便于查看。

mixins的使用示例

var Magix=require('magix');
var LoadingMixin=require('./mixins-loading');//加载动画
var StoreStateMxin=require('./mixins-store-state');//翻页保存选择的数据
module.exports=Magix.View.extend({
    tmpl:'@test.html',
    mixins:[LoadingMixin,StoreStateMixin],
    render:function(){

    }
});

mixins的实现细节

mixins的实现

mixins的实现与react的相似,返回一个对象即可,magix会把返回的对象mix到当前view的prorotype上。
所以在mixin中完全可以使用view中定义的所有方法和事件
magix会对mixin中的某些点做特殊处理,详见下述内容

mixins的初始化

当我们实现一个跨翻页保存数据的mixin时,需要在view初始化的时候声明一些变量

//mixins-store-state.js
module.exports={
    ctor:function(){
         this.$storeState={};
    }
}

使用ctor做初始化的工作。ctor方法会在view初始化时调用,且在view的init方法被调用前调用

早期magix view提供了init方法用于初始化,但在继承时,由于子类会覆盖父类中的方法,当父类和子类都实现init时,最终只会调用子类的init方法,而父类的init方法需要子类显示调用 。这在这某些情况下非常不方便,所以magix 3.x版本提供了ctor方法,父类、子类中提供的ctor均会被自动调用,不用担心覆盖的情况,这在继承时,父类中一些必须做的初始化工作,子类在继承时完全不用考虑了。

因此mixin也采用了ctor命名,进行初始化的工作。

一个view使用多个mixin时,每一个mixin的ctor方法均会被调用,且按mixins中的数组顺序进行调用。

mixins的事件处理

因为mixin的对象是mix到view的prototype上的,因此可以使用view的事件绑定,示例如下

//mixins-store-state.js
module.exports={
    ctor:function(){
         this.$storeState={};
    },
    'selectAll<change>':function(e){//全选checkbox的事件处理
        console.log(e);
    }
}

这时候就需要在html的全选checkbox添加上mx-change="selectAll()事件
所以这样的方式在使用mixin时显得不方便,事件绑定也可以使用选择器的形式进行绑定,示例如下

//mixins-store-state.js
module.exports={
    ctor:function(){
         this.$storeState={};
    },
    '$input[data-all-checkbox]<change>':function(e){//全选checkbox的事件处理
        console.log(e);
    }
}

这样就不需要修改html了,但这种情况要考虑view嵌套的情况下,选择器选中子view的情况。
不过综合来看,推荐使用选择器方案进行绑定
。 已实现选择器事件的隔离功能,即使选中子view中的节点,也不会响应这个事件

事件绑定细节考虑

多个mixin绑定了同一个事件和同一个名称或同一个选择器。
还是以前面的全选为例,假设有2个或更多个mixin都使用同样的选择器绑定了全选checkbox该如何处理

//mixins-1.js
module.exports={
    '$input[data-all-checkbox]<change>':function(e){//全选checkbox的事件处理
        console.log('mixin 1',e);
    }
}
//mixins-2.js
module.exports={
    '$input[data-all-checkbox]<change>':function(e){//全选checkbox的事件处理
        console.log('mixin 2',e);
    }
}

假设某个view同时使用了mixins-1和mixins-2

var Magix=require('magix');
var Mixin1=require('./mixins-1');
var Mixin2=require('./mixins-2');
module.exports=Magix.View.extend({
    tmpl:'@test.html',
    mixins:[Mixin1,Mixin2],
    render:function(){

    }
});

这种情况magix会把这2个mixin的事件都绑定上,它们会同时工作,调用顺序取决于mixins中的数组顺序。

如果view中也实现了插件中的事件绑定,如下:

var Magix=require('magix');
var Mixin1=require('./mixins-1');
var Mixin2=require('./mixins-2');
module.exports=Magix.View.extend({
    tmpl:'@test.html',
    mixins:[Mixin1,Mixin2],
    render:function(){

    },
   '$input[data-all-checkbox]<change>':function(e){//与2个mixins中的事件绑定相同
       console.log('view ',e);
    }
});

这种情况2个mixin中的事件将不会被绑定,即checkbox状态发生变化时,只会打印出view中的log

view在使用mixins时,允许对某些mxins的实现进行拦截处理,这样在处理一些复杂情况时会更加方便。拦截的方式就是在view上定义与mixin中相同的方法

当我们拦截,加上自己的处理逻辑后,仍然可以再调用mixin中的方法进行继续处理,如

var Magix=require('magix');
var Mixin1=require('./mixins-1');
var Mixin2=require('./mixins-2');
module.exports=Magix.View.extend({
    tmpl:'@test.html',
    mixins:[Mixin1,Mixin2],
    render:function(){

    },
   '$input[data-all-checkbox]<change>':function(e){//与2个mixins中的事件绑定相同
       console.log('view ',e);
      Mixin1['$input[data-all-checkbox]<change>'].call(this,e);
      Mixin2['$input[data-all-checkbox]<change>'].call(this,e);
    }
});

mixins中的普通方法

除ctor和事件绑定外,其它方法均为普通方法。多个mixin之间的普通方法不能有相同名称,只有ctor和事件绑定允许相同名称。

多个mixin中相同名称的普通方法在应用到同一个view上时,magix会抛出错误,需要开发者设计mixin时注意,这点也与react的mixin相似。

在view中除了可以重写拦截mixin中的事件方法外,也同样可以拦截mixin中的普通方法,仍然是在view中提供与mixin中相同名称的方法即可。

相关链接

事件绑定可参考:
内置不再支持$win与$doc的事件绑定,另外处理(更新:仍然支持~)

magix中的2种事件绑定方式

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions