Zepto
的 Data
模塊用來獲取 DOM
節點中的 data-*
屬性的數據,和儲存跟 DOM
相關的數據。javascript
讀 Zepto 源碼系列文章已經放到了github上,歡迎star: reading-zeptojava
本文閱讀的源碼爲 zepto1.2.0node
《reading-zepto》git
var data = {}, dataAttr = $.fn.data, camelize = $.camelCase, exp = $.expando = 'Zepto' + (+new Date()), emptyArray = [] function attributeData(node) { var store = {} $.each(node.attributes || emptyArray, function(i, attr){ if (attr.name.indexOf('data-') == 0) store[camelize(attr.name.replace('data-', ''))] = $.zepto.deserializeValue(attr.value) }) return store }
這個方法用來獲取給定 node
中全部 data-*
屬性的值,並儲存到 store
對象中。github
node.attributes
獲取到的是節點的全部屬性,所以在遍歷的時候,須要判斷屬性名是否以 data-
開頭。segmentfault
在存儲的時候,將屬性名的 data-
去掉,剩餘部分轉換成駝峯式,做爲 store
對象的 key
。數組
在 DOM
中的屬性值都爲字符串格式,爲方便操做,調用 deserializeValue
方法,轉換成對應的數據類型,關於這個方法的具體分析,請看 《讀Zepto源碼之屬性操做》緩存
function setData(node, name, value) { var id = node[exp] || (node[exp] = ++$.uuid), store = data[id] || (data[id] = attributeData(node)) if (name !== undefined) store[camelize(name)] = value return store }
更多時候,儲存數據不須要寫在 DOM
中,只須要儲存在內存中便可。並且讀取 DOM
的成本很是高。微信
setData
方法會將對應 DOM
的數據儲存在 store
對象中。函數
var id = node[exp] || (node[exp] = ++$.uuid)
首先讀取 node
的 exp
屬性,從前面能夠看到 exp
是一個 Zepto
加上時間戳的字符串,以確保屬性名的惟一性,避免覆蓋用戶自定義的屬性,若是 node
還沒有打上 exp
標記,代表這個節點並無緩存的數據,則設置節點的 exp
屬性。
store = data[id] || (data[id] = attributeData(node))
從 data
中獲取節點以前緩存的數據,若是以前沒有緩存數據,則調用 attributeData
方法,獲取節點上全部以 data-
開頭的屬性值,緩存到 data
對象中。
store[camelize(name)] = value
最後,設置須要緩存的值。
function getData(node, name) { var id = node[exp], store = id && data[id] if (name === undefined) return store || setData(node) else { if (store) { if (name in store) return store[name] var camelName = camelize(name) if (camelName in store) return store[camelName] } return dataAttr.call($(node), name) } }
獲取 node
節點指定的緩存值。
if (name === undefined) return store || setData(node)
若是沒有指定屬性名,則將節點對應的緩存所有返回,若是緩存爲空,則調用 setData
方法,返回 node
節點上全部以 data-
開頭的屬性值。
if (name in store) return store[name]
若是指定的 name
在緩存 store
中,則將結果返回。
var camelName = camelize(name) if (camelName in store) return store[camelName]
不然,將指定的 name
轉換成駝峯式,再從緩存 store
中查找,將找到的結果返回。這是兼容 camel-name
這樣的參數形式,提供更靈活的 API
。
若是緩存中都沒找到,則回退到用 $.fn.data
查找,其實就是查找 data-
屬性上的值,這個方法後面會分析到。
$.fn.data = function(name, value) { return value === undefined ? $.isPlainObject(name) ? this.each(function(i, node){ $.each(name, function(key, value){ setData(node, key, value) }) }) : (0 in this ? getData(this[0], name) : undefined) : this.each(function(){ setData(this, name, value) }) }
data
方法能夠設置或者獲取對應 node
節點的緩存數據,最終分別調用的是 setData
和 getData
方法。
分析這段代碼,照例仍是將三元表達式一個一個拆解,來看看都作了什麼事情。
value === undefined ? 三元表達式 : this.each(function(){ setData(this, name, value) })
先看第一層,當有傳遞 name
和 value
時,代表是設置緩存,遍歷全部元素,分別調用 setData
方法設置緩存。
$.isPlainObject(name) ? this.each(function(i, node){ $.each(name, function(key, value){ setData(node, key, value) }) }) : 三元表達式
data
的第一個參數還支持對象的傳值,例如 $(el).data({key1: 'value1'})
。若是是對象,則對象裏的屬性爲須要設置的緩存名,值爲緩存值。
所以,遍歷全部元素,調用 setData
設置緩存。
0 in this ? getData(this[0], name) : undefined
最後,判斷集合是否不爲空( 0 in this
), 若是爲空,則直接返回 undefined
,不然,調用 getData
,返回第一個元素節點對應 name
的緩存。
$.fn.removeData = function(names) { if (typeof names == 'string') names = names.split(/\s+/) return this.each(function(){ var id = this[exp], store = id && data[id] if (store) $.each(names || store, function(key){ delete store[names ? camelize(this) : key] }) }) }
removeData
用來刪除緩存的數據,若是沒有傳遞參數,則所有清空,若是有傳遞參數,則只刪除指定的數據。
names
能夠爲數組,指定須要刪除的一組數據,也能夠爲以空格分割的字符串。
if (typeof names == 'string') names = names.split(/\s+/)
若是檢測到 names
爲字符串,則先將字符串轉換成數組。
return this.each(function(){ var id = this[exp], store = id && data[id] ... })
遍歷元素,對全部的元素都進行刪除操做,找出和元素對應的緩存 store
。
if (store) $.each(names || store, function(key){ delete store[names ? camelize(this) : key] })
若是 names
存在,則刪除指定的數據,不然將 store
緩存的數據所有刪除。
;['remove', 'empty'].forEach(function(methodName){ var origFn = $.fn[methodName] $.fn[methodName] = function() { var elements = this.find('*') if (methodName === 'remove') elements = elements.add(this) elements.removeData() return origFn.call(this) } })
原有的 remove
和 empty
方法,都會有 DOM
節點的移除,在移除 DOM
節點後,對應節點的緩存數據也就沒有什麼意義了,全部在移除 DOM
節點後,也須要將節點對應的數據也清空,以釋放內存。
var elements = this.find('*') if (methodName === 'remove') elements = elements.add(this)
elements
爲全部下級節點,若是爲 remove
方法,則節點自身也是要被移除的,因此須要將自身也加入到節點中。
最後調用 removeData
方法,不傳參清空全部數據,在清空數據後,再調用原來的方法移除節點。
$.data = function(elem, name, value) { return $(elem).data(name, value) }
data
最後調用的也就是 DOM
的 data
方法。
$.hasData = function(elem) { var id = elem[exp], store = id && data[id] return store ? !$.isEmptyObject(store) : false }
判斷某個元素是否已經有緩存的數據。
首先經過從緩存 data
中,取出對應 DOM
的緩存 store
,若是 store
存在,而且不爲空,則返回 true
,其實狀況返回 false
。
署名-非商業性使用-禁止演繹 4.0 國際 (CC BY-NC-ND 4.0)
最後,全部文章都會同步發送到微信公衆號上,歡迎關注,歡迎提意見:
做者:對角另外一面