郑昀编纂 关键词:Hybrid,Zepto,Fastclick,Backbone,sui,SPA,pushState,跨域,CORS
- click 事件仍是 tap 事件?
- Zepto 的 show/hide 有时不靠谱
- Android 下的跨域问题
- pushState 调用失败也属于跨域问题
内部作 Hybrid App 开发历程也不短了,杨海波、潘军和刘勤红与前端组、App 组一块儿总结了很多宝贵的经验教训,逐步造成咱们本身的标准打法。下面选了几个经验点(坑)作进一步解读。
0x00,click 事件仍是 tap 事件?
如
300 毫秒点击延迟的前因后果一文所言『尽管苹果公司创造的双击缩放行为,是一种在移动设备上访问桌面端站点的不错的解决方案,但随之引入的 300 毫秒点击延迟也成为了移动端网站让用户感受卡顿的罪魁祸首之一』,此文把缘由和对策讲得清楚透彻,此处郑昀再也不赘述,感兴趣的同窗自习一下。
那么,咱们看到,时至今日,有三种应对措施:
- 对于无需缩放的页面,禁用双击缩放功能,
- 引入 Zepto 框架的 tap 事件,
- Zepto 趁着 JQuery 在移动互联网市场历史包袱重的机会,搞了一套轻量级类 JQuery 框架的代码,核心代码千行,从而迅速成为移动端 DOM 操做库的首选。
- Zepto 提供了一个 touch 库,在载入 Zepto 后为 document 绑定 touchstart、touchmove、touchend 事件,根据手指 x、y 值的位置,判断方向从而触发 tap、doubleTap、swipeLeft 等事件。
- 本来没有 tap 事件,Zepto tap 是用 touchstart 和 touchend 模拟出来的。
- 引入 Fastclick 的 click 事件,
- FastClick 是 FT Labs 专门为解决移动端浏览器 300 毫秒点击延迟问题所开发的一个轻量级的库。简而言之,FastClick 在检测到
touchend
事件的时候,会经过 DOM 自定义事件当即触发一个模拟click
事件,并把浏览器在 300 毫秒以后真正触发的 click
事件阻止掉。
但咱们在实践过程当中,也领教了著名的
Zepto tap 『点透』坑。
『
1,一旦引入 touch 库便会在全局绑定事件,每次点击皆会触发无心义的 tap 事件 ;
2,zepto 为了实现 doubleTap 等功能,2B 地在 touchend 上设置了一个 setTimeout,而后整个世界都充满了翔了。
因为 setTimeout 的抛出主干流程,致使其 event 参数失效,这个时候就算在 tap 事件函数中执行 e.preventDefault() 或者什么都是无效的,这是致使 zepto tap“点透”的罪魁祸首。
』
Fastclick 将事件绑定到你传入的元素 上,在 touchstart 和 touchend 后(会手动获取当前点击 el),若是是类 click 事件便手动触发 dom 元素的 click 事件。因此 click 事件在 touchend 便被触发,因而整个响应速度就起来了。这里虽然使用了 touch 事件,但 touch 事件是绑定到了具体 dom 而不是 document 上 ,因此 e.preventDefault 是有效的,咱们能够阻止冒泡,也能够阻止浏览器默认事件。
最终咱们采用 Fastclick 的 click 事件来规避点击延迟响应。
0x01,Zepto 的 show/hide 有时不靠谱
在 Hybrid App 开发初期,咱们引入了 Zepto 的 fx_methods 模块,来控制元素的显示与隐藏:
fx_methods |
|
Animated show , hide , toggle , and fade*() methods. |
但老是出现一些莫名奇妙的问题,如元素不能正常显示,元素的定位发生变化等,很是很差定位,最终前端同窗发现是 Zepto 的 show/hide 形成的。
对此,咱们能够找到 2012 年的一个 issue 佐证:
|
When you add fx_methods to your Zepto, the show() and hide() methods are overriden with methods that also call animate.
Even in the case of undefined speed (normal show), animate still adds many CSS properties to the shown/hidden nodes.
These unexpected CSS rules can interfere with the page CSS (in my case, it has changed the positioning of my child fixed DIVs).
|
即,她认为,即便是简单的显示,fx_methods.js 的 show/hide 也会加入一些额外的 CSS,以致于影响整个页面样式,并且还会乱弹琴地改变透明度,重置为 1.0。samwu 在 2013 年也
讲过相似问题。
因此,咱们有两种选择:
- 继续引入 Zepto 的 fx_methods,但按 madrobby 所说,没有定义 speed 时,仅调用原生的 show;
- 如 0x00 和本小节所描述的问题,不要引入 Zepto 的 touch 和 fx_methods 模块。
- 只引入 zepto+event+ajax+form+ie 基本模块
0x02,Android 下的跨域问题
在 Hyrbid App 里,郑昀要求采用 Template+Data=HTML 模式在手机客户端本地渲染出所需的页面。
Template(HTML5 模板文件+JS+CSS+Images)既能够提早打包到 App 安装包里,也能够从 CDN(如无,则溯源到静态文件服务器)拉到客户端里并存储在本地。
Data 则是经过模板文件里的 JavaScript 脚本,从远端拉取 JSON 格式的数据包。
那么,这里存在一个跨域问题:
- Android 的 WebView 经过 file:// 协议加载本地的 HTML5 模板文件。本地文件的 JS 向远端发送 AJAX 请求,受到同源策略的限制,譬如你可能会看到“XMLHttpRequest cannot load file://…… Origin null is not allowed by Access-Control-Allow-Origin.”的错误提示,即当你的 URI 是 file:// 时,源域名(origin)为空。
- 借用 difcareer 的评论:Web 应用程序能且只能使用 XMLHttpRequest 对象向其加载的源域名发起请求,而不能向任何其余域名发起请求,但 HTML5 容许 AJAX 跨域向其余域名发起请求,可是不能获取服务器端响应。
- iOS 下无此跨域问题。
对此,大体有四种应对措施:
- 若是是 GET 请求:
- 能够经过 JSONP 来解决跨域问题;
- 若是是 POST 请求:
- 由 App 原生代码发起网络操做,成功后回调 JS。
- 利用 HTML5 的“Cross-Origin Resource Sharing(CORS,跨域资源共享)”新特性,在服务器端响应头里设置:Access-Control-Allow-Origin 头域;
- 不限制来源,Access-Control-Allow-Origin: *
- 限制来源域,Access-Control-Allow-Origin: http://xxx.com
- 具体设置参考 http://enable-cors.org/;
- 这须要浏览器支持;
- SmdCn 说起,『在某些版本的 Android 浏览器中,由于缓存的缘由,第一次 CORS 正常,但第二次会失效。对此,能够经过在响应头中增长 Cache-Control: no-cache 来解决』。
-
设置 Android 的 WebView 以下 settings 参数为 True:html
if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.JELLY_BEAN){前端
webView.getSettings().setAllowUniversalAccessFromFileURLs(true);node
}git
由此容许经过
file url 加载的 Javascript 能够访问其余的源,包括其余的文件和 http/https 等源。这个设置在 JELLY_BEAN 之前的版本默认是容许,在 JELLY_BEAN 及之后的版本中默认是禁止的。
According to the documentation for setAllowFileAccessFromFileURLs and setAllowUniversalAccessFromFileURLs, they used to be set to true for ICS and older. Starting in JellyBean they turned it off. If you are trying to load a local JS file from a local HTML page, then you'll need to enable these.
– Steven
咱们是既会在 Native API 里封装了一个接口 sendpostmsg,让 JS 调用(但也所以致使『
除了 POST 请求,其余均可以在 PC 浏览器调试』),也会使用 setAllowUniversalAccessFromFileURLs 方法。
0x03,pushState 调用失败也属于跨域问题
咱们须要使用 HTML5 的特性 history.pushState,手动插入历史记录和修改地址栏,这样虽然地址栏被修改了,但并不触发网页跳转。
同上节,本地文件的 JS 调用 history.pushState 也会遇到跨域问题,报错以下图所示:
缘由还是当你的 URI 是 file:// 时,源域名(origin)为空。
因为咱们使用了
SUI,绕不开这个问题。因此,最终仍是得调用 webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
完全解决各类跨域问题。
参考资源:
6,2011,sof,
WebView Javascript cross domain from a local HTML file;
欢迎订阅个人微信订阅号『老兵笔记』,请扫描二维码关注:
转载时请注明“转载自旁观者-博客园”或者给出本文的原始连接。
-EOF-