Omi框架学习之旅 - 插件机制之omi-finger 及原理说明
以前那篇我写的alloyfinger源码解读那篇帖子,就说过这是一个很好用的手势库,hammer能做的,他都能做到,
而且源码只有350来行代码,很容易看懂。
那么怎么把这么好的库作为omi库的一个插件呢,使dom,用起来更爽,更方便呢?
omi自己有个叫插件体系的功能,主要是赋予dom元素一些能力,并且可以和组件的实例产生关联。
这当然棒极了。那怎么实现的呢?
还是先看个demo,看看用起来爽不,爽的话,再看原理也不迟啊。
OmiFinger.init(); // 初始化OmiFinger插件 class App extends Omi.Component { constructor(data) { super(data); } style() { return ` .touchArea{ background-color: green; width: 200px; min-height: 200px; text-align: center; color:white; height:auto; } `; } tap(evt) { console.log(this.refs.div1); this.refs.ptext.innerHTML = 'tap'; } longTap(evt) { console.log(evt); this.refs.ptext.innerHTML = 'longTap'; } swipe(evt) { this.refs.ptext.innerHTML = evt.direction; } render() { return ` <div> <div omi-finger class="touchArea" tap="tap" longTap="longTap" swipe="swipe" ref="div1"> <!--在这里写事件名即可--> Tap or Swipe Me! <p ref="ptext"></p> </div> </div> `; } } var app = new App(); Omi.render(app, 'body');
看下结果:
我热,就是这么简单,就把rotate touchStart multipointStart multipointEnd pinch swipe tap doubleTap longTap singleTap pressMove touchMove touchEnd touchCancel这14个事件都能赋给dom去监听相应的实例函数。
的确很方便,很omi,一个类(哦,不,一个组件(大家都喜欢叫组件,我内心是拒绝的))管理一切啊。
demo的疑问和疑问的说明:
疑问一:
那omi是怎么做到的啊?
答: 其实omi的插件机制代码很少,少的可怜,只是作为Component原型上_execPlugins方法,当然少不了和omi挂钩了,Omi上有个plugins对象属性,里面专门放插件名及插件函数的。
那就一步一步来吧。
demo上的OmiFinger.init();其实就是把初始化了一个插件,放到omi.plugins上去了,仅此而已,没帮我们做别的。来看源码
Omi.plugins = {}; // omi插件集合 // 扩展插件的方法(其实是给了plugins这个对象) Omi.extendPlugin = function(name, handler) { Omi.plugins[name] = handler; };
再来看一下OmiFinger.init();方法是不是调用了Omi.extendPlugin方法
var OmiFinger = {}; var noop = function(){ }; var getHandler = function(name, dom, instance) { var value = dom.getAttribute(name); // 从属性上获取对应的函数名 if (value === null) { return noop; }else{ return instance[value].bind(instance); // 从类上找到对应的方法 }; }; OmiFinger.init = function(){ Omi.extendPlugin('omi-finger',function(dom, instance){ if (!instance.alloyFingerInstances) instance.alloyFingerInstances = []; // finger的实例都存到这里面 var len = instance.alloyFingerInstances.length; var i = 0; for( ; i<len; i++){ if(instance.alloyFingerInstances[i].dom === dom){ // 如果以前绑定过得, 就先销毁, 然后重新來过 instance.alloyFingerInstances[i].fg.destroy(); instance.alloyFingerInstances.splice(i,1); // 并且剔除 break; }; }; var alloyFinger = new AlloyFinger(dom,{ touchStart: getHandler('touchStart', dom, instance), touchMove: getHandler('touchMove', dom, instance), touchEnd: getHandler('touchEnd', dom, instance), touchCancel: getHandler('touchCancel', dom, instance), multipointStart: getHandler('multipointStart', dom, instance), multipointEnd: getHandler('multipointEnd', dom, instance), tap: getHandler('tap', dom, instance), doubleTap: getHandler('doubleTap', dom, instance), longTap: getHandler('longTap', dom, instance), singleTap: getHandler('singleTap', dom, instance), rotate: getHandler('rotate', dom, instance), pinch: getHandler('pinch', dom, instance), pressMove: getHandler('pressMove', dom, instance), swipe: getHandler('swipe', dom, instance) }); instance.alloyFingerInstances.push({fg:alloyFinger,dom:dom}); }); } OmiFinger.destroy = function(){ delete Omi.plugins['omi-finger']; }; window.OmiFinger = OmiFinger;
这里我把代码都贴出来,因为比较简单。
Omi.plugins有了对象插件名和函数,那是怎么循环遍历的呢?
其实就在Component类的_render方法最后面,遍历的,也就是当html插入到指定容器后,再调用的。
那是怎么调用的呢?
// 插件机制 _execPlugins(){ Object.keys(Omi.plugins).forEach(item => { // 遍历omi的插件 let nodes = Omi.$$('*['+item+']',this.node); // 具有插件名属性的dom nodes.forEach(node => { if(node.hasAttribute(this._omi_scoped_attr) ) { // 节点是否含有_omi_scoped_id属性 Omi.plugins[item](node, this); // 调用插件init方法中第二个函数 }; }); if(this.node.hasAttribute(item)) { // 看一下根节点是否含有插件名属性,有的话也执行 Omi.plugins[item](this.node, this); }; }); }
可以看到,会传2个参数,一个dom,一个实例.正是验证了官网的这句话。 Omi插件体系可以赋予dom元素一些能力,并且可以和组件的实例产生关联。
至此就这么说完了。
ps:
当然还有transform.js, touch.js也可以让dom玩的飞起,后续再写帖子吧。