摘要:有一個模塊,專門用來做數據緩存,允許我們存放任何與相關的數據。在匹配元素上存儲任意相關數據或返回匹配的元素集合中的第一個元素的給定名稱的數據存儲的值。確定元素是否有與之相關的數據。
前言
以前我們使用Zepto進行開發的時候,會把一些自定義的數據存到dom節點上,好處是非常直觀和便捷,但是也帶來了例如直接將數據暴露出來會出現安全問題,數據以html自定義屬性標簽存在,對于瀏覽器本身來說是沒有多大意義的,最后要獲取數據的時候還得操作dom。Zepto有一個data模塊,專門用來做數據緩存,允許我們存放任何與dom相關的數據。
原文鏈接
源碼倉庫
原理在開始學習和閱讀Zepto中的data模塊前,我們先大致了解一下dom元素和要緩存的數據是如何聯系起來的。
看一下上面那張圖。簡單地理解就是
dom元素身上有一exp(Zepto1507010934916)屬性,其對應的值是1,2,3整數數字,
data是一個存儲著與dom元素相關聯的自定義數據的大對象類似下面這樣
{ 1: { name: "qianlongo" }, 2: { sex: "boy" } }
dom元素就是通過1,2,3數字索引和大對象data關聯起來
對于DOM自定義數據的增刪改查就是在對數字索引對應的對象進行操作。
$.fn.data在匹配元素上存儲任意相關數據或返回匹配的元素集合中的第一個元素的給定名稱的數據存儲的值。
例子
let $box = $(".box") // setData $box.data("foo", 52) $box.data("bar", { myType: "test", count: 40 }) $box.data({ baz: [ 1, 2, 3 ] }) // getData $box.data("foo") // 52 $box.data("name") // qianlongo $box.data() // { name: "qianlongo", sex: "boy", foo: 52, bar: { myType: "test", count: 40 }, baz: [ 1, 2, 3 ] }
基本用法大家肯定很熟悉,需要注意的地方是,我們也可以直接獲取定義在html標簽上以data-為前綴的屬性。接下來我們就直接看源碼實現啦
源碼
$.fn.data = function(name, value) { return value === undefined ? // set multiple values via object $.isPlainObject(name) ? this.each(function(i, node){ $.each(name, function(key, value){ setData(node, key, value) }) }) : // get value from first element (0 in this ? getData(this[0], name) : undefined) : // set value on all elements this.each(function(){ setData(this, name, value) }) }
通過上面的例子我們知道,設置數據的時候可以單個屬性設置,也可以多個屬性(傳遞一個對象)一起設置。大量使用三目運算是Zepto一貫的風格。我們來拆解一下這段代碼。
當value傳遞了值并且不是undefined的時候可以認為是設置單個數據屬性。于是走這段代碼
this.each(function(){ setData(this, name, value) })
通過遍歷匹配元素,并調用setData方法傳入元素,要設置的數據的key和value。
當沒有傳遞value進來,并且name是個純粹的對象時候。也就是類似這樣使用
$box.data({ baz: [ 1, 2, 3 ] })
此時走的是這段代碼
this.each(function(i, node){ $.each(name, function(key, value){ setData(node, key, value) }) })
還是遍歷當前匹配元素,并且遍歷傳進的對象name,到底層還是調用setData方法一個個屬性進行設置。
當name不是一個對象的時候,認為是對數據的讀取操作。走的是這段代碼
(0 in this ? getData(this[0], name) : undefined)
通過判斷當前是否有匹配的元素,如果有則是調用getData方法,并傳入匹配元素集合中的第一個元素,以及要獲取的數據name屬性。如果沒有匹配元素,就直接返回undefined了。
總體邏輯還是挺清晰的。接下來我們主要需要弄清楚上面用到的幾個函數setData,getData。以及解釋一下data模塊初始定義的幾個變量
var data = {}, dataAttr = $.fn.data, camelize = $.camelCase, exp = $.expando = "Zepto" + (+new Date())
各變量解釋如下
/** * data 存儲于dom相映射的數據數據結構如同下 * { * 1: { * name: "qianlongo", * sex: "boy" * }, * 2: { * age: 100 * } * } * * dataAttr $原型上的data方法,通過getAttribute和setAttribute設置或讀取元素屬性 * camelize 中劃線轉小駝峰函數 * exp => Zepto1507004986420 設置在dom上的屬性,value是data中的key 1, 2,3等 */
setData
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 }
exp是類似Zepto1507004986420的字符串,$.uuid初始值是0,首先會嘗試去讀取元素身上的exp屬性,元素沒有該屬性就為該元素設置exp屬性。
并去data大對象中讀取id(1, 2, 3...)屬性,當然了如果data對象中沒有讀取到,就通過調用attributeData函數先獲取
node節點所有以data-為前綴的自定義屬性,并將其賦值。
現在自定義屬性的集合已經有了,先判斷name是否是個undefined,不是就往store上添加name屬性。
最后函數調用之后會返回整個數據對象store。
attributeData
獲取元素以data-為前綴的自定義屬性的集合
// Read all "data-*" attributes from a node 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.attributes mdn是個啥
Element.attributes 屬性返回該元素所有屬性節點的一個實時集合。該集合是一個 NamedNodeMap 對象,不是一個數組,所以它沒有 數組 的方法,其包含的 屬性 節點的索引順序隨瀏覽器不同而不同。更確切地說,attributes 是字符串形式的名/值對,每一對名/值對對應一個屬性節點。
例子
let $box = document.querySelector(".box") $box.dataset.age = 100 console.log($box.attributes)
得到的數據如上圖所示,接下來我們再回到attributeData函數的源碼分析
if (attr.name.indexOf("data-") == 0) store[camelize(attr.name.replace("data-", ""))] = $.zepto.deserializeValue(attr.value)
通過判斷ele.attributes拿到的集合中,是否是以data-開頭的屬性,如果是就往store對象中添加駝峰化后的該屬性,并且序列化之后的attr.value作為該屬性的值。最后將store對象返回。
getData
獲取存儲在data中與DOM元素關聯的對象name屬性。當name屬性不存在的時候直接返回整個對象。
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) } }
實現思路還是首先去讀取setData時候添加在node節點上的id,然后以該id為key去data中查找。如果name沒有傳,此時直接返回整個store,當然如果store也沒有找到,就返回調用setData后返回的該元素的自定義屬性的集合。
當store存在時,先判斷name屬性在store中存在與否,存在便直接返回相應的屬性,否則對傳入的name進行駝峰化之后再判斷在store中是否存在,存在即返回對應的屬性。也就是說你傳入的name為min-age或者minAge得到的是一樣的值。
最后如果在數據緩存中還沒有找到屬性name,就調用dataAttr函數,去直接查找元素身上的相關屬性。
removeData在元素上移除綁定的數據
可以添加或者更新數據自然也就可以移除數據了,先看下例子
例子
let $box = $(".box") $box.data("foo", 52) $box.data("bar", { myType: "test", count: 40 }) $box.data({ baz: [ 1, 2, 3 ] }) // $box.removeData("foo") // $box.removeData("foo bar baz") // $box.removeData(["foo", "bar", "baz"]) // $box.removeData()
我們可以指定刪除單個屬性,也可以通過空格隔開刪除多個屬性,也可以傳入一個要刪除的屬性數組,甚至當你什么都不傳的時候,原先設置在該元素身上的data會被全部清空
源碼
$.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] }) }) }
首先傳進來的names是字符串的情況下,先轉化成數組,接著就是對當前匹配的元素集合進行遍歷,逐個刪除元素對應的緩存的數據。
當查找到store的時候對轉化后的names或者store進行遍歷,如果是自己指定要刪除的屬性,先駝峰化一下,再用delete刪除,否則全部清空則直接delete store中的key
$.data存儲任意數據到指定的元素并且/或者返回設置的值
$.data = function(elem, name, value) { return $(elem).data(name, value) }
定義在$函數身上的靜態方法,底層還是調用的實例方法.data。
$.hasData確定元素是否有與之相關的Zepto數據。
$.hasData = function(elem) { var id = elem[exp], store = id && data[id] return store ? !$.isEmptyObject(store) : false }
同樣定義在$函數身上的靜態方法,原理就是拿著elem身上的id,去data中查找是否有與之關聯的數據對象,如果找到了并且不是一個空對象,便返回true,否則沒有找到或者是空對象都是返回false
remove, empty生成擴展的remove和empty方法,未擴展之前的remove和empty功能依舊還在,增添了刪除選中的元素緩存的數據功能。
;["remove", "empty"].forEach(function(methodName){ // 緩存原型上之前對應的remove和empty方法 var origFn = $.fn[methodName] // 重寫兩個方法 $.fn[methodName] = function() { // 獲取當前選中元素的所有內部包含元素 var elements = this.find("*") // 如果是remove方法,則在獲取的elements元素基礎上把本身也添加進去 if (methodName === "remove") elements = elements.add(this) // 調用removeData刪除與dom關聯的data中的數據 elements.removeData() // 最后還是調用對應的方法刪除dom,或者清除dom的內容 return origFn.call(this) } })結尾
以上是Zepto種data模塊所有源碼分析,歡迎大家指正其中有問題的地方。
文章記錄
data模塊
Zepto中數據緩存原理與實現(2017-10-03)
form模塊
zepto源碼分析之form模塊(2017-10-01)
zepto模塊
這些Zepto中實用的方法集(2017-08-26)
Zepto核心模塊之工具方法拾遺 (2017-08-30)
看zepto如何實現增刪改查DOM (2017-10-2)
event模塊
mouseenter與mouseover為何這般糾纏不清?(2017-06-05)
向zepto.js學習如何手動觸發DOM事件(2017-06-07)
誰說你只是"會用"jQuery?(2017-06-08)
ajax模塊
原來你是這樣的jsonp(原理與具體實現細節)(2017-06-11)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88834.html
摘要:的模塊用來獲取節點中的屬性的數據,和儲存跟相關的數據。獲取節點指定的緩存值。如果存在,則刪除指定的數據,否則將緩存的數據全部刪除。為所有下級節點,如果為方法,則節點自身也是要被移除的,所以需要將自身也加入到節點中。 Zepto 的 Data 模塊用來獲取 DOM 節點中的 data-* 屬性的數據,和儲存跟 DOM 相關的數據。 讀 Zepto 源碼系列文章已經放到了github上,歡...
摘要:還有一點需要注意的是方法設置或者獲取都是在操作元素的屬性,那它和,的區別在哪呢可以查看設置設置與的設置部分比較類似,既支持直接傳入普通的字符串也支持傳入回調函數。 前言 使用Zepto的時候,我們經常會要去操作一些DOM的屬性,或元素本身的固有屬性或自定義屬性等。比如常見的有attr(),removeAttr(),prop(),removeProp(),data()等。接下來我們挨個整...
摘要:獲得當前元素相對于的位置。返回一個對象含有和當給定一個含有和屬性對象時,使用這些值來對集合中每一個元素進行相對于的定位。獲取對象集合中第一個元素相對于其的位置。結尾以上就是中與偏移相關的幾個的解析,歡迎指出其中的問題和有錯誤的地方。 前言 這篇文章主要想說一下Zepto中與偏移相關的一些事,很久很久以前,我們經常會使用offset、position、scrollTop、scrollLe...
閱讀 1410·2021-11-25 09:43
閱讀 2268·2021-09-27 13:36
閱讀 1121·2021-09-04 16:40
閱讀 1962·2019-08-30 11:12
閱讀 3314·2019-08-29 14:14
閱讀 570·2019-08-28 17:56
閱讀 1328·2019-08-26 13:50
閱讀 1251·2019-08-26 13:29