数据驱动核心实现 data方法
返回值是update选择集合
export default function(value, key) {
// 没有value的话 就是取值器
if (!value) {
data = new Array(this.size()), j = -1;
this.each(function(d) { data[++j] = d; });
return data;
}
....
update = new Selection(update, parents);
update._enter = enter;
update._exit = exit;
// 返回update
return update;
}
bindKey bingIndex
export default function(value, key) {
if (!value) {
data = new Array(this.size()), j = -1;
this.each(function(d) { data[++j] = d; });
return data;
}
var bind = key ? bindKey : bindIndex,
parents = this._parents,
groups = this._groups;
if (typeof value !== "function") value = constant(value);
// 遍历所有的groups
for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
var parent = parents[j],
group = groups[j],
groupLength = group.length,
data = value.call(parent, parent && parent.__data__, j, parents),
dataLength = data.length,
enterGroup = enter[j] = new Array(dataLength),
updateGroup = update[j] = new Array(dataLength),
exitGroup = exit[j] = new Array(groupLength);
bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);
...
}
update = new Selection(update, parents);
update._enter = enter;
update._exit = exit;
return update;
}
bindIndex
实现
三种情况:
- data数据项大于原来的nodelist
- data数据项等于原来的nodelist
- data数据项小于原来的nodelist
最终的逻辑,等于原来nodelist长度的data数据项 全部都是属于update集合,
超过nodelist长度的data数据项属于enter集合,超过data数据项长度的nodelist部分 属于exit集合。
function bindIndex(parent, group, enter, update, exit, data) {
var i = 0,
node,
groupLength = group.length,
dataLength = data.length;
// Put any non-null nodes that fit into update.
// Put any null nodes into enter.
// Put any remaining data into enter.
// 遍历data新的数据源
for (; i < dataLength; ++i) {
// 如果当前group中存在 则是update操作
if (node = group[i]) {
node.__data__ = data[i];
update[i] = node;
} else {
// 否则就是新增
enter[i] = new EnterNode(parent, data[i]);
}
}
// Put any non-null nodes that don’t fit into exit.
// 如果dom节点数量比 data数量多 那么后面的都是需要删除的
for (; i < groupLength; ++i) {
if (node = group[i]) {
exit[i] = node;
}
}
}
bingKey
的实现
整个逻辑:
- 先遍历nodelist 获取每一个node的key 并且缓存,如果有多个node拥有相同的key,则把重复的node 放到exit集合中
- 遍历data数据源,获取数据源的每一项的key,如果整个key在上一步的nodelist的key中存在,就把node放到update集合中(并且会把缓存的node的key清空);其他的情况:数据项的key在node缓存的key中不存在或者是重复的key,都会被放到enter集合中。
- 原来nodelist中的node的key在新的数据源中不存在了,那么这些node属于exit集合。
function bindKey(parent, group, enter, update, exit, data, key) {
var i,
node,
nodeByKeyValue = {},
groupLength = group.length,
dataLength = data.length,
keyValues = new Array(groupLength),
keyValue;
// Compute the key for each node.
// If multiple nodes have the same key, the duplicates are added to exit.
for (i = 0; i < groupLength; ++i) {
// 如果dom节点不为空
if (node = group[i]) {
// 获取当前node节点的keyvalue 并且缓存在keyValues数组中
keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);
// 多个dom节点有相同的key 就会将后面的添加到exit数组中
if (keyValue in nodeByKeyValue) {
exit[i] = node;
} else {
// 缓存dom节点和他对应的keyValue
nodeByKeyValue[keyValue] = node;
}
}
}
// Compute the key for each datum.
// If there a node associated with this key, join and add it to update.
// If there is not (or the key is a duplicate), add it to enter.
// 遍历数据data
for (i = 0; i < dataLength; ++i) {
// 获取数据的key
keyValue = keyPrefix + key.call(parent, data[i], i, data);
// 如果之前缓存的dom节点key中存在当前的数据源中的key
// 就添加到update 更新数组结合中
// 并且吧dom节点key在缓存中 清楚
if (node = nodeByKeyValue[keyValue]) {
update[i] = node;
node.__data__ = data[i];
nodeByKeyValue[keyValue] = null;
} else {
// 如果当前数据中的key 在 node keyValue缓存中 没有
// 新增node到enter数组中
enter[i] = new EnterNode(parent, data[i]);
}
}
// Add any remaining nodes that were not bound to data to exit.
for (i = 0; i < groupLength; ++i) {
if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {
exit[i] = node;
}
}
}
网友评论