Zepto 源碼分析 2 - Polyfill 設計

在進入 Zepto Core 模塊代碼以前,本節簡略列舉 Zepto 及其餘開源庫中一些 Polyfill 的設計思路與實現技巧。css

涉及模塊:IE/IOS 3/Detect.前端

IE 模塊 / CSSOM 相關 Polyfill

Zepto 的 IE 模塊 src/ie.js 中僅僅包含了一個兼容性降級邏輯,雖簡單其實現也值得學習:react

(function() {
  try {
    getComputedStyle(undefined);
  } catch (e) {
    var nativeGetComputedStyle = getComputedStyle;
    window.getComputedStyle = function(element, pseudoElement) {
      try {
        return nativeGetComputedStyle(element, pseudoElement);
      } catch (e) {
        return null;
      }
    };
  }
})();

低版本兼容模式(以 IE 7 爲例)調用 getComputedStyle 會出現找不到該方法的問題),已經在高版本 IE 得到支持jquery

The value of the property 'getComputedStyle' is null or undefined, not a Function object

顧名思義該方法用於得到元素的動態計算屬性,此處在 windows 對象上顯式掛載包含 Failover 的 getComputedStyle 方法,使得該方法不存在時的調用代碼仍可繼續運行,不行成阻塞。更詳細的多瀏覽器兼容方案能夠經過閱讀 jQuery css API 源碼找到。ios

此模塊包含的設計思路即爲Failover 預 catch以匹配降級方案git

從該問題中能夠引伸出一個常見問題,CSSOM 的瀏覽器支持程度遠遠低於 DOM 的支持程度,W3C 對於 Document Object Model (DOM) Level 2 Style Specification 的聲明早已於 2000 年底時刻完成,然而 CSSOM 的官方標準 CSS Object Model (CSSOM) 因爲 CSS 3 多管道演進的實現方式影響,仍未推出廠商公認的實際標準,所以對於 CSSOM 的操做設計與跨瀏覽器兼容性測試,jQuery 仍有極好的參考價值。同時,開源社區中也存在大量的 Polyfill(膩子腳本)用於對低版本瀏覽器經過 JavaScript 附加邏輯的方式附加較新潮的特性,能夠在 Modernizr/Modernizr 相似的代碼源中找到。閱讀 Polyfill 每每能夠得到對 原型鏈和 JS 面向對象設計思惟的更深入認識,以及更深層次的設計技巧,以以下的一個 IE 8 opacity 屬性的 Polyfill 函數爲例,完成該函數的技巧已經遠遠超越了自身實現的功能:github

//\ 正則表達式,匹配知足 alpha 定義規則的字符串
var opacityre = /\b\s*alpha\s*\(\s*opacity\s*=\s*(\d+)\s*\)/;

//\ 原型鏈掛載,直接將 opacity 放入 CSSStyleDeclaration 中
defineProperty(window.CSSStyleDeclaration.prototype, "opacity", {

  //\ getter 函數,自定義 toString 方法
  get: function() {
    var m = this.filter.match(opacityre);
    return m ? (m[1] / 100).toString() : "";
  },
  
  //\ setter 函數,將 opacity 值寫入 alpha(opacity=$value) 的形式,供瀏覽器使用
  set: function(value) {
    this.zoom = 1;
    var found = false;
    if (value < 1) {
      value = " alpha(opacity=" + Math.round(value * 100) + ")";
    } else {
      value = "";
    }
    this.filter = this.filter.replace(opacityre, function() {
      found = true;
      return value;
    });
    if (!found && value) {
      this.filter += value;
    }
  }
});

此腳本包含的設計思路爲利用 Getter/Setter 控制不一樣上下文中屬性的設置與獲取,一樣的思路即爲 Vue.js 數據綁定的設計源泉。正則表達式

IOS 3 模塊 / 語言特性 Polyfill

Zepto 默認編譯中未包含的 IOS 3 模塊 src/ios3.js 包含了兩個函數的兼容實現,實際上屬於語言特性 Polyfill,這類 Polyfill 主要用於解決語言發展與實現不一樣步等問題,並提供一些實現良好的公共方法用於業務開發,最多見的兩類例子:算法

  • Lodash / Underscore 提供大量實現良好的工具函數
  • TypeScript 提供類型系統,實際這門語言也可被當作 Polyfill 看,由於 ECMAScript 提案中,已經包含了一個靜態類型系統 的建議

IOS 3 模塊中的兩個 Polyfill 分別爲 String / Array 兩個包裝類原型上掛載了一個常見方法:windows

//\ Line 6
    String.prototype.trim = function() {
      //\ 將字符串首末的空格剪除
      return this.replace(/^\s+|\s+$/g, "");
    };

該方法原始定義於 ES 5 標準中的 String.prototype.trim(),此處實現依賴 ES 5 標準中的 White Space 中的描述。該方法實現相對簡單,同時也提示了一個設計常識:向公認的 API 靠齊,實現方法核心後提供方法擴展,遵循該原則的包括:

  • Preact 與 React
  • Zepto 與 jQuery
  • Lodash 與 Underscore 等

trim() 函數較爲簡單明確,而 reduce() 方法的實現與 ES 5 中的 Array.prototype.reduce(callbackfn[,initialValue]) 定義的算法徹底相同,更能體現這一原則,此段不進行註釋,進入 ES 5 規範中該函數定義便可對照理解該 Polyfill 的實現方法。

//\ Line 11
  if (Array.prototype.reduce === undefined)
    Array.prototype.reduce = function(fun) {
      //\ 略
    };

Detect 模塊 / User Agent 識別

Detect 模塊用於識別瀏覽器平臺類型,默認也不處於編譯列表中,其代碼 src/detect.js 組織結構以下:

//\ Line 5
;(function($){

  //\ 平臺偵測邏輯
  function detect(ua, platform){
  }

  //\ 傳入 Zepto 及平臺環境變量
  detect.call($, navigator.userAgent, navigator.platform)
  // make available to unit tests
  $.__detect = detect

//\ 將全局變量 Zepto 帶入,化爲參數 '$'
})(Zepto)

平臺偵測邏輯 function detect(ua, platform) 內部爲一組大的字符串判斷邏輯,造成這樣雜亂無章的平臺判斷邏輯,正是由於一代一代的瀏覽器大戰。 User-Agent 字符串被定義爲包含了當前瀏覽器(規範名稱 User Agent)信息的 HTTP 頭部標識,用於使服務器能夠根據平臺完成瀏覽器檢測並下發不一樣的原始代碼用於渲染。因爲瀏覽器假裝等各類緣由,UA 實際並不可信,所以對於它的偵測至關困難,常見 UA 能夠從 List of User Agents 頁面內查詢到。

Zepto 沒有默認編譯該模塊,以及利用該模塊判斷後提供平臺相關邏輯的主要緣由在於其設計原則:20% 的代碼完成 jQuery 核心 80% 的功能。此處,也引出了代碼實現的另外一個基本原則:面向功能/API 標準,先功能覆蓋再優雅降級。以提供一個常見的 Browser Compatibility Matrix 爲例,根據實現規格測試前端產出在不一樣平臺的可用性,再提供降級方案或 Polyfill 以知足更多的用戶需求。

相關文章
相關標籤/搜索