js框架開發之旅--選擇器


我在幕後已經給我們的框架添加了許多功能,如果你想看到這些功能,就去我們的GitHub下載吧,turing.js.

這一章我們將講解選擇器的原理,比較分析一些流行的選擇器的特性。選擇器是js web框架重要的組成部分,值得我們將花比較多篇幅來講解。


選擇器的歷史

提供一個跨平臺的選擇器接口,對於一個框架來說至關重要。不管是XPath式的還是CSS式的選擇器,瀏覽器的支持都存在差異。
爲了理解選擇器的重要性,我們可以回到90年代,看看在沒有js框架的時候,人們是怎麼做的。
document.all    // A proprietary property in IE4
document.getElementById('navigation');
人們想到的第一件事是簡化代碼。Prototype通過$()方法做到了這些。
function $(element) {
  if (arguments.length > 1) {
    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
      elements.push($(arguments[i]));
    return elements;
  }
  if (Object.isString(element))
    element = document.getElementById(element);
  return Element.extend(element);
}
Prototype不是簡單的給document.getElementById一個別名,它可以查詢多個id,並且通過繼承給元素賦予更多的功能。
我們正真需要的是getElementsBySelector。我們不僅僅想通過id和tagname檢索元素,我們還要對元素進行一系列操作。我們在定義樣式時,經常使用CSS選擇器,因此我們在選擇元素時也可以參考css選擇器。
Simon Willison在2003年寫了getElementsBySelector方法。這給我們對操作DOM結構提供了非常好的支持。
瀏覽器除了提供getElementById方法外,還提供了getElementsByClassName和getElementsByName等DOM查詢方法。大部分非IE瀏覽器提供了XPath表達式的查詢。
現在的瀏覽器已經提供了querySelector和querySelectorAll方法,這兩個方法同樣不能受到IE的支持(小牧注:IE8以上瀏覽器已經實現了querySelector和querySelectorAll方法)。


瀏覽器支持

Web開發人員對瀏覽器的差異感到非常苦惱,選擇器是其中的重災區。跨瀏覽器的選擇器確實給Web開發人員減輕了不少的麻煩。
我曾經看到過一些處理瀏覽器bug的漂亮代碼,像Sizzle中的一段代碼:
// Check to see if the browser returns elements by name when
// querying by getElementById (and provide a workaround)
(function(){
  // We're going to inject a fake input element with a specified name
  var form = document.createElement("div"),
  id = "script" + (new Date()).getTime();
  form.innerHTML = "<a name='" + id + "'/>";
   
  // Inject it into the root element, check its status, and remove it quickly
  var root = document.documentElement;
  root.insertBefore( form, root.firstChild );
它創建了一個僞造的元素用來探測瀏覽器行爲。瀏覽器的支持確實是一門非常考驗程序員耐心的藝術。


性能

Javascript程序員頻繁的使用選擇器,查出在DOM結構任意位置的元素,因此選擇器需要足夠的塊。
使用瀏覽器支持的原生選擇器可以獲取很好的性能, Sizzle使用querySelectorAll,但是不是所有的瀏覽器都支持該方法。
另一個辦法是使用緩存,Sizzle和Prototype等框架都使用了緩存。

通過slickspeed或者Woosh這樣的工具或類庫可以測試選擇器的性能。這些工具都非常有用,但是你對測試結果要小心。像Prototype和MooTools這樣的庫給元素繼承了額外的方法,但是想Sizzle這樣的純選擇器引擎沒有這樣做。這樣的不同類型的框架應該區別對待。


其它的選擇器引擎

我一直在參考Sizzle來給大家介紹選擇器,其實還有更多的像Sizzle這樣的選擇器。去年受Sizzle發佈的影響,接連出現了好幾個類似的框架。自從出現以後,Sizzle一直受到框架開發者的歡迎。
Peppy借鑑了許多的類庫,並且引入了一些有用的功能處理緩存。Sly也做了許多優化,並且允許你暴露選擇器解析的過程。Peppy和Sly都是一個豐富的庫,任何一點都不不比Sizzle差。


API設計

一般來說有兩種API。一種是通過調用一個方法返回匹配的元素,並且把元素包裝在自定義的類裏,包裝類可以給元素提供複雜的查詢和操作方法。jQuery,Dojo和Glow就是這樣做的。
jQuery通過$()方法,返回一個節點的列表,並且提供一個可鏈式調用的方法集。
Dojo通過dojo.query方法,返回一個類似數組的dojo.NodeList。Glow的glow.dom.get方法也有類似的功能。
第二種方法是直接返回繼承了操作方法的元素列表,Prototype和MooTools就屬於第二種。
Prototype使用$()方法代替getElementById,使用$$()方法來實現CSS或XPath規則的查詢。他們都給查詢的元素繼承了自己提供的方法,都支持字符串和元素列表作爲參數。
Turing設計了一個像Glow一樣的類庫,因此我們也採用Glow一樣的方法設計我們的選擇器接口。選擇這個也是我出於自己的喜好,它能夠提供一個包裝在對象裏的未經處理過的元素,這個包裝幫我們屏蔽瀏覽器之間的操作差異。
turing.dom.find('.class')                  // return elements wrapped in a class without looking at each of them
.find('a')                                 // find elements that are links
.css({ 'background-color': '#aabbcc' })    // apply the style by actually processing elements

目標

因爲Turing是一個爲教學而生的框架,我們還是儘量讓它保持簡單:
  • 只支持CSS類型的選擇器(可能在將來的版本或插件裏支持XPath)
  • 有限的CSS僞類支持,我們會選擇支持幾個常用的僞類。
  • 儘量使用原生的方法,讓代碼越快越好。
  • 使用緩存提高性能。
  • 像Sly那樣暴露解析結果。
  • 借鑑其他選擇器引擎的處理瀏覽器問題的智慧。


結論

我希望你能夠了解選擇器的複雜性和重要性。如果僅僅是處理瀏覽器的缺陷,框架確實沒有存在的必要。但是許多瀏覽器不是標準的瀏覽器,或者在邁向標準瀏覽器的過程中。我們要做的就是彌補這一部分的不足。

Sizzle是所有選擇器引擎之母。我認爲實現一個小巧的順手的選擇器,是件值的去做的事情。歡迎關注我們接下來的教程,我們將開始實現一個我們的選擇器。


牧客網--讓自由職業成爲一個靠譜的工作


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章