Angular default_iterator_diff 源码解析
源码解析 default_iterable_differ.ts
源码路径: packages/core/src/change_detection/differs/default_iterable_differ.ts
数据结构
IterableChangeRecord
这个是数组中每个元素的封装。注意内部的私有属性,基本是成对的,用以描述双向链表
export class IterableChangeRecord_<V> implements IterableChangeRecord<V> {
currentIndex: number|null = null;
previousIndex: number|null = null;
/** @internal */
_nextPrevious: IterableChangeRecord_<V>|null = null;
/** @internal */
_prev: IterableChangeRecord_<V>|null = null;
/** @internal */
_next: IterableChangeRecord_<V>|null = null;
/** @internal */
_prevDup: IterableChangeRecord_<V>|null = null;
/** @internal */
_nextDup: IterableChangeRecord_<V>|null = null;
/** @internal */
_prevRemoved: IterableChangeRecord_<V>|null = null;
/** @internal */
_nextRemoved: IterableChangeRecord_<V>|null = null;
/** @internal */
_nextAdded: IterableChangeRecord_<V>|null = null;
/** @internal */
_nextMoved: IterableChangeRecord_<V>|null = null;
/** @internal */
_nextIdentityChange: IterableChangeRecord_<V>|null = null;
constructor(public item: V, public trackById: any) {}
}
DuplicateItemRecordList
这个是描述了一个单向链表,内部维护了表头,表尾(_head,_tail),对外提供了add,get,remove
三个方法。这个链表内的元素不去重。它内部的元素则是IterableChangeRecord
,借用了它内部的_nextDup,_prevDup
来维护一个双向链表。
// A linked list of IterableChangeRecords with the same IterableChangeRecord_.item
class _DuplicateItemRecordList<V> {
/** @internal */
_head: IterableChangeRecord_<V>|null = null;
/** @internal */
_tail: IterableChangeRecord_<V>|null = null;
/**
* Append the record to the list of duplicates.
*
* Note: by design all records in the list of duplicates hold the same value in record.item.
*/
add(record: IterableChangeRecord_<V>): void {
if (this._head === null) {
this._head = this._tail = record;
record._nextDup = null;
record._prevDup = null;
} else {
// TODO(vicb):
// assert(record.item == _head.item ||
// record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN);
this._tail!._nextDup = record;
record._prevDup = this._tail;
record._nextDup = null;
this._tail = record;
}
}
// Returns a IterableChangeRecord_ having IterableChangeRecord_.trackById == trackById and
// IterableChangeRecord_.currentIndex >= atOrAfterIndex
get(trackById: any, atOrAfterIndex: number|null): IterableChangeRecord_<V>|null {
let record: IterableChangeRecord_<V>|null;
for (record = this._head; record !== null; record = record._nextDup) {
if ((atOrAfterIndex === null || atOrAfterIndex <= record.currentIndex!) &&
Object.is(record.trackById, trackById)) {
return record;
}
}
return null;
}
/**
* Remove one {@link IterableChangeRecord_} from the list of duplicates.
*
* Returns whether the list of duplicates is empty.
*/
remove(record: IterableChangeRecord_<V>): boolean {
// TODO(vicb):
// assert(() {
// // verify that the record being removed is in the list.
// for (IterableChangeRecord_ cursor = _head; cursor != null; cursor = cursor._nextDup) {
// if (identical(cursor, record)) return true;
// }
// return false;
//});
const prev: IterableChangeRecord_<V>|null = record._prevDup;
const next: IterableChangeRecord_<V>|null = record._nextDup;
if (prev === null) {
this._head = next;
} else {
prev._nextDup = next;
}
if (next === null) {
this._tail = prev;
} else {
next._prevDup = prev;
}
return this._head === null;
}
}
DuplicateMap
,这个是一个Map,它对应的key 是trackById 计算出来的值,value 是上面介绍的DuplicateItemRecordList
。对外提供了put,get,remove
三个接口。
class _DuplicateMap<V> {
map = new Map<any, _DuplicateItemRecordList<V>>();
put(record: IterableChangeRecord_<V>) {
const key = record.trackById;
let duplicates = this.map.get(key);
if (!duplicates) {
duplicates = new _DuplicateItemRecordList<V>();
this.map.set(key, duplicates);
}
duplicates.add(record);
}
/**
* Retrieve the `value` using key. Because the IterableChangeRecord_ value may be one which we
* have already iterated over, we use the `atOrAfterIndex` to pretend it is not there.
*
* Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
* have any more `a`s needs to return the second `a`.
*/
get(trackById: any, atOrAfterIndex: number|null): IterableChangeRecord_<V>|null {
const key = trackById;
const recordList = this.map.get(key);
return recordList ? recordList.get(trackById, atOrAfterIndex) : null;
}
/**
* Removes a {@link IterableChangeRecord_} from the list of duplicates.
*
* The list of duplicates also is removed from the map if it gets empty.
*/
remove(record: IterableChangeRecord_<V>): IterableChangeRecord_<V> {
const key = record.trackById;
const recordList: _DuplicateItemRecordList<V> = this.map.get(key)!;
// Remove the list of duplicates when it gets empty
if (recordList.remove(record)) {
this.map.delete(key);
}
return record;
}
get isEmpty(): boolean {
return this.map.size === 0;
}
clear() {
this.map.clear();
}
}
总结
IterableChangeRecord
是数组中元素的封装,内部拥有额外私有属性用于维护各种双向链表。DuplicateItemRecordList
代表一个双向链表,head/tail
,借助于IterableChangeRecord._nextDup,IterableChangeRecord._preDup
实现双向链表。DuplicateMap
一个Map, key 是trackById
,value 则是DuplicateItemRecordList
,它提供了数组以trackById为key的字典结构视图。
diff算法
diff算法则是比较新旧两个数组的差异。旧的数组已经转化为了_itHead/_itTail表示的双向链表。新的数组通过collection[index]
来进行遍历。关注check(collection: NgIterable<V>): boolean
这个方法。为了简单,以下是伪代码。对象的比较是通过Object.is
先介绍几个双向链表
- 迭代链表(_linkedRecords),表示数组最终的成员,
_itHead/_itTail
表示头尾。_linkedRecords提供O(1)的trackById的访问。 - 删除链表(_uninkedRecords),表示将要删除的成员,
_removalHead/_removalTail
表示头尾,_uninkedRecords提O(1)的trackById的访问。 - 移动链表,表示需要移动的成员,
_movesHead/_movesTail
标记头尾 - 对象引用变更链表,表示同一个对象但是引用变化了
_identityChangesHead/_identityChangesTail
标记头尾。 - 添加链表,表示添加的元素,
_additionsHead/_additionsTail
标记头尾。
let record: IterableChangeRecord_<V>|null = this._itHead;
for (let index = 0; index < this.length; index++){
item = collection[index];
itemTrackBy = this._trackByFn(index, item);
//通过使用Object.is 来进行对比,对比的对象是trackById。
if (record === null || !Object.is(record.trackById, itemTrackBy)) {
record = this._mismatch(record, item, itemTrackBy, index);
mayBeDirty = true;
} else {
if (mayBeDirty) {
// TODO(misko): can we limit this to duplicates only?
record = this._verifyReinsertion(record, item, itemTrackBy, index);
}
if (!Object.is(record.item, item)) this._addIdentityChange(record, item);
}
record = record._next;
}
// 发现差异后的操作,record: itemFromPreviousArray, item: itemFromCurrentArray, itemTrackBy: trackById, index: currentIndex.
// 除了第一个元素是旧的数据,两位三个都是新的数组里面的值。
function _mismatch(record: IterableChangeRecord_<V>|null, item: V, itemTrackBy: any, index: number):
IterableChangeRecord_<V> {
//首先删除旧的元素,
_remove(record);
// 新的元素有两种可能,一个是插入一个全新的元素,另一个则是,从前面的index或者后面的index,将元素移过来。
// 检查一下当前要插入的元素是不是在之前刚刚删除过,
// 如果是之前删除掉的,那么其实是一个move 操作。后移操作, 重新插入操作 _reinsertAfter
// 如果是后面某个index 的元素,则是前移操作,移动操作 _moveAfter
// 否则则是插入操作, 插入操作。_addAfter
// 先检查删除的链表DuplicateMap
record = getItemFromRemovedDuplicateMap(itemTrackBy);
// 后移操作
if(record !== null){
this._reinsertAfter(record, previousRecord, index);
}else{
record = getItemFromCurrentList(itemTrackBy);
if(record !== null){
this._moveAfter(record, previousRecord, index);
}else{
record = this._addAfter(new IterableChangeRecord_<V>(item, itemTrackBy), previousRecord, index);
}
}
}
_remove()
伪代码
- 从迭代链表中删除
- 添加到删除链表中
_remove(record: IterableChangeRecord_<V>): IterableChangeRecord_<V> {
return this._addToRemovals(this._unlink(record));
}
_insertAfter()
伪代码
- 插入目标节点到迭代链表中,
- 更新linkedRecords 字典,
- 更新目标元素的currentIndex
_insertAfter(
record: IterableChangeRecord_<V>, prevRecord: IterableChangeRecord_<V>|null,
index: number): IterableChangeRecord_<V> {
// TODO(vicb):
// assert(record != prevRecord);
// assert(record._next === null);
// assert(record._prev === null);
const next: IterableChangeRecord_<V>|null =
prevRecord === null ? this._itHead : prevRecord._next;
// TODO(vicb):
// assert(next != record);
// assert(prevRecord != record);
record._next = next;
record._prev = prevRecord;
if (next === null) {
this._itTail = record;
} else {
next._prev = record;
}
if (prevRecord === null) {
this._itHead = record;
} else {
prevRecord._next = record;
}
if (this._linkedRecords === null) {
this._linkedRecords = new _DuplicateMap<V>();
}
this._linkedRecords.put(record);
record.currentIndex = index;
return record;
}
_addAfter()
伪代码
- 在某个节点后面插入目标节点 (_insertAfter)
- 更新添加链表
_addAfter(
record: IterableChangeRecord_<V>, prevRecord: IterableChangeRecord_<V>|null,
index: number): IterableChangeRecord_<V> {
this._insertAfter(record, prevRecord, index);
if (this._additionsTail === null) {
// TODO(vicb):
// assert(this._additionsHead === null);
this._additionsTail = this._additionsHead = record;
} else {
// TODO(vicb):
// assert(_additionsTail._nextAdded === null);
// assert(record._nextAdded === null);
this._additionsTail = this._additionsTail._nextAdded = record;
}
return record;
}
_reinsertAfter
- 从删除链表中移除节点
- 插入节点至迭代链表 (_insertAfter)
- 添加到移动链表中 (_addToMoves)
_reinsertAfter(
record: IterableChangeRecord_<V>, prevRecord: IterableChangeRecord_<V>|null,
index: number): IterableChangeRecord_<V> {
if (this._unlinkedRecords !== null) {
this._unlinkedRecords.remove(record);
}
const prev = record._prevRemoved;
const next = record._nextRemoved;
if (prev === null) {
this._removalsHead = next;
} else {
prev._nextRemoved = next;
}
if (next === null) {
this._removalsTail = prev;
} else {
next._prevRemoved = prev;
}
this._insertAfter(record, prevRecord, index);
this._addToMoves(record, index);
return record;
}