【小貼士】【stringify神BUG】【localstorage失效】【消滅Safari alert框】【是否延遲載入】【頁面10px白屏】

葉小釵發表於2014-07-21

前言

最近碰到幾個噁心問題,也發現一點優化技巧,以及對Hybrid知識的一些整理,這裡便一併拿出來做分享了,關於Hybrid的除錯,會是我今後一個重點
我的部落格首先是學習筆記,方便自己做知識沉澱,以後好查閱,其次才是分享,所以其中有誤請提出,覺得亂是很有可能的~~~~~~
我們在工作中一般會有這麼一個流程:發現問題->定位問題->解決問題
其中最難就是定位問題,有時候我們會花上幾天時間定位問題,而解決問題卻只需要幾秒......
所以定位問題的能力非常重要,這也是經驗的體現,所謂高手其實就是坑踩得多而又善於總結罷了
我這裡首先分享一個坑爹的問題,然後由此問題展開今天的學習

JSON.stringify出BUG啦!

沒錯,這個我最近碰到最為噁心的一個問題!!!
所有頁面,本來在手機上好好的,就算所有手機、奇葩機都測試通過了,最後Hybrid聯調時總會出兩個莫名其妙的BUG
PS:不知道Hybrid各位聽過麼?沒有聽過的前端需要好好補補課了,最近2年這個可能是一大趨勢,在與Hybrid的戰鬥中,Hybrid表現出了移動開發萬惡之源的應有素質,配合索尼小米三星組成了一個難以逾越的障礙牆(UC我們就不提了)
問題的現象是,一個伺服器下發的資料物件被存到了localstorage中,拿出來後其中的小數變成null了
該問題暫時發現發生於Hybrid 三星S3 Sony L39H中,應該還有不少其它低端機型有問題。
幾經定位發現現象如下:
var testObject1 = {
  OrderAmount: 0.11
};
JSON.stringify(testObject1) => OrderAmount: null
① 進頁面便觸發這段程式碼不會出問題~~
② click事件中執行上面程式碼在60%的概率中便中招了!
最初我當然不相信原生的JSON.stringify會出問題,便反覆測試,反覆定位,最後頁面的程式碼刪到只有幾行的時候,我不得不承認是他出了問題~~~~~~Hybrid就是讓你料想不到
一旦定位問題後,這裡的解決方案也便出來了:
在Hybrid中判斷useAgent,重寫掉JSON.stringify的邏輯即可,這裡貼一段參考程式碼:
var json2 = {
  type: function (obj) {
    if (obj == null) return String(obj);
    var h = { '[object Boolean]': 'boolean', '[object Number]': 'number', '[object String]': 'string', '[object Function]': 'function', '[object Array]': 'array', '[object Date]': 'date', '[object RegExp]': 'regexp', '[object Error]': 'error' };
    var t = Object.prototype.toString.call(obj);
    if (t in h) return h[t];
    if (t == '[object Object]') t = obj + '';
    var arr = t.match(/^\[object (HTML\w+)\]$/);
    if (arr) return arr[1];
    return 'object';
  },
  stringifyJSON: function (obj) {
    var str, t = window.JSON;
    var rstringifyJSON = /([\n\r\f\\\/\'\"])/g;
    var arr = [], i = 0, n, p;
    var stringHash = {
      '\n': '\\n',
      '\r': '\\r',
      '\f': '\\f'
    };
    switch (json2.type(obj)) {
      case null:
        str = 'null';
        break;
      case 'undefined':
        str = 'undefined';
        break;
      case 'object':
        for (p in obj) {
          if (obj.hasOwnProperty(p)) {
            arr[i++] = json2.stringifyJSON(p) + ':' + json2.stringifyJSON(obj[p]);
          }
        }
        str = '{' + arr.join(',') + '}';
        break;
      case 'array':
        for (i = 0, n = obj.length; i < n; i++) {
          arr[i] = json2.stringifyJSON(obj[i]);
        }
        str = '[' + arr.join(',') + ']';
        break;
      case 'string':
        str = '\"' + obj.replace(rstringifyJSON, function (a) {
          return stringHash[a] || '\\' + a;
        }) + '\"';
        break;
      case 'date':
        str = 'new Date(' + obj.getTime() + ')';
        break;
      case 'number':
      case 'boolean':
      case 'function':
      case 'regexp':
        str = obj.toString();
        break;
      default:
        str = 'null';
    }
    return str;
  }
};

JSON.stringify = json2.stringifyJSON; 
View Code

當然,我這裡其實挖掘的不夠徹底,我只是定位到了JSON.stringify有問題,卻不能再定位裡面哪個環節有問題了......

更加優雅的做法:

var stringifyFunc = JSON.stringify
JSON.stringify = function () {
  if (arguments.length == 1) {
    return stringifyFunc.call(this, arguments[0], function (k, v) {
      if (!isNaN(v)) return v + '';
      else return v;
    })
  }
  else {
    stringifyFunc.apply(this, arguments);
  }
}

localstorage讀取失效

上面說到了localstorage,這裡正好將它拿出來說下,首先有幾個必須要牢記的規則

① localstorage最大字元為500多萬(5M)
各個手機有所差異,但是不會太大,所以使用localstorage一定要記得清理,不清理可能導致
讀取localstorage效率下降,localstorage滿了會引發業務邏輯錯誤
② localstorage讀取檔案的
所以其效能沒有記憶體讀取快,firefox更是會一次性將資料匯入記憶體,想想就覺得嚇人啊
③ localstorage不被爬蟲識別,所以與SEO相關的關鍵資訊需要避免使用localstorage,否則後續會被坑死
上面說了幾個localstorage需要注意的地方,事實上localstorage對效能提升還是有一些作用的
儲存不太重要的資料,比如城市資訊;存取1分鐘內有用的資料也是可以減少請求的
但是在android Hybrid中有一個神奇的後退按鈕,此按鈕一旦按下會回到上一個頁面,這個時候裡面的localstorage可能會讀取失效!!!一個簡單不靠譜的解決方案是在webapp中加入:
window.onunload = function () { };//不要問我為什麼,我也不知道!

最後在開啟隱私模式下時,safari的localstorage讀寫是不可用的,但是qq瀏覽器卻可以,至於原因我就不知道了......

消除連結失效時safari alert框

該問題的使用場景首先出現在這裡:
導致alert框的出現的原因是我點選了一個無效連結,這個時候Safari便會彈框提示
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title></title>
  <meta name="viewport" content="width=320.1, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no,minimal-ui">
</head>
<body>
<a href="http://www.baidu.com">百度一下</a>
<a href="taobao://wireless">測試無效URL</a>
</body>
</html>

前段時間,小釵的一個同事找到了解決方案,大致做法如下:
 1 <html xmlns="http://www.w3.org/1999/xhtml">
 2 <head>
 3   <title></title>
 4   <meta name="viewport" content="width=320.1, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no,minimal-ui">
 5   <script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script>
 6 </head>
 7 <body>
 8   <a href="http://www.baidu.com">百度一下</a> <a href="taobao://wireless">測試無效URL</a>
 9   <script type="text/javascript">
10     $('a').click(function (e) {
11       var el = $(e.target);
12       var url = el.attr('href');
13       if (url.indexOf('taobao') != -1) {
14         var ifm = document.createElement('iframe');
15         ifm.onload = function () {
16           ifm.contentWindow.document.write('<script>location.href="' + url + '"</' + '' + 'script>');
17         }
18         ifm.src = 'about:blank';
19         document.body.appendChild(ifm);
20       } else {
21         window.location = url;
22       }
23       e.preventDefault();
24     });
25   </script>
26 </body>
27 </html>
View Code

核心程式碼在此:

 1     $('a').click(function (e) {
 2       var el = $(e.target);
 3       var url = el.attr('href');
 4       if (url.indexOf('taobao') != -1) {
 5         var ifm = document.createElement('iframe');
 6         ifm.onload = function () {
 7           ifm.contentWindow.document.write('<script>location.href="' + url + '"</' + '' + 'script>');
 8         }
 9         ifm.src = 'about:blank';
10         document.body.appendChild(ifm);
11       } else {
12         window.location = url;
13       }
14       e.preventDefault();

其原理就是iframe中url解析錯誤的話,Safari不太理睬~~~~~~

延遲載入·效能與體驗

以延遲載入來說,最常見的便是圖片延遲載入,但是很多朋友卻不知道為什麼要做延遲載入
瀏覽器能同時請求的併發數是有限的,對於手機來說一般是4-6之間,不同型號的手機或者瀏覽器會有所不同,差距不會太大
這個請求數限制存於瀏覽器,所以一處請求卡死,就算新開標籤也會受到影響(手機一般不考慮tab),下面有一個場景:
一個頁面開啟,裡面有N多圖片,並且有幾個js待載入,這個時候若是圖片先載入的話,圖片會佔用js的併發數,從而阻塞頁面的載入~~~~~~
舉個webapp的例子,我們進一個列表頁,載入了15個圖片,使用者點選列表項booking頁模組js開始載入(requireJS規則),這個時候業務js需要等待前面圖片載入結束後才能載入,至少需要空閒併發數
所以,圖片是有可能堵塞JS的,這個也是我們做圖片延遲載入的主要原因

首屏載入速度

延遲載入是提升首頁載入速度的一大手段,對於webapp來說,操作會有所不同
webapp中一個個業務view都是一個獨立的js檔案,我們能控制第二個view在首頁是否載入
或者說,頁面中用到的元件,我們皆可以按需載入,但這裡就有一個情況需要取捨
首屏快,操作慢VS首屏慢操作快
說得多不如親身操作:
  
第一個便是首屏快的程式碼,其它元件全部採用按需載入的手段,但是事實上這類做法會導致後續操作十分卡!!因為每一個操作可能引發一次請求!
第二個便是首屏將UI與View業務程式碼全部打包一起了,這樣首屏載入會比較慢,他的效果時後續操作的無縫性
當然,是否需要將js全部打包,這會是一場口水戰,直接有一個閥值,有一個區間,只要做到這個區間便好

統計程式碼導致10px白屏

很多大型網站都會具有統計程式碼,而此類統計程式碼一般是以img做請求發出,但是他可以導致10px白屏你知道嗎?

會出現10px左右的白屏區域,這個問題導致的原因是:
獨立的inline元素出現時,會為他建立一個line boxes,這個就是傳說中的文字框
一行文字有一個line boxes,line boxes的高度由line-height控制而不是行內元素的width height控制
所以,img的高度與line boxes沒有關係
下面那一行白屏空間其實就是一個匿名line boxes,這個時候給body設定line-height他便會消失,或者讓img脫離文件流即可

結語

依舊這句話,問題的定位才是難點,若能定位一個問題,其解決方案往往是分分鐘的事情......
這裡記錄這些奇怪的知識點,以便今後查閱,也希望對各位有所幫助!

相關文章