轻量级高性能移动web框架 Javascript Mobile Framework
本文导语: JM(Javascript Mobile Framework) 是新一代轻量级高性能移动web框架,由腾讯前端团队AlloyTeam经项目实践积累沉淀而成。为拥抱移动互联网全新设计,专注为移动web项目,整个框架压缩后只有36K。 新的移动平台,给web带来更多的机会,...
JM(Javascript Mobile Framework) 是新一代轻量级高性能移动web框架,由腾讯前端团队AlloyTeam经项目实践积累沉淀而成。为拥抱移动互联网全新设计,专注为移动web项目,整个框架压缩后只有36K。
新的移动平台,给web带来更多的机会,同时也带来更多的挑战。原PC端的各种浏览器兼容,已经升级为多平台多终端多版本多浏览器的复杂问题。为了解决这个问题,各种移动框架应运而生,但是为了解决问题而使整个库的体积庞大,最终在兼容和性能的权衡上没有取得平衡。JM在设计上更看重性能,所以代码量上力求精简,同时能够处理大部分的移动web兼容问题。或许你发现JM并没有过多地为帮你做什么事情,但我们却为你避开了很多移动上的坑。
特性体积小
源用JX的编码方式
模块按需加载,可定制
可与JMUI结合使用,提供一整套移动方案
下面我们尝试使用JM来写一个HelloWorld程序,通过这个小Demo,你可以对JM有个概括的了解。
主要html
Hello World Hello World !
主要js
var $E = JM.event;var $D = JM.dom;var btn = $D.id('myButton');var word1 = $D.id('word1');var word2 = $D.id('word2');$E.on(btn,'click',function(){ //设置动画基本参数,并且每个动画间隔为1.5秒 J.Animation({ selector:'#word1', duration:1500, use3d:true }).setStyle({ //向右运动了40%,并且color变成蓝色 'top':'60px', 'opacity':'1' }).transit(function(){ this.setDuration(600); this.scale(2).setStyle({ 'top': '40px', 'opacity':'0' }).transit(function(){ word1.style.cssText = ""; //清空dom的style,让动画回归起初状态 }); }); J.Animation({ selector:'#word2', duration: 1500, use3d : true }).setStyle({ 'top':'140px', 'opacity':'1' }).transit(function(){ this.setDuration(800); this.scale(2).setStyle({ 'top': '150px', 'opacity':'0' }).transit(function(){ word2.style.cssText = ""; }); });});
代码解析
示例程序的button样式额外地使用了JMUI的button样式,不使用也没有影响,样式可以你写你喜欢的。我们来看看js代码部分,$D和$E是引用jm的模块名,这样再代码使用中更加简洁而已,你完全也可以var btn = JM.dom.id('#word1');。代码中展示了JM三个模块的使用:Dom节点选择器,事实处理机制,动画模块。
JM除了提供常规的选择器外JM.dom.$(selectorText),还提供专门的选择id选择器JM.dom.$(id),我们也建议尽量使用id选择器,因为在效率上id选择器是最快的。在事件处理上JM.event.on(obj, evtType, handler),obj 为绑定的dom节点,evtType为事件类型,hadler的事件处理函数,obj和handler都可以输入数组,我们支持多对象多事件同时处理处 理。还有些常规的off,one,fire等方法外,还对swip,hold,toch等移动专属事件方法,详细情况可以参考event模块。
事件处理的代码里面是JM.Animation内容。有两个段动画代码,我们看第一段就可以了。传给Animation的是一些基本参数,其中use3d来开启这段动画是否使用GPU的3D动画加速(注意这不是银弹,不是所有动画都必须加上),setStyle方法顾名思义设置要修改的样式,transit方法就是把我们setStyle修改的样式使用css3的transiton的动画形式展示,transit方法只接受一个FinshFunc参数,动画执行后就会执行FinshFunc。通过在FinshFunc函数里在写动画函数,就形成动画回调了。在最后的transit方法回调中,我们把dom的style属性取消掉,这样进行动画就会回归源点。
Animation动画模块,JM的动画实现全部由CSS3实现,有以下几个特征
动画底层由CSS3实现
动画参数采用链式调用动画,非传统的class操作css3动画。
可自由开启GPU的3D动画加速
依赖dom,event,type三个模块。
核心代码
runBtn.onclick = function(){ $D.setStyle(runBtn,"display","none"); rect.style.cssText = ""; //设置动画基本参数,并且每个动画间隔为1秒 J.Animation({ selector:"#demo1_rect", //要操作的dom的selector duration:1000, use3d:true }).setStyle({ //向右运动了60%,并且color变成蓝色 "left":"60%", "background":"blue" }).transit(function(){ this.scale(.5).setStyle({ 'background':"red" }).transit(function(){ this.rotate(180).setStyle({ opacity:0 }).transit(function(){ this.toOrigin().setStyle({ "opacity":1, "background":"green" }).transit(function(){ $D.setStyle(runBtn,"display","block"); }); }); }); });}
J.Animation是这个模块的命名模块,传入一个options的obj来设置动画的基本参数。然后在函数尾部使用setStyle进行回调,setStyle是用于设置动画改变的状态。再在函数尾部调用transit方法表示以CSS3的transition方式进行启动动画。我们依旧在transit里面传入匿名函数,表示一段动画结束后,进行回调函数callback,这样我们就不断进行链式调用和callback回调来完成一段动画。
Animation 传入参数说明selector 动画操作的dom的selector(id)
duration 一段动画执行时间
use3d 是否启用硬件加速
runType 动画类型(ease-in, ease-in-out)
delay 动画延迟时间
scale:function(scale){}
scaleX:function(scaleX){}
scaleY:function(scaleY){}
rotate:function(rotate){}
rotateX:function(rotateX){}
rotateY:function(rotateY){}
rotateZ:function(rotateZ){}
translate:function(translateX,translateY,translateZ){}
translateX:function(translateX){}
translateY:function(translateY){}
skew:function(x,y){}
skewX:function(x){}
skewY:function(y){}
setStyle:function(styleName,styleValue){}
toOrigin:function(){} 动画参数清空
transit:function(onFinished){} 启动动画 onFinished为动画回调函数
大体上动画函数都很好理解,不做过多解析,我们使用链式js的方式来编辑CSS3动画,CSS3的动画参数都可以自个设置,并且动画结束后可自行销毁,不会冗余页面代码样式。
audio(按需加载)在web上播放声音一直体验都不是很好,包括下载声源需要加载时间,而且各种的声音播放方式兼容性差等等。JM的额外模块audio是就是处理web上声音的模块。因为不是所有的web页面都需要声音,所以我们建议需要播放声音的时候才加载这个模块。在HTML5诞 生到来,让web在多媒体领域上不区于flash,原生的控件,让我们的页面播放页面更加简单。但是并不是真正的简单,问题依旧是浏览器间的问题,或者是 各种终端版本的问题,兼容性依旧是个核心问题。我们audio模块就是为了解决播放声音的兼容性及操作的一致性而生,让我们的声音在web上,在真正意义上的简单起来。
依赖dom,event,browser三个模块。
base 核心模块JM的一些核心方法,为所有模块所共用。例如JM的命名方式namespace,类封装Class,继承extend,操作Listfilter``map``some等一系列继承函数。
示例代码
JM.$package("MUI",function(J){ // JM的命名空间});//JM的类继承方式var AnimateTab = J.Class({extend:MUI.Tab},{ init:function(options){ //do something }, _handleEvent:function(e){ //do something }, bindHandlers:function(){ //do something }});base 公开对外基础函数
$namespace: function(name) {}为$package函数内部使用
$package:function(ns,func) {}命名空间
extend:function(destination,source) {} 继承
bind:function(func, context, var_args) {}
Class:function(){} 定义类函数
toArray: function(pseudoArrayObj){}
indexOf:function(arr,elem){}
every:function(arr,callback){}
some:function(arr,callback){}
each:function(arr,callback){}
map:function(arr,callback){}
filter:function(arr,callback){} 过滤List
isEmptyObject:function(obj){}
random : function(min, max){}
本可简单简单地划分到utils模块,但是在移动web上我们检测的不单单是浏览器及其版本后,后继拓展还有,终端平台及其版本,所以我们单独地划分出一个模块出来(其实最终还是会合到一个js里面)。
//对应的值是版本号,如果不是这个浏览器则版本号为0 if(J.browser.plugins.flash>=9){ } if(J.browser.ie){ } if(J.browser.chrome >= 30){ } if(J.adobeAir){ }cookie
非常简单的cookie种植和移除,直接粘贴源码吧
J.$package(function(J){ var domainPrefix = window.location.host; var cookie = { set : function(name, value, domain, path, hour) { if (hour) { var today = new Date(); var expire = new Date(); expire.setTime(today.getTime() + 3600000 * hour); } window.document.cookie = name + "=" + value + "; " + (hour ? ("expires=" + expire.toGMTString() + "; ") : "") + (path ? ("path=" + path + "; ") : "path=/; ") + (domain ? ("domain=" + domain + ";") : ("domain=" + domainPrefix + ";")); return true; }, get : function(name) { var r = new RegExp("(?:^|;+|\s+)" + name + "=([^;]*)"); var m = window.document.cookie.match(r); return (!m ? "" : m[1]); }, remove : function(name, domain, path) { window.document.cookie = name + "=; expires=Mon, 26 Jul 1997 05:00:00 GMT; " + (path ? ("path=" + path + "; ") : "path=/; ") + (domain ? ("domain=" + domain + ";") : ("domain=" + domainPrefix + ";")); } }; J.cookie = cookie;});dom
HTML的dom操作模块。包括选择器,选取样式,修改样式切换Class等方法。虽然跟著名的jquery对比会逊色一筹,但是 基本的功能都能满足,最重要的是jquery有100多K,我们才30多K,我们把最实际常用的Dom操作抽离处理,减少库的代码量,在移动Web上带来 更少的流量。
示例代码
var $D = J.dom;var runBtn = $D.id('runBtn'), runObj = $D.$('#demo_block .rect'), stopBtn = $D.className('demaxiya');runBtn.onclick = function(){ $D.toggleClass(runBtn,'active');}if($D.getStyle(runBtn,'background') == '#000'){ $D.remove(stopBtn);}
除了比较常用的J.dom.$()的选择器方式,我们还提供了J.dom.id()和J.dom.tagName()等 的选择器方式,我们建议,可以的话尽量是用id选择器,因为这是效率最高的。我们没有像Jquery那样吧选择到的Dom元素包装成一个Jquery对 象,我们选择到的是原生的Dom元素,方式也是传入Dom和参数。It's simple,but it do well。
dom 函数列表$:function(selector,context){} 会优先选用原生的
id:function(id){}
tagName:function(tagName,context){}
className:function(className,context){}
remove:function(node){}
setSelectorEngine:function(func){}
toDomStyle:function(cssStyle){}
toCssStyle:function(domStyle){}
setStyle:function(elem ,styleName,styleValue){}
getStyle: function(el, styleName){}
getVendorPropertyName : function(prop) {}
isSupprot3d : function(){}
filterSelector:function(arr,selector){}
addClass function(elem,className){}
removeClass function(elem,className){}
hasClass function(elem,className){}
toggleClass:function(ele,className){}
insertAfter: function(parentElement, newElement, refernceElement){} 类似源生方法 insertBefore
除了平常的支持IE等浏览器兼容外,最重要的是支持移动端的touch,swip等手势事件及transform等移动专有事件.PC端和移动端最大一个 不同除了屏幕分辨率外,还有一个非常重要的就是事件不同。我们不但支持了这些事件,还自定义了手势事和提供了很多辅助方法,帮助开发者的实际开发应用,例 如:getTouchPos``getDist等。在移动web上坑最多的就是event处理的这一块,至少JM会让你少走很多弯路。
示例代码
var $E = J.event;//动画事件结束后$E.on(this.contentWrap,"webkitTransitionEnd",function(e){ // 自身销毁changed事件 $E.fire(self ,"changed" ,{ type:"changed", currentIndex:self.currentIndex });}); var startEvt = isTouchDevice ? "touchstart" : "mousedown"; var moveEvt = isTouchDevice ? "touchmove" : "mousemove"; var endEvt = isTouchDevice ? "touchend" : "mouseup"; //touch事件的绑定$E.on(document.body ,moveEvt ,_handleEvent); $E.on(document.body ,endEvt ,_handleEvent);
注意,这里关于touch或swip事件的例子可以查看JMUI的控件,这样会更加清楚.
event 方法事件列表isDomEvent function(obj,evtType){} 如果是DOM事件,返回正确的事件名;否则返回布尔值 false
bindDomEvent function(obj, evtType, handler){} 封装绑定DOM事件的方法,以兼容低版本IE
unbindDomEvent function(obj, evtType, handler){} 解除绑定DOM事件的方法
on:function(obj, evtType, handler){}
once:function(obj,evtType,handler){}
off:function(obj,evtType,handler){}
fire:function(obj,evtType){}
getActionTarget : function(event, level, property, parent){}
获取点击的事件源, 该事件源是有 cmd 属性的 默认从 event.target 往上找三层,找不到就返回null @param {Event} event
@param {Int}level 指定寻找的层次
@param {String} property 查找具有特定属性的target,默认为cmd
@param {HTMLElement} parent 指定查找结束点, 默认为document.body
@return {HTMLElement} | null
bindCommands : function(targetElement, eventName, commends, commendName){}
@example
bindCommands(cmds);
bindCommands(el, cmds);
bindCommands(el, 'click', cmds);
getTouchPos = function(e){} 辅助函数
getDist = function(p1 , p2){} 计算两点之间距离
getAngle = function(p1 , p2){} 计算两点之间所成角度
_fire
_off
tap 按下松开之间的移动距离小于20,认为发生了tap
hold 按下松开之间的移动距离小于20,认为点击生效swipe按下之后移动30px之后就认为swipe开始,swipe最大经历时间500
transform
scrollstart
scrollend
scrolltobottom
ortchange 兼容性更好的orientationchange事件,这里使用resize实现。不覆盖原生orientation change 和 resize事件
不多说,直接上源码
var date = function(date, formatString){ /* * eg:formatString="YYYY-MM-DD hh:mm:ss"; */ var o = { "M+" : date.getMonth()+1, //month "D+" : date.getDate(), //day "h+" : date.getHours(), //hour "m+" : date.getMinutes(), //minute "s+" : date.getSeconds(), //second "q+" : Math.floor((date.getMonth()+3)/3), //quarter "S" : date.getMilliseconds() //millisecond } if(/(Y+)/.test(formatString)){ formatString = formatString.replace(RegExp.$1, (date.getFullYear()+"").substr(4 - RegExp.$1.length)); } for(var k in o){ if(new RegExp("("+ k +")").test(formatString)){ formatString = formatString.replace(RegExp.$1, RegExp.$1.length==1 ? o[k] : ("00"+ o[k]).substr((""+ o[k]).length)); } } return formatString;};http
http处理模块,一些处理urlparam方法和ajax方法。
示例代码
J.http.ajax({ url: 'http:www.xxx.com/getSomething' method : 'post', timeout : 50000, withCredentials : true, //是否跨域 async : true, //是否异步 onError : function(data){ alert(' ajax error : ' + data.msg); }, onSuccess : function(data){ //do something ... }, onTimeout : function(data){ //do something ... }});http 方法列表
serializeParam : function ( param ) {}
getUrlParam : function ( name ,href ,noDecode ) {}
ajax : function ( option ) {}
offlineSend:function(options) {}
平台检查模块跟browser模块一样,是检测模块,后继优化会把两者这合并。为兼容多终端多版本,平台检查是少不了。
示例代码
if(J.platform.android && J.platform.ieVersion > 4){ //....}if(platform.android){}//其他平台检查//platform.android//platform.iPhone//platform.iPad//platform.iPod//platform.winPhone //platform.IOS//.touchDeviceprefix 前缀辅助模块
提供了集中使用prefixe的场景,dom节点,css,js等。
J.prefix.dom == 'WebKit'J.prefix.lowercase == 'moz'J.prefix.css == '-ms'J.prefix.js == 'webkit'string
提供tmpl 轻量的模板函数。encodeHTML和decodeHTML常用字符编码解析函数和一些URL操作的辅助函数。
template function(str, data){}
encodeHtml function(sStr){}
isURL function(str) {}
parseURL function(str) {}
buildURL function(obj) {}
目前特效检测内容只用audio,fixed,flash和transition。
J.support = { audio : { m4a : 'maybe', mp3 : 'maybe', ogg : 'probably', wav : 'probably' } fixed : true, flash : ture, transitionend : 'transititonend'}type 类型检查辅助模块
isArray : function(o){}
isObject : function(o) {}
isBoolean : function(o) {}
isNumber : function(o) {}
isUndefined : function(o) {}
isNull : function(o) {}
isFunction : function(o) {}
isString : function(o) {}
提供常用的辅助函数,还有提供一些移动上特色需要的辅助函数,例如hideUrlBar隐藏url栏,preventScrolling禁止滚动等。
hideUrlBar:function(){} 隐藏URL栏
preventScrolling : function() {} 禁止滚动
activeScrolling:function(){} 启用滚动
scrollToTop:function(duration,runType){} 滚动到顶部动画(css3动画)
fixElement:function(ele,options){} 兼容浏览器的fixed定位
hoverEffect:function(ele,className){}