Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IOS9.0+中hash值的bug与解决方案(转) #149

Open
mishe opened this issue Dec 27, 2016 · 0 comments
Open

IOS9.0+中hash值的bug与解决方案(转) #149

mishe opened this issue Dec 27, 2016 · 0 comments

Comments

@mishe
Copy link
Owner

mishe commented Dec 27, 2016

事件起因

事情是这样的:产品上线发布,突然出现了问题。运营Gg过来反应, 当场给露珠演示, 运营同事的手机是iphone,bug确实是存在的。奇怪的是露珠用了其他iphone手机(借别人的,露珠的是吊死安卓机),却没有发现这个问题。仔细询问,同事说他最近刚刚升级的ios9,于是问题最初定位在操作系统上。接下来检查代码,发现运行正常,逻辑也没错。问题就卡到这里了。没办法,线上问题,fiddler替换本地脚本调试,一级一级alert,从项目文件到底层库,最后定位到了backbone。查看backbone源码,发现问题结症:_updateHash方法中有改变hash值的方法,location.hash和location.replace两个,不管执行的是哪个,hash值不会立即改变!也就是说早ios9中hash在地址栏中变化有延迟!

backbone的hash跳转机制

一、代码层:在backbone(以下简称bk)中,利用的是route.navigate方法进行路由的跳转,bk框架会根据你传入的hash值,手动替换地址栏中的hash,接着调用loadUrl方法去内存中读取传入的路由配置,执行回掉函数。大概流程类似如下代码:

route.navigate(手动调用跳转)--->_updateHash(更新地址栏中的url)->loadUrl(去内存中读取注册的路由事件)->callback(执行回掉函数);
二、浏览器层:浏览器层是不受代码控制的,但是浏览器提供给了js监听url的事件,现代浏览器中都支持onhashchange事件,当然,这你也可以在start中配置默认采用popstate事件监听,如果是老旧的浏览器,bk采取的是定时器时刷新的监听方式。不管哪一种方式,都是为了检测地址栏中hash值得变化。通过浏览器的回退和前进功能,hash值会跟随变化,而浏览器本身不会刷新,所以,通过各种对url变化事件的监听,可以做到响应自定义的事件。大概流程类似如下的代码:

back/forward(浏览器前进或者后退)------>onhashchange(出发hash值变化)----->checkUrl(执行loadUrl之前检测url,该方法主要应付的事浏览器的行为,代码层则会return)---->loadUrl(通过查找内存找到对应路由的回掉)---->callback(执行自定义回掉函数);
一切都井井有条地进行着,直到ios9的发布,而你们这群有钱人又急着把自己的果机升级。结果是,如果你在回掉函数中需要处理url的话,那么你得到的是旧的url不会是最新的。

补救和兼容

为了查找结症,露珠在百度无果后google到了同样在这方面遇到的问题的同行。没想到为了这个bug,老外在github上炸开了锅,各种吐槽ios9,不管是bk或者anglar都存的开发者都遇到了这个hash改变延迟的问题。然后还有的大神给出了解决的方案(链接在文章末尾中露珠会发出来)。同时卤煮为了验证,自己在ios9真机上调试了。以下是露珠的代码调试和输出的结果,问题都出在_updateHash这个方法上面:

IOS9.0中hash值的bug与解决方案

为了解决延迟问题,我们的方案当然也是延迟。通过测试,hash值的变化大概在ios9下游9~30ms(一次一次地alert结果,不太准确),所以,我们可以将回掉函数延迟>30ms时间执行。我们把loadUrl延迟了大概50ms,50ms对于用户来说是一个很短的过程,所以,对于那些没有出毛病的系统来说也是可以接受的。于是,我们把源码(1599行左右)改成一下面这样的:

if (options.trigger) {
var self = this;//this存入全局变量中
setTimeout(function(){//延迟50ms执行
return self.loadUrl(fragment);
}, 50);
}

通过检测,通过了各个系统的测试。目前还是比较稳定。

结语

露珠坚持不动框架源码的原则,不在万不得已的情况下坚决不动框架源码。但是一旦出现了类似系统的bug,为了修复,修改源码也是一种无奈。苹果这种大公司,一个小bug可能牵动的是全球成千上万的开发者。希望苹果公司早日修复此bug。而不是要我们这些开发者写兼容。同时也希望露珠的博客对于正在开发bk或者其他单页面应用的读者诸君有些许的帮助。

参考文档

ionic-team/ionic-framework@e5b85df
angular/angular.js#12241

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant