-
Notifications
You must be signed in to change notification settings - Fork 0
view的事件设计细节 #33
Description
在说事件设计细节之前,先讲一下view的设计,便于理解事件的设计
view的设计
view的设计原则上要求view尽可能的独立,尤其是view之间应该相互独立。像iframe标签一样,每个内容都是一个独立的单元。
view的分类
在实际使用中,可以简单把view归为两大类。
一类是完成界面功能,比如下拉框、弹出框、业务中的header,footer等,这些view拥有自己的html及可能有的css。不但有功能,还有相应的界面。
再一类就是增强界面功能的,比如已经存在了列表界面,列表项带checkbox及一个全选checkbox。这时候我们实现一个view,用于联动checkbox。当然,这个功能可以使用mixin实现,也可以用view来实现,通常这类view只有功能,没有界面
view的示例代码
我们对前述的view做一个示例,更直观的来看下具体情况
我们需要三个view:入口(index) 列表 (list) 联动checkbox (linkage)
接下来我们只展示入口index的html部分
<!-- index.html -->
<div>
<ul mx-view="app/views/linkage">
<li><input type="checkbox" data-checkbox-type="all" />全选</li>
<li><input type="checkbox" data-checkbox-type="one"/>1</li>
<li><input type="checkbox" data-checkbox-type="one"/>2</li>
<li><input type="checkbox" data-checkbox-type="one"/>3</li>
<li><input type="checkbox" data-checkbox-type="one"/>4</li>
</ul>
<div mx-view="app/views/list">
loading...
</div>
</div>该示例里面,ul标签上的app/views/linkage加载后会控制li里面的checkbox联动。
下面的div标签上的app/views/list加载后,会把真实的列表内容填充进来。
view的事件处理
对上述示例,我们添加一些事件来讲述具体的细节。
<!-- index.html -->
<div>
<ul mx-view="app/views/linkage">
<li mx-click="clickLi()"><input mx-click="clickAll()" type="checkbox" data-checkbox-type="all" />全选</li>
<li><input type="checkbox" data-checkbox-type="one"/>1</li>
<li><input type="checkbox" data-checkbox-type="one"/>2</li>
<li><input type="checkbox" data-checkbox-type="one"/>3</li>
<li><input type="checkbox" data-checkbox-type="one"/>4</li>
</ul>
<div mx-view="app/views/list">
<div>正在加载数据,如果长时间不显示,请<a href="javascript:;" mx-click="retry()">点击这里重试</a></div>
</div>
</div>此时对应的index.js可能如下
//index.js
var Magix = require('magix');
module.exports = Magix.View.extend({
tmpl: '@index.html'
render: function() {
},
'clickLi<click>': function(e) {
console.log('li ', e);
},
'clickAll<click>': function(e) {
console.log('all ', e);
},
'retry<click>': function(e) {
console.log('retry ', e);
}
});我们从html上结构上来看,这些事件clickLi、clickAll及retry都是在其它view所属的范围内的,比如clickLi和clickAll离的最近的view是app/views/linkage,retry离的最近的view是app/views/list。所以当click事件发生时,magix该如何处理它们?
magix之前的版本是找最近的view,然后把事件交与最近的view来处理,但是显然从这个例子来看,这个做法是不妥的。理想的做法是:谁创建的界面,谁负责事件处理。
所以这个问题高效的解决方案是使用magix配套的magix-combine工具来打包view。另外magix仍然内置了动态查找,以应对不使用工具时也能正确处理。
所以到目前为止,这种在html写mx-eventType的方式可以方便的解决,然而棘手的是另外一个事件绑定
view支持选择器绑定事件
通常带界面的我们可以很方便的通过在html里添加mx-eventType属性来标记事件的处理方法,这也是我们推荐的做法,而对于不带界面的view,如前述的linkage来联动checkbox的,我们就没办法使用这样的方案来绑定事件了。
所以这时候我们可以调用类库的事件绑定来自己处理,如
//linkage.js
var Magix = require('magix');
var $ = require('jquery');
module.exports = Magix.View.extend({
init: function() {
var me = this;
$('#' + me.id + ' input[data-checkbox-type=all]').on('change', function() {
me.processAll();
});
$('#' + me.id + ' input[data-checkbox-type=one]').on('change', function() {
me.processOne();
});
},
processAll: function() {
},
processOne: function() {
}
});这种方式也是可以的,但是显然开发者要考虑事件绑定,还需要考虑事件解绑,同时还要考虑动态添加的checkbox,还要考虑只处理当前view范围内的checkbox。所以需要开发者关注的事情较多,magix综合这些问题,推出了以下的方案
//linkage.js
var Magix = require('magix');
var $ = require('jquery');
module.exports = Magix.View.extend({
processAll: function() {
},
processOne: function() {
},
'$input[data-checkbox-type=all]<change>': function(e) {
this.processAll();
},
'$input[data-checkbox-type=one]<change>': function(e) {
this.processOne();
}
});以$开头的表示接下来是一个选择器,开发者不需要考虑绑定、解绑、动态添加、移除、只选择当前范围内的元素等问题,magix会自动帮你搞定。
支持了选择器绑定事件后,接下来就有一个比较现实的问题:
view可以自由嵌套,选择器很可能会选中子view中的节点元素。(其实css也有这样的问题,当然这是另外一个话题了。)
所以到这里我们就有必要讨论一下如果用选择器来绑定事件的话,如果子view也有能被选择器选择到的节点,该如何处理?
magix的最终结论是和在mx-eventType中的事件行为保持一致,这是其中一点,另一点是前述的view设计,view本身就要求尽可能的独立,所以自己的事情自己办,所有的view中的事件只在当前范围内生效。
那么magix该如何处理选中子view中的节点元素?这块已从代码上解决,关键点见下述内容
选择器事件绑定细节
- 所有view有没有渲染结束magix是知道的,详见view的源码endUpdate方法
- view有没有界面也是知道的,我们约定有界面的一定有tmpl属性
- 所有事件绑定在magix中都是统一处理的
- 当选择器绑定的事件发生时,向上查找最近的vframe,如果经过已经渲染过的,且带界面的vframe,则停止查找,事件也不再派发,只有找到 绑定选择器事件时view与查找到的view相同,才派发事件。
更多细节则需要从代码上查看了