博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
读Zepto源码之Data模块
阅读量:7019 次
发布时间:2019-06-28

本文共 4945 字,大约阅读时间需要 16 分钟。

ZeptoData 模块用来获取 DOM 节点中的 data-* 属性的数据,和储存跟 DOM 相关的数据。

读 Zepto 源码系列文章已经放到了github上,欢迎star:

源码版本

本文阅读的源码为

GitBook

《》

内部方法

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 方法,转换成对应的数据类型,关于这个方法的具体分析,请看 《》

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)复制代码

首先读取 nodeexp 属性,从前面可以看到 exp 是一个 Zepto 加上时间戳的字符串,以确保属性名的唯一性,避免覆盖用户自定义的属性,如果 node 尚未打上 exp 标记,表明这个节点并没有缓存的数据,则设置节点的 exp 属性。

store = data[id] || (data[id] = attributeData(node))复制代码

data 中获取节点之前缓存的数据,如果之前没有缓存数据,则调用 attributeData 方法,获取节点上所有以 data- 开头的属性值,缓存到 data 对象中。

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)  }}复制代码

获取 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- 属性上的值,这个方法后面会分析到。

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) })}复制代码

data 方法可以设置或者获取对应 node 节点的缓存数据,最终分别调用的是 setDatagetData 方法。

分析这段代码,照例还是将三元表达式一个一个拆解,来看看都做了什么事情。

value === undefined ? 三元表达式 : this.each(function(){ setData(this, name, value) })复制代码

先看第一层,当有传递 namevalue 时,表明是设置缓存,遍历所有元素,分别调用 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 的缓存。

.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]    })  })}复制代码

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()方法的改写

;['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)  }})复制代码

原有的 removeempty 方法,都会有 DOM 节点的移除,在移除 DOM 节点后,对应节点的缓存数据也就没有什么意义了,所有在移除 DOM 节点后,也需要将节点对应的数据也清空,以释放内存。

var elements = this.find('*')if (methodName === 'remove') elements = elements.add(this)复制代码

elements 为所有下级节点,如果为 remove 方法,则节点自身也是要被移除的,所以需要将自身也加入到节点中。

最后调用 removeData 方法,不传参清空所有数据,在清空数据后,再调用原来的方法移除节点。

工具方法

$.data

$.data = function(elem, name, value) {  return $(elem).data(name, value)}复制代码

data 最后调用的也就是 DOMdata 方法。

$.hasData

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

判断某个元素是否已经有缓存的数据。

首先通过从缓存 data 中,取出对应 DOM 的缓存 store ,如果 store 存在,并且不为空,则返回 true ,其实情况返回 false

系列文章

附文

参考

License

最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:

作者:对角另一面

转载地址:http://ftzxl.baihongyu.com/

你可能感兴趣的文章
Python核心编程(第3版)-客户端FTP程序示例
查看>>
OC最实用的runtime总结
查看>>
myeclipse对导入的jquery.js出现中文乱码错误
查看>>
kvm的VM 添加scsi vdisk后启动报错
查看>>
解决Win8下IE10无法打开的故障
查看>>
javascript var变量删除
查看>>
AndroidStudio——导出apk
查看>>
centos7安装nfs服务器
查看>>
交换机概念名词解释
查看>>
yum更新php版本
查看>>
Struts2 标签库讲解
查看>>
数据建模大数据就业挑战月薪30K
查看>>
python面试题
查看>>
intro Two-Phase Commit(2PC)
查看>>
用AutoIt自动安装和卸载程序
查看>>
刷新网卡ip
查看>>
移动构造函数和移动赋值函数
查看>>
supervisor的使用简介
查看>>
深入浅出之-route命令实战使用指南
查看>>
反掩码详解
查看>>