ZeptoソースのDataモジュールを読む
8075 ワード
Zepto
のData
モジュールは、DOM
ノード内のdata-*
属性のデータを取得し、DOM
関連するデータを格納するために使用される.Zeptoソースシリーズの記事を読んでgithubに載せました.star:reading-zeptoを歓迎します.
ソースバージョン
本稿で読むソースコードはzepto 1である.2.0
GitBook
《reading-zepto》
内部メソッド
attributeData
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
オブジェクトに格納するために使用される.node.attributes
取得したのはノードのすべての属性であるため,遍歴の際に属性名がdata-
で始まるか否かを判断する必要がある.格納するときは、属性名の
data-
を外し、残りはアルパカ式に変換してstore
対象とするkey
.DOM
の属性値はいずれも文字列形式であり、操作を容易にするために呼び出されるdeserializeValue
メソッドは、対応するデータ型に変換される.このメソッドの具体的な解析については、『Zeptoソースコードを読む属性操作』を参照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
}
より多くの場合、格納データは
DOM
に書く必要はなく、メモリに格納するだけでよい.また読み取りDOM
のコストは非常に高い.setData
メソッドは対応DOM
のデータをstore
オブジェクトに格納します.var id = node[exp] || (node[exp] = ++$.uuid)
まず
node
のexp
属性を読み出し、前から見えるexp
Zepto
タイムスタンプを付けた文字列であり、属性名の一意性を確保し、ユーザがカスタマイズした属性を上書きしないようにしている.ノードのnode
プロパティを設定します.store = data[id] || (data[id] = attributeData(node))
exp
からノードの前にキャッシュされたデータを取得し、前にキャッシュされていない場合はexp
メソッドを呼び出し、ノード上のdata
先頭のすべての属性値を取得し、attributeData
オブジェクトにキャッシュする.store[camelize(name)] = value
最後に、キャッシュする値を設定します.
getData
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)
}
}
data-
ノード指定のキャッシュ値を取得します.if (name === undefined) return store || setData(node)
属性名が指定されていない場合は、ノードに対応するキャッシュをすべて返し、キャッシュが空の場合は
data
メソッドを呼び出し、node
ノード上のsetData
先頭の属性値をすべて返します.if (name in store) return store[name]
指定した
node
キャッシュdata-
であれば、結果を返します.var camelName = camelize(name)
if (camelName in store) return store[camelName]
そうでなければ、指定した
name
をアルパカ式に変換し、キャッシュstore
から検索し、見つかった結果を返します.これは互換性name
というパラメータ形式で、より柔軟なstore
を提供しています.キャッシュに見つからない場合は、
camel-name
で検索しますが、実は検索API
属性の値です.この方法は後で分析されます.DOMメソッド
.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) })
}
$.fn.data
メソッドは対応data-
ノードのキャッシュデータを設定または取得でき、最終的にはそれぞれdata
およびnode
メソッドが呼び出される.このコードを分析するには、例によって三元表現を一つ一つ分解して、何をしたかを見てみましょう.
value === undefined ? : this.each(function(){ setData(this, name, value) })
まず第1層を見て、転送
setData
およびgetData
がある場合は、キャッシュの設定であることを示し、すべての要素を巡り、それぞれname
メソッド設定キャッシュを呼び出す.$.isPlainObject(name) ?
this.each(function(i, node){
$.each(name, function(key, value){ setData(node, key, value) })
}) :
value
の最初のパラメータは、オブジェクトの伝達値、例えばsetData
もサポートする.オブジェクトの場合、オブジェクトのプロパティは設定するキャッシュ名で、値はキャッシュ値です.したがって、すべての要素を巡り、
data
キャッシュを設定します.0 in this ? getData(this[0], name) : undefined
最後に、集合が空でないか否かを判断し(
$(el).data({key1: 'value1'})
)、空であればそのままsetData
、そうでなければ0 in this
を呼び出し、最初の要素ノード対応undefined
のキャッシュを返す..removeData()
$.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]
})
})
}
getData
キャッシュされたデータを削除するために使用し、転送パラメータがない場合は全てクリアし、転送パラメータがある場合は指定されたデータのみを削除する.name
配列として、削除するデータのセットを指定してもよいし、スペースで区切られた文字列としてもよい.if (typeof names == 'string') names = names.split(/\s+/)
removeData
が文字列であることが検出された場合は、まず文字列を配列に変換します.return this.each(function(){
var id = this[exp], store = id && data[id]
...
})
要素を巡り、すべての要素を削除し、要素に対応するキャッシュを見つける
names
.if (store) $.each(names || store, function(key){
delete store[names ? camelize(this) : key]
})
names
存在する場合は指定したデータを削除し、そうでない場合はstore
キャッシュしたデータを全て削除します..remove()と.Empty()メソッドの書き換え
;['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)
}
})
従来の
names
とstore
方法では、いずれもremove
ノードの削除がありますが、empty
ノードの削除後は、対応するノードのキャッシュデータにも意味がありません.DOM
ノードの削除後も、ノードに対応するデータをすべてクリアしてメモリを解放する必要があります.var elements = this.find('*')
if (methodName === 'remove') elements = elements.add(this)
DOM
すべての下位ノードであり、DOM
メソッドであれば、ノード自体も除去されるので、自身もノードに加える必要がある.最後に
elements
メソッドを呼び出し、すべてのデータを参照せずにクリアし、データをクリアした後、元のメソッドを呼び出してノードを削除します.ツールメソッド
$.data
$.data = function(elem, name, value) {
return $(elem).data(name, value)
}
remove
最後に呼び出されたのがremoveData
のdata
メソッドです.$.hasData
$.hasData = function(elem) {
var id = elem[exp], store = id && data[id]
return store ? !$.isEmptyObject(store) : false
}
エレメントにキャッシュされたデータがあるかどうかを判断します.
まず、キャッシュ
DOM
から、対応data
のキャッシュdata
を取り出すことにより、DOM
が存在し、かつ空でなければstore
を返し、実際にはstore
を返す.シリーズ記事
License
署名-非商業的使用-演繹禁止4.0国際(CC BY-NC-ND 4.0)
最後に、すべての文章は同時に微信の公衆番号に送信され、注目を歓迎し、意見を歓迎します.
作者:対角の反対側