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;
  }
posted @ 2022-07-18 06:12  kongshu  阅读(31)  评论(0编辑  收藏  举报