亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Zepto中數(shù)據(jù)緩存原理與實(shí)現(xiàn)

macg0406 / 2084人閱讀

摘要:有一個(gè)模塊,專門(mén)用來(lái)做數(shù)據(jù)緩存,允許我們存放任何與相關(guān)的數(shù)據(jù)。在匹配元素上存儲(chǔ)任意相關(guān)數(shù)據(jù)或返回匹配的元素集合中的第一個(gè)元素的給定名稱的數(shù)據(jù)存儲(chǔ)的值。確定元素是否有與之相關(guān)的數(shù)據(jù)。

前言

以前我們使用Zepto進(jìn)行開(kāi)發(fā)的時(shí)候,會(huì)把一些自定義的數(shù)據(jù)存到dom節(jié)點(diǎn)上,好處是非常直觀和便捷,但是也帶來(lái)了例如直接將數(shù)據(jù)暴露出來(lái)會(huì)出現(xiàn)安全問(wèn)題,數(shù)據(jù)以html自定義屬性標(biāo)簽存在,對(duì)于瀏覽器本身來(lái)說(shuō)是沒(méi)有多大意義的,最后要獲取數(shù)據(jù)的時(shí)候還得操作dom。Zepto有一個(gè)data模塊,專門(mén)用來(lái)做數(shù)據(jù)緩存,允許我們存放任何與dom相關(guān)的數(shù)據(jù)。

原文鏈接

源碼倉(cāng)庫(kù)

原理

在開(kāi)始學(xué)習(xí)和閱讀Zepto中的data模塊前,我們先大致了解一下dom元素和要緩存的數(shù)據(jù)是如何聯(lián)系起來(lái)的。

看一下上面那張圖。簡(jiǎn)單地理解就是

dom元素身上有一exp(Zepto1507010934916)屬性,其對(duì)應(yīng)的值是1,2,3整數(shù)數(shù)字,

data是一個(gè)存儲(chǔ)著與dom元素相關(guān)聯(lián)的自定義數(shù)據(jù)的大對(duì)象類(lèi)似下面這樣

{
  1: {
    name: "qianlongo"
  },
  2: {
    sex: "boy"
  }
}

dom元素就是通過(guò)1,2,3數(shù)字索引和大對(duì)象data關(guān)聯(lián)起來(lái)

對(duì)于DOM自定義數(shù)據(jù)的增刪改查就是在對(duì)數(shù)字索引對(duì)應(yīng)的對(duì)象進(jìn)行操作。

$.fn.data

在匹配元素上存儲(chǔ)任意相關(guān)數(shù)據(jù)或返回匹配的元素集合中的第一個(gè)元素的給定名稱的數(shù)據(jù)存儲(chǔ)的值。

例子

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標(biāo)簽上以data-為前綴的屬性。接下來(lái)我們就直接看源碼實(shí)現(xiàn)啦

源碼

$.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) })
}

通過(guò)上面的例子我們知道,設(shè)置數(shù)據(jù)的時(shí)候可以單個(gè)屬性設(shè)置,也可以多個(gè)屬性(傳遞一個(gè)對(duì)象)一起設(shè)置。大量使用三目運(yùn)算是Zepto一貫的風(fēng)格。我們來(lái)拆解一下這段代碼。

當(dāng)value傳遞了值并且不是undefined的時(shí)候可以認(rèn)為是設(shè)置單個(gè)數(shù)據(jù)屬性。于是走這段代碼

this.each(function(){ setData(this, name, value) })

通過(guò)遍歷匹配元素,并調(diào)用setData方法傳入元素,要設(shè)置的數(shù)據(jù)的key和value。

當(dāng)沒(méi)有傳遞value進(jìn)來(lái),并且name是個(gè)純粹的對(duì)象時(shí)候。也就是類(lèi)似這樣使用

$box.data({ baz: [ 1, 2, 3 ] })

此時(shí)走的是這段代碼

this.each(function(i, node){
  $.each(name, function(key, value){ setData(node, key, value) })
})

還是遍歷當(dāng)前匹配元素,并且遍歷傳進(jìn)的對(duì)象name,到底層還是調(diào)用setData方法一個(gè)個(gè)屬性進(jìn)行設(shè)置。

當(dāng)name不是一個(gè)對(duì)象的時(shí)候,認(rèn)為是對(duì)數(shù)據(jù)的讀取操作。走的是這段代碼

(0 in this ? getData(this[0], name) : undefined)

通過(guò)判斷當(dāng)前是否有匹配的元素,如果有則是調(diào)用getData方法,并傳入匹配元素集合中的第一個(gè)元素,以及要獲取的數(shù)據(jù)name屬性。如果沒(méi)有匹配元素,就直接返回undefined了。

總體邏輯還是挺清晰的。接下來(lái)我們主要需要弄清楚上面用到的幾個(gè)函數(shù)setData,getData。以及解釋一下data模塊初始定義的幾個(gè)變量

var data = {}, 
    dataAttr = $.fn.data, 
    camelize = $.camelCase,
    exp = $.expando = "Zepto" + (+new Date())

各變量解釋如下

/**
   * data 存儲(chǔ)于dom相映射的數(shù)據(jù)數(shù)據(jù)結(jié)構(gòu)如同下
   * {
   *   1: {
   *      name: "qianlongo",
   *      sex: "boy"
   *    },
   *   2: {
   *      age: 100
   *    }
   * }
   * 
   * dataAttr $原型上的data方法,通過(guò)getAttribute和setAttribute設(shè)置或讀取元素屬性
   * camelize 中劃線轉(zhuǎn)小駝峰函數(shù)
   * exp => Zepto1507004986420 設(shè)置在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是類(lèi)似Zepto1507004986420的字符串,$.uuid初始值是0,首先會(huì)嘗試去讀取元素身上的exp屬性,元素沒(méi)有該屬性就為該元素設(shè)置exp屬性。

并去data大對(duì)象中讀取id(1, 2, 3...)屬性,當(dāng)然了如果data對(duì)象中沒(méi)有讀取到,就通過(guò)調(diào)用attributeData函數(shù)先獲取
node節(jié)點(diǎn)所有以data-為前綴的自定義屬性,并將其賦值。

現(xiàn)在自定義屬性的集合已經(jīng)有了,先判斷name是否是個(gè)undefined,不是就往store上添加name屬性。

最后函數(shù)調(diào)用之后會(huì)返回整個(gè)數(shù)據(jù)對(duì)象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
}

我們先來(lái)看一下node.attributes mdn是個(gè)啥

Element.attributes 屬性返回該元素所有屬性節(jié)點(diǎn)的一個(gè)實(shí)時(shí)集合。該集合是一個(gè) NamedNodeMap 對(duì)象,不是一個(gè)數(shù)組,所以它沒(méi)有 數(shù)組 的方法,其包含的 屬性 節(jié)點(diǎn)的索引順序隨瀏覽器不同而不同。更確切地說(shuō),attributes 是字符串形式的名/值對(duì),每一對(duì)名/值對(duì)對(duì)應(yīng)一個(gè)屬性節(jié)點(diǎn)。

例子

let $box = document.querySelector(".box")
    $box.dataset.age = 100
    console.log($box.attributes)

得到的數(shù)據(jù)如上圖所示,接下來(lái)我們?cè)倩氐?b>attributeData函數(shù)的源碼分析

if (attr.name.indexOf("data-") == 0)
    store[camelize(attr.name.replace("data-", ""))] =
      $.zepto.deserializeValue(attr.value)

通過(guò)判斷ele.attributes拿到的集合中,是否是以data-開(kāi)頭的屬性,如果是就往store對(duì)象中添加駝峰化后的該屬性,并且序列化之后的attr.value作為該屬性的值。最后將store對(duì)象返回。

getData

獲取存儲(chǔ)在data中與DOM元素關(guān)聯(lián)的對(duì)象name屬性。當(dāng)name屬性不存在的時(shí)候直接返回整個(gè)對(duì)象。

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)
  }
}

實(shí)現(xiàn)思路還是首先去讀取setData時(shí)候添加在node節(jié)點(diǎn)上的id,然后以該id為key去data中查找。如果name沒(méi)有傳,此時(shí)直接返回整個(gè)store,當(dāng)然如果store也沒(méi)有找到,就返回調(diào)用setData后返回的該元素的自定義屬性的集合。

當(dāng)store存在時(shí),先判斷name屬性在store中存在與否,存在便直接返回相應(yīng)的屬性,否則對(duì)傳入的name進(jìn)行駝峰化之后再判斷在store中是否存在,存在即返回對(duì)應(yīng)的屬性。也就是說(shuō)你傳入的name為min-age或者minAge得到的是一樣的值。

最后如果在數(shù)據(jù)緩存中還沒(méi)有找到屬性name,就調(diào)用dataAttr函數(shù),去直接查找元素身上的相關(guān)屬性。

removeData

在元素上移除綁定的數(shù)據(jù)

可以添加或者更新數(shù)據(jù)自然也就可以移除數(shù)據(jù)了,先看下例子

例子

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()

我們可以指定刪除單個(gè)屬性,也可以通過(guò)空格隔開(kāi)刪除多個(gè)屬性,也可以傳入一個(gè)要?jiǎng)h除的屬性數(shù)組,甚至當(dāng)你什么都不傳的時(shí)候,原先設(shè)置在該元素身上的data會(huì)被全部清空

源碼

$.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]
    })
  })
}

首先傳進(jìn)來(lái)的names是字符串的情況下,先轉(zhuǎn)化成數(shù)組,接著就是對(duì)當(dāng)前匹配的元素集合進(jìn)行遍歷,逐個(gè)刪除元素對(duì)應(yīng)的緩存的數(shù)據(jù)。

當(dāng)查找到store的時(shí)候?qū)D(zhuǎn)化后的names或者store進(jìn)行遍歷,如果是自己指定要?jiǎng)h除的屬性,先駝峰化一下,再用delete刪除,否則全部清空則直接delete store中的key

$.data

存儲(chǔ)任意數(shù)據(jù)到指定的元素并且/或者返回設(shè)置的值

$.data = function(elem, name, value) {
  return $(elem).data(name, value)
}

定義在$函數(shù)身上的靜態(tài)方法,底層還是調(diào)用的實(shí)例方法.data。

$.hasData

確定元素是否有與之相關(guān)的Zepto數(shù)據(jù)。

$.hasData = function(elem) {
  var id = elem[exp], store = id && data[id]
  return store ? !$.isEmptyObject(store) : false
}

同樣定義在$函數(shù)身上的靜態(tài)方法,原理就是拿著elem身上的id,去data中查找是否有與之關(guān)聯(lián)的數(shù)據(jù)對(duì)象,如果找到了并且不是一個(gè)空對(duì)象,便返回true,否則沒(méi)有找到或者是空對(duì)象都是返回false

remove, empty

生成擴(kuò)展的remove和empty方法,未擴(kuò)展之前的remove和empty功能依舊還在,增添了刪除選中的元素緩存的數(shù)據(jù)功能。

;["remove", "empty"].forEach(function(methodName){
  // 緩存原型上之前對(duì)應(yīng)的remove和empty方法
  var origFn = $.fn[methodName]
  // 重寫(xiě)兩個(gè)方法
  $.fn[methodName] = function() {
    // 獲取當(dāng)前選中元素的所有內(nèi)部包含元素
    var elements = this.find("*")
    // 如果是remove方法,則在獲取的elements元素基礎(chǔ)上把本身也添加進(jìn)去
    if (methodName === "remove") elements = elements.add(this)
    // 調(diào)用removeData刪除與dom關(guān)聯(lián)的data中的數(shù)據(jù)
    elements.removeData()
    // 最后還是調(diào)用對(duì)應(yīng)的方法刪除dom,或者清除dom的內(nèi)容
    return origFn.call(this)
  }
})
結(jié)尾

以上是Zepto種data模塊所有源碼分析,歡迎大家指正其中有問(wèn)題的地方。

文章記錄

data模塊

Zepto中數(shù)據(jù)緩存原理與實(shí)現(xiàn)(2017-10-03)

form模塊

zepto源碼分析之form模塊(2017-10-01)

zepto模塊

這些Zepto中實(shí)用的方法集(2017-08-26)

Zepto核心模塊之工具方法拾遺 (2017-08-30)

看zepto如何實(shí)現(xiàn)增刪改查DOM (2017-10-2)

event模塊

mouseenter與mouseover為何這般糾纏不清?(2017-06-05)

向zepto.js學(xué)習(xí)如何手動(dòng)觸發(fā)DOM事件(2017-06-07)

誰(shuí)說(shuō)你只是"會(huì)用"jQuery?(2017-06-08)

ajax模塊

原來(lái)你是這樣的jsonp(原理與具體實(shí)現(xiàn)細(xì)節(jié))(2017-06-11)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/88834.html

相關(guān)文章

  • Zepto源碼之Data模塊

    摘要:的模塊用來(lái)獲取節(jié)點(diǎn)中的屬性的數(shù)據(jù),和儲(chǔ)存跟相關(guān)的數(shù)據(jù)。獲取節(jié)點(diǎn)指定的緩存值。如果存在,則刪除指定的數(shù)據(jù),否則將緩存的數(shù)據(jù)全部刪除。為所有下級(jí)節(jié)點(diǎn),如果為方法,則節(jié)點(diǎn)自身也是要被移除的,所以需要將自身也加入到節(jié)點(diǎn)中。 Zepto 的 Data 模塊用來(lái)獲取 DOM 節(jié)點(diǎn)中的 data-* 屬性的數(shù)據(jù),和儲(chǔ)存跟 DOM 相關(guān)的數(shù)據(jù)。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡...

    wua_wua2012 評(píng)論0 收藏0
  • Zepto這樣操作元素屬性

    摘要:還有一點(diǎn)需要注意的是方法設(shè)置或者獲取都是在操作元素的屬性,那它和,的區(qū)別在哪呢可以查看設(shè)置設(shè)置與的設(shè)置部分比較類(lèi)似,既支持直接傳入普通的字符串也支持傳入回調(diào)函數(shù)。 前言 使用Zepto的時(shí)候,我們經(jīng)常會(huì)要去操作一些DOM的屬性,或元素本身的固有屬性或自定義屬性等。比如常見(jiàn)的有attr(),removeAttr(),prop(),removeProp(),data()等。接下來(lái)我們挨個(gè)整...

    付倫 評(píng)論0 收藏0
  • Zepto學(xué)習(xí)關(guān)于"偏移"的那些事

    摘要:獲得當(dāng)前元素相對(duì)于的位置。返回一個(gè)對(duì)象含有和當(dāng)給定一個(gè)含有和屬性對(duì)象時(shí),使用這些值來(lái)對(duì)集合中每一個(gè)元素進(jìn)行相對(duì)于的定位。獲取對(duì)象集合中第一個(gè)元素相對(duì)于其的位置。結(jié)尾以上就是中與偏移相關(guān)的幾個(gè)的解析,歡迎指出其中的問(wèn)題和有錯(cuò)誤的地方。 前言 這篇文章主要想說(shuō)一下Zepto中與偏移相關(guān)的一些事,很久很久以前,我們經(jīng)常會(huì)使用offset、position、scrollTop、scrollLe...

    hzx 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<