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

view的mixin设计 #32

Open
xinglie opened this issue Feb 18, 2017 · 0 comments
Open

view的mixin设计 #32

xinglie opened this issue Feb 18, 2017 · 0 comments

Comments

@xinglie
Copy link
Member

xinglie commented Feb 18, 2017

为什么要有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种事件绑定方式

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant