事件起因
  事情是這樣的:產品上線發布,突然出現了問題。運營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這個方法上面:

  為了解決延遲問題,我們的方案當然也是延遲。通過測試,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或者其他單頁面應用的讀者諸君有些許的幫助。