中铁二局第六工程有限公司
摘要:为了降低开发复杂度和减少前端开发工作量,屏蔽移动端和PC桌面端浏览器的细微差异,在一个开发框架中,构造一个通用底层结构,在DOM渲染、网络请求、鼠标按键及触屏响应等多个方面,分别支持各个常用浏览器,对外提供统一的API接口,基于这些API的业务逻辑代码,即可运行于各个浏览器,并得到足够的一致性,从而降低开发复杂度和减少开发工作量。
关键词:互联网前端应用,一致性,接口
Abstact:In order to reduce development complexity,the workload of front-end development and to shield the subtle differences between the mobile client and PC desktop browsers client,a generic underlying structure is constructed to support the common browsers in many aspects such as DOM rendering,network requests,mouse events and touch screen response in a development framework.The business logic code based on the unified API interface can run in various browsers and get enough consistency to reduce development complexity and development workload.
Keywords:Internet front-end application,Consistency,Interface
前言
随着互联网+、云计算的普及,互联网前端应用(以下简称为:前端应用)越来越多,重要性也越来越受到重视。然而,在HTML5(以下简称为:H5)尚未普及的情况下,各个浏览器的兼容性是需要专门处理的。比如,IE8仍然是最为主要的浏览器(【1】),即使是IE11,其渲染内核也与Firefox、Chrome等浏览器内核不同;此外,各个浏览器对H5和CSS3的支持也不全面,存在一些细微区别。这就要求一款前端应用需要针对不同的浏览器开发独立的版本,导致开发复杂度提高,工作量增加。
为了较好地降低开发复杂度和减少开发工作量,本文尝试通过前端应用一致性来解决这个问题。
一.一致性概念
一致性(Consistency),是指应用在各个浏览器中的表现一致、操作一致、结果一致。
鉴于各个浏览器的特性,应用做到完全一致性,是有一定难度的;但是,做到大致的一致性,是可以实现的。
为此,本文提出了一个解决方案:构造一个通用底层结构,在DOM渲染、网络请求、鼠标按键及触屏响应等多个方面,分别支持各个常用浏览器,对外提供统一的API接口,实现只需要编写一套基于这些API的业务逻辑代码,即可运行于各个浏览器,并得到足够的一致性,从而降低开发复杂度和减少开发工作量。
这个实现方案如下图所示:
图1 通用底层结构图示
此外,由于一致性好,开发出来的具体应用更容易得到用户的认可,比如:
●不需要让用户重新学习。
●不会误导用户。
在最初接触应用程序时,用户会通过一些教程或仅仅通过仔细地浏览学习来熟悉这款软件。这就是在相似的平台中鼓励更直观的可用性操作。当然,手势和导航控件也各不相同,但是桌面端和移动端的信息框架和架构框架应该是相似的。缺乏一致性将可能严重影响用户的体验。
二.一致性方案及实现
前端应用一致性涉及多个方面,本节将阐述具体的实现方案。
构造一个通用底层结构,是实现浏览器一致性的关键。由图1可知,这个通用底层结构大致包含如下功能:DOM渲染(分为盒子模型、响应式布局、动画三个方面)、网络请求、鼠标按键及触屏。
1.盒子模型
不同浏览器对于盒子模型(Box Model)(【2】)有两种解释,分别为:content-box 和 border-box。两者的区别是:盒子的宽度是否包含边框(border)和内边距(padding)。
content-box,也称为W3C标准盒模型,即CSS样式声明的高度和宽度是 content 的尺寸,不包含padding 和 border,如下图所示:
图2:content-box 图示
border-box,也称为IE盒子模型,即即CSS样式声明的高度和宽度是整体的尺寸,包含了padding 和 border,如下图所示:
图3:border-box 图示
显然,这两种模式差别很大。会直接导致同一个 css 申明,在不同浏览器中显示结果不同。
为了解决这个问题,css3 标准加入了 box-sizing 这个属性(【3】)。通过显式地设置来明确地让浏览器采用一种盒子模型进行DOM渲染。
一般地,border-box 比 content-box 更为符合人们的常识,也更为友好。
所以,对于具体的应用,我们要求前端应用在设计时,对每一个具有静态大小要求的 Element 显式地设置 box-sizing 为 border-box 值。这样处理还有一个好处,就是同时适用于各个浏览器,无需单独针对一个浏览器再做处理。
2.响应式布局
现代的显示器分辨率非常多样,从移动端的320*480到桌面端的1920*1080,多达数十种。为了适应在不同的分辨率下均能得道满意的显示效果,我们专门提供了一整套具有响应式功能的布局(Layout)API接口,主要有onResize、onDock两个。
一.1.1.onResize
onResize接口主要实现切换分辨率、显示缩放、屏幕旋转等事件响应。
除去低于IE8的各个浏览器在切换分辨率、显示缩放、屏幕旋转时,均会向window发出resize事件,我们只需要响应这个resize事件,即可调用onResize接口,示例代码如下:
var oldBox = getWindowBox();
Viewport._rlh = on(window,"resize",function(){
var newBox = getWindowBox();
if(oldBox.h === newBox.h && oldBox.w === newBox.w){
return;
}
oldBox = newBox;
Viewport.emit("resize");
});
针对低于IE8的浏览器,需要单独处理。我们通过一个定时循环来检测,示例代码如下:
if(has("ie")<= 8){
var deviceXDPI = screen.deviceXDPI;
Viewport._ie8ScreenSizeHandle = setInterval(function(){
if(screen.deviceXDPI !== deviceXDPI){
deviceXDPI = screen.deviceXDPI;
Viewport.emit("resize");
}
},500);
}
通过这两项处理,我们可以完善地获取到显示模式的改变,并及时地做出相应的响应。
一.1.2.onDock
onDock主要用在布局控件(Layout Widget)中。当布局控件接收到onResize调用时,会检测当前的DOM层级显示模式,根据其父节点的显示模式的变化而产生onDock,从而实现响应式布局。示例代码如下:
onDock:function(parentBox){
//parentBox 为父节点的 box
if(parentBox.w * 2 > parentBox.h * 3){//判断父节点是否为横向显示
//如果父节点是横向显示,则向右停靠,即 setDockRegionRight
if(this._dockRegion !== "right"){
this.setDockRegionRight();
}
}else{
//如果父节点是纵向显示,则向底部停靠,即 setDockRegionBottom
if(this._dockRegion !== "bottom"){
this.setDockRegionBottom();
}
}
}
通过对onResize和onDock的综合处理,我们的前端应用即可做出完善的自适应布局响应。
3.动画
CSS3里面新增了transition 属性(【4】),通过这个属性,可以较好地实现动画功能。
然而,并不是所有的浏览器都能够很好地支持transition 属性,所以我们专门编写了动画API接口,自动判断浏览器是否能够使用transition 属性。当浏览器能够较好地使用transition 属性时,则直接通过transition 属性产生动画,否则就通过接口模拟产生相应的动画。
所谓模拟产生动画,即是接口通过一个定时器来模拟transition 属性,生成一系列动画帧来实现动画。示例代码如下:
_clearTimer:function(){
clearTimeout(this._delayTimer);
delete this._delayTimer;
},
_startTimer:function(){
//启动定时器
var self = this;
if(!self._timer){
self._timer = after(runner,"run",function(){
self._cycle();//动画处理
},true);
countor++;
}
if(!timer){
timer = setInterval(function(){
runner.run();//
},self.rate);
}
},
_stopTimer:function(){
//停止定时器
if(this._timer){
this._timer.remove();
this._timer = null;
countor--;
}
if(countor <= 0){
clearInterval(timer);
timer = null;
countor = 0;
}
},
_cycle:function(){
//动画处理
var self = this,
curr,step;
if(self._active){
curr = new Date().valueOf();
step = self.duration === 0 ? 1:(curr - self._startTime)/(self.duration);//计算动画帧曲线值
if(step >= 1){
step = 1;
}
self._percent = step;
self._fire("_onAnimate",[self._getCurrentValue()]);//动画帧处理
if(self._percent < 1){
self._startTimer();
}else{
self._fire("_onEnd",[]);
if(!self.repeat){
self._stopTimer();
}
}
}
return self;
}
由上述代码可知:_onAnimate为实际的动画帧处理方法,具体的前端应用,只需要实现 _onAnimate方法即可完全地模拟transition 属性,从而很好地实现了各个浏览器的动画一致性。
4.网络请求
各个浏览器的网络请求(xhr)也是有差异的,我们为此构建了一个xhr 对象,统一处理各个浏览器的网络请求。
首先,针对各个浏览器初始化 xhr 对象,示例代码如下:
if(has('native-xhr')){
//如果浏览器支持 XMLHttpRequest,则直接使用 XMLHttpRequest
xhr._create = function(){
return new XMLHttpRequest();
};
}else if(has('activex')){
//如果浏览器支持activex,则分别尝试Msxml2.XMLHTTP或Microsoft.XMLHTTP
try{
new ActiveXObject('Msxml2.XMLHTTP');
xhr._create = function(){
return new ActiveXObject('Msxml2.XMLHTTP');
};
}catch(e){
try{
new ActiveXObject('Microsoft.XMLHTTP');
xhr._create = function(){
return new ActiveXObject('Microsoft.XMLHTTP');
};
}catch(e){}
}
}
接下来,则是实现 xhr 的四种请求模式,即:GET、POST、PUT、DELETE,并统一接口API,对外提供为四个API接口:
xhr.get = function(url,options){};
xhr.post = function(url,options){};
xhr.put = function(url,options){};
xhr.del = function(url,options){};
具体的前端应用都可以通过这四个API无差别的实现网络请求,从而保证了浏览器一致性。
5.鼠标按键及触屏
各个浏览器的鼠标和按键响应没有差异,但是对触屏的响应则略有差异;此外,如果能够将触屏和鼠标按键响应统一起来,则能够得到更好的一致性。
各个浏览器对触屏的响应差异主要体现为事件名的不同,IE系列浏览器以MSPointer作为事件名,其他浏览器以pointer作为事件名。这个比较好处理,示例代码如下:
function pointerName(type){
return has("MSPointer")?
"MSPointer" + type.charAt(0).toUpperCase()+ type.slice(1):
"pointer" + type;
}
为了能够将触屏和鼠标按键响应统一起来,我们构建了一个 Touch 对象,示例代码如下:
Touch = {
click:function(node,listener){
//键鼠的click和触屏的click一样处理
return on(node,"click",listener);
},
dblclick:function(node,listener){
//通过判断两次click的时间间隔,来模拟dblclick
return on(node,"click",function(evt){
var now =(new Date()).getTime();
if(now - _lastClick < 500){
listener(evt);
}
_lastClick = now;
});
},
press:function(node,listener){
//通过mousedown和keydown来模拟press
var touchListener = on(node,touch.press,function(evt){
if(evt.type === "mousedown" && !mouse.isLeft(evt)){
//忽略右键click
return;
}
listener(evt);
}),keyListener = on(node,"keydown",function(evt){
if(evt.keyCode === keys.ENTER || evt.keyCode === keys.SPACE){
listener(evt);
}
});
return {
remove:function(){
touchListener.remove();
keyListener.remove();
}
};
},
release:function(node,listener){
//通过mouseup和keyup来模拟release
var touchListener = on(node,touch.release,function(evt){
if(evt.type === "mouseup" && !mouse.isLeft(evt)){
//忽略右键click
return;
}
listener(evt);
}),keyListener = on(node,"keyup",function(evt){
if(evt.keyCode === keys.ENTER || evt.keyCode === keys.SPACE){
listener(evt);
}
});
return {
remove:function(){
touchListener.remove();
keyListener.remove();
}
};
},
move:touch.move,
cancel:touch.cancel,
over:touch.over,
out:touch.out,
enter:touch.enter,
leave:touch.leave,
tap:gesture.tap,
hold:gesture.tap.hold,
doubletap:gesture.tap.doubletap,
swipe:gesture.swipe,
swipeend:gesture.swipe.end
};
如此处理后,具体前端应用中,只需要调用Touch即可统一响应鼠标按键和触屏的事件;即只需要编写一个事件响应方法,就可以无差别地响应鼠标按键或触屏,保证了应用的操作一致性。比如:
on(Touch.press,function(evt){
evt.preventDefault();
//这里是具体的处理代码
});
通过上面这段代码,即可以响应鼠标的mousedown,也可以响应按键的keydown,以及触屏的press。
6.桌面端与移动端一致性
实现了前面几项一致性功能后,我们不仅能够保证桌面端的浏览器一致性,甚至可以保证桌面端和移动端的浏览器一致性。
下面的图4和图5分别展示了桌面Chrome浏览器显示效果和Android默认浏览器显示效果。从这两幅图示可以看出,浏览器一致性相当满意。
图4:桌面 Chrome 浏览器显示效果图示
图5:Android默认浏览器显示效果图示
三.总结
本文提出了浏览器一致性概念,并阐述了浏览器一致性的解决方案,即:构造一个通用底层结构,在DOM渲染(分为盒子模型、响应式布局、动画三个方面)、网络请求、鼠标按键及触屏响应等多个方面,分别支持各个常用浏览器,对外提供统一的API接口,实现只需要编写一套基于这些API的业务逻辑代码,即可运行于各个浏览器,并得到足够的一致性,从而降低开发复杂度和减少开发工作量。
此解决方案,经过实际的前端应用,不仅实现了桌面端各个浏览器一致性,还实现了桌面端浏览器和移动端浏览器一致性,并获得了满意效果。
参考资料:
【1】:2016年网页浏览器发展现状分析
【2】:MDN Web Docs盒模型
【3】:W3school CSS参考手册CSS3 box-sizing 属性
【4】:W3school CSS参考手册CSS3 transition 属性
论文作者:马海
论文发表刊物:《基层建设》2018年第16期
论文发表时间:2018/7/18
标签:浏览器论文; 鼠标论文; 动画论文; 按键论文; 代码论文; 接口论文; 属性论文; 《基层建设》2018年第16期论文;