openlayers源碼分析(1)

1 準備

// 碼云fork鸥滨,速度快
git clone https://gitee.com/solfKwolf/openlayers.git
// 以v5.3版本為準
git checkout v5.3.0

2 入口文件

src/ol/index.js

/**
 * @module ol
 */

export {default as AssertionError} from './AssertionError.js';
export {default as Collection} from './Collection.js';
export {default as Disposable} from './Disposable.js';
export {default as Feature} from './Feature.js';
export {default as Geolocation} from './Geolocation.js';
export {default as Graticule} from './Graticule.js';
export {default as Image} from './Image.js';
export {default as ImageBase} from './ImageBase.js';
export {default as ImageCanvas} from './ImageCanvas.js';
export {default as ImageTile} from './ImageTile.js';
export {default as Kinetic} from './Kinetic.js';
export {default as Map} from './Map.js';
export {default as MapBrowserEvent} from './MapBrowserEvent.js';
export {default as MapBrowserEventHandler} from './MapBrowserEventHandler.js';
export {default as MapBrowserPointerEvent} from './MapBrowserPointerEvent.js';
export {default as MapEvent} from './MapEvent.js';
export {default as Object} from './Object.js';
export {default as Observable} from './Observable.js';
export {default as Overlay} from './Overlay.js';
export {default as PluggableMap} from './PluggableMap.js';
export {default as Tile} from './Tile.js';
export {default as TileCache} from './TileCache.js';
export {default as TileQueue} from './TileQueue.js';
export {default as TileRange} from './TileRange.js';
export {default as VectorImageTile} from './VectorImageTile.js';
export {default as VectorTile} from './VectorTile.js';
export {default as View} from './View.js';
export {default as WebGLMap} from './WebGLMap.js';

export {getUid, inherits, VERSION} from './util.js';

ol是將不同的模塊拆分成不同的文件集中放置在ol文件下愉老,再統一通過index.js入口文件暴露出去颁糟。

將default暴露的模塊重命名

// xxx.js
export default function() {
  // statement
}
// index.js
export {default as xxx} from './xxx.js';

// 相當于
import { default as xxx } from './xxx.js'

3 AssertionError

openlayer自定義錯誤類(繼承Error)句携,目的是方便自定義錯誤描述與錯誤碼相對應宏怔。

示例截圖

class AssertionError extends Error {

  /**
   * @param {number} code Error code.
   */
  constructor(code) {
    const path = VERSION === 'latest' ? VERSION : 'v' + VERSION.split('-')[0];
    const message = 'Assertion failed. See https://openlayers.org/en/' + path +
    '/doc/errors/#' + code + ' for details.';

    super(message);

    /**
     * Error code. The meaning of the code can be found on
     * https://openlayers.org/en/latest/doc/errors/ (replace `latest` with
     * the version found in the OpenLayers script's header comment if a version
     * other than the latest is used).
     * @type {number}
     * @api
     */
    this.code = code;

    /**
     * @type {string}
     */
    this.name = 'AssertionError';

    // Re-assign message, see https://github.com/Rich-Harris/buble/issues/40
    this.message = message;
  }

}

如果需要自己開發(fā)類庫鹃唯,需要認真研讀Error類,非常簡單阵幸。

5 Event

openlayers庫中定義的事件基類花履,只提供typetarget屬性以及stopPropagationpreventDefault方法芽世。方便后面的自定義事件。

class Event {

  /**
   * @param {string} type Type.
   */
  constructor(type) {

    /**
     * @type {boolean}
     */
    this.propagationStopped;

    /**
     * The event type.
     * @type {string}
     * @api
     */
    this.type = type;

    /**
     * The event target.
     * @type {Object}
     * @api
     */
    this.target = null;
  }

  /**
   * Stop event propagation.
   * @api
   */
  preventDefault() {
    this.propagationStopped = true;
  }

  /**
   * Stop event propagation.
   * @api
   */
  stopPropagation() {
    this.propagationStopped = true;
  }

}


/**
 * @param {Event|import("./Event.js").default} evt Event
 */
export function stopPropagation(evt) {
  evt.stopPropagation();
}


/**
 * @param {Event|import("./Event.js").default} evt Event
 */
export function preventDefault(evt) {
  evt.preventDefault();
}

export default Event;

6 Target

對象類是配合事件類的诡壁。

class Target extends Disposable {
  constructor() {

    super();

    /**
     * @private
     * @type {!Object<string, number>}
     */
    this.pendingRemovals_ = {};

    /**
     * @private
     * @type {!Object<string, number>}
     */
    this.dispatching_ = {};

    /**
     * @private
     * @type {!Object<string, Array<import("../events.js").ListenerFunction>>}
     */
    this.listeners_ = {};

  }

  /**
   * @param {string} type Type.
   * @param {import("../events.js").ListenerFunction} listener Listener.
   */
  addEventListener(type, listener) {
    let listeners = this.listeners_[type];
    if (!listeners) {
      listeners = this.listeners_[type] = [];
    }
    if (listeners.indexOf(listener) === -1) {
      listeners.push(listener);
    }
  }

  /**
   * Dispatches an event and calls all listeners listening for events
   * of this type. The event parameter can either be a string or an
   * Object with a `type` property.
   *
   * @param {{type: string,
   *     target: (EventTargetLike|undefined),
   *     propagationStopped: (boolean|undefined)}|
   *     import("./Event.js").default|string} event Event object.
   * @return {boolean|undefined} `false` if anyone called preventDefault on the
   *     event object or if any of the listeners returned false.
   * @api
   */
  dispatchEvent(event) {
    const evt = typeof event === 'string' ? new Event(event) : event;
    const type = evt.type;
    evt.target = this;
    const listeners = this.listeners_[type];
    let propagate;
    if (listeners) {
      if (!(type in this.dispatching_)) {
        this.dispatching_[type] = 0;
        this.pendingRemovals_[type] = 0;
      }
      ++this.dispatching_[type];
      for (let i = 0, ii = listeners.length; i < ii; ++i) {
        if (listeners[i].call(this, evt) === false || evt.propagationStopped) {
          propagate = false;
          break;
        }
      }
      --this.dispatching_[type];
      if (this.dispatching_[type] === 0) {
        let pendingRemovals = this.pendingRemovals_[type];
        delete this.pendingRemovals_[type];
        while (pendingRemovals--) {
          this.removeEventListener(type, VOID);
        }
        delete this.dispatching_[type];
      }
      return propagate;
    }
  }

  /**
   * @inheritDoc
   */
  disposeInternal() {
    unlistenAll(this);
  }

  /**
   * Get the listeners for a specified event type. Listeners are returned in the
   * order that they will be called in.
   *
   * @param {string} type Type.
   * @return {Array<import("../events.js").ListenerFunction>} Listeners.
   */
  getListeners(type) {
    return this.listeners_[type];
  }

  /**
   * @param {string=} opt_type Type. If not provided,
   *     `true` will be returned if this event target has any listeners.
   * @return {boolean} Has listeners.
   */
  hasListener(opt_type) {
    return opt_type ?
      opt_type in this.listeners_ :
      Object.keys(this.listeners_).length > 0;
  }

  /**
   * @param {string} type Type.
   * @param {import("../events.js").ListenerFunction} listener Listener.
   */
  removeEventListener(type, listener) {
    const listeners = this.listeners_[type];
    if (listeners) {
      const index = listeners.indexOf(listener);
      if (type in this.pendingRemovals_) {
        // make listener a no-op, and remove later in #dispatchEvent()
        listeners[index] = VOID;
        ++this.pendingRemovals_[type];
      } else {
        listeners.splice(index, 1);
        if (listeners.length === 0) {
          delete this.listeners_[type];
        }
      }
    }
  }
}


export default Target;

7 EventType

事件類型字典表

export default {
  /**
   * Generic change event. Triggered when the revision counter is increased.
   * @event module:ol/events/Event~Event#change
   * @api
   */
  CHANGE: 'change',

  CLEAR: 'clear',
  CONTEXTMENU: 'contextmenu',
  CLICK: 'click',
  DBLCLICK: 'dblclick',
  DRAGENTER: 'dragenter',
  DRAGOVER: 'dragover',
  DROP: 'drop',
  ERROR: 'error',
  KEYDOWN: 'keydown',
  KEYPRESS: 'keypress',
  LOAD: 'load',
  MOUSEDOWN: 'mousedown',
  MOUSEMOVE: 'mousemove',
  MOUSEOUT: 'mouseout',
  MOUSEUP: 'mouseup',
  MOUSEWHEEL: 'mousewheel',
  MSPOINTERDOWN: 'MSPointerDown',
  RESIZE: 'resize',
  TOUCHSTART: 'touchstart',
  TOUCHMOVE: 'touchmove',
  TOUCHEND: 'touchend',
  WHEEL: 'wheel'
};

7 BaseObject

src/BaseObject.js這個對象其實使用頻率最高济瓢,為啥,因為經常用到它定義的get的方法

8 Collection.js

這里定義了CollectionCollectionEvent2個類

CollectionEvent

只在Event的基礎上增加了element屬性

/**
 * @classdesc
 * Events emitted by {@link module:ol/Collection~Collection} instances are instances of this
 * type.
 */
export class CollectionEvent extends Event {

  /**
   * @param {CollectionEventType} type Type.
   * @param {*=} opt_element Element.
   */
  constructor(type, opt_element) {
    super(type);

    /**
     * The element that is added to or removed from the collection.
     * @type {*}
     * @api
     */
    this.element = opt_element;

  }
}

Collection

對js數組的擴展妹卿,簡單的一個數據接口旺矾,可以參考下。

class Collection extends BaseObject {

  /**
   * @param {Array<T>=} opt_array Array.
   * @param {Options=} opt_options Collection options.
   */
  constructor(opt_array, opt_options) {

    super();

    const options = opt_options || {};

    /**
     * @private
     * @type {boolean}
     */
    this.unique_ = !!options.unique;

    /**
     * @private
     * @type {!Array<T>}
     */
    this.array_ = opt_array ? opt_array : [];

    if (this.unique_) {
      for (let i = 0, ii = this.array_.length; i < ii; ++i) {
        this.assertUnique_(this.array_[i], i);
      }
    }

    this.updateLength_();

  }

  /**
   * Remove all elements from the collection.
   * @api
   */
  clear() {
    while (this.getLength() > 0) {
      this.pop();
    }
  }

  /**
   * Add elements to the collection.  This pushes each item in the provided array
   * to the end of the collection.
   * @param {!Array<T>} arr Array.
   * @return {Collection<T>} This collection.
   * @api
   */
  extend(arr) {
    for (let i = 0, ii = arr.length; i < ii; ++i) {
      this.push(arr[i]);
    }
    return this;
  }

  /**
   * Iterate over each element, calling the provided callback.
   * @param {function(T, number, Array<T>): *} f The function to call
   *     for every element. This function takes 3 arguments (the element, the
   *     index and the array). The return value is ignored.
   * @api
   */
  forEach(f) {
    const array = this.array_;
    for (let i = 0, ii = array.length; i < ii; ++i) {
      f(array[i], i, array);
    }
  }

  /**
   * Get a reference to the underlying Array object. Warning: if the array
   * is mutated, no events will be dispatched by the collection, and the
   * collection's "length" property won't be in sync with the actual length
   * of the array.
   * @return {!Array<T>} Array.
   * @api
   */
  getArray() {
    return this.array_;
  }

  /**
   * Get the element at the provided index.
   * @param {number} index Index.
   * @return {T} Element.
   * @api
   */
  item(index) {
    return this.array_[index];
  }

  /**
   * Get the length of this collection.
   * @return {number} The length of the array.
   * @observable
   * @api
   */
  getLength() {
    return this.get(Property.LENGTH);
  }

  /**
   * Insert an element at the provided index.
   * @param {number} index Index.
   * @param {T} elem Element.
   * @api
   */
  insertAt(index, elem) {
    if (this.unique_) {
      this.assertUnique_(elem);
    }
    this.array_.splice(index, 0, elem);
    this.updateLength_();
    this.dispatchEvent(
      new CollectionEvent(CollectionEventType.ADD, elem));
  }

  /**
   * Remove the last element of the collection and return it.
   * Return `undefined` if the collection is empty.
   * @return {T|undefined} Element.
   * @api
   */
  pop() {
    return this.removeAt(this.getLength() - 1);
  }

  /**
   * Insert the provided element at the end of the collection.
   * @param {T} elem Element.
   * @return {number} New length of the collection.
   * @api
   */
  push(elem) {
    if (this.unique_) {
      this.assertUnique_(elem);
    }
    const n = this.getLength();
    this.insertAt(n, elem);
    return this.getLength();
  }

  /**
   * Remove the first occurrence of an element from the collection.
   * @param {T} elem Element.
   * @return {T|undefined} The removed element or undefined if none found.
   * @api
   */
  remove(elem) {
    const arr = this.array_;
    for (let i = 0, ii = arr.length; i < ii; ++i) {
      if (arr[i] === elem) {
        return this.removeAt(i);
      }
    }
    return undefined;
  }

  /**
   * Remove the element at the provided index and return it.
   * Return `undefined` if the collection does not contain this index.
   * @param {number} index Index.
   * @return {T|undefined} Value.
   * @api
   */
  removeAt(index) {
    const prev = this.array_[index];
    this.array_.splice(index, 1);
    this.updateLength_();
    this.dispatchEvent(new CollectionEvent(CollectionEventType.REMOVE, prev));
    return prev;
  }

  /**
   * Set the element at the provided index.
   * @param {number} index Index.
   * @param {T} elem Element.
   * @api
   */
  setAt(index, elem) {
    const n = this.getLength();
    if (index < n) {
      if (this.unique_) {
        this.assertUnique_(elem, index);
      }
      const prev = this.array_[index];
      this.array_[index] = elem;
      this.dispatchEvent(
        new CollectionEvent(CollectionEventType.REMOVE, prev));
      this.dispatchEvent(
        new CollectionEvent(CollectionEventType.ADD, elem));
    } else {
      for (let j = n; j < index; ++j) {
        this.insertAt(j, undefined);
      }
      this.insertAt(index, elem);
    }
  }

  /**
   * @private
   */
  updateLength_() {
    this.set(Property.LENGTH, this.array_.length);
  }

  /**
   * @private
   * @param {T} elem Element.
   * @param {number=} opt_except Optional index to ignore.
   */
  assertUnique_(elem, opt_except) {
    for (let i = 0, ii = this.array_.length; i < ii; ++i) {
      if (this.array_[i] === elem && i !== opt_except) {
        throw new AssertionError(58);
      }
    }
  }
}

Disposable

需要被清理的對象

class Disposable {

  constructor() {
    /**
     * The object has already been disposed.
     * @type {boolean}
     * @private
     */
    this.disposed_ = false;
  }

  /**
   * Clean up.
   */
  dispose() {
    if (!this.disposed_) {
      this.disposed_ = true;
      this.disposeInternal();
    }
  }

  /**
   * Extension point for disposable objects.
   * @protected
   */
  disposeInternal() {}
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末夺克,一起剝皮案震驚了整個濱河市箕宙,隨后出現的幾起案子,更是在濱河造成了極大的恐慌铺纽,老刑警劉巖柬帕,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異室囊,居然都是意外死亡雕崩,警方通過查閱死者的電腦和手機魁索,發(fā)現死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門融撞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人粗蔚,你說我怎么就攤上這事尝偎。” “怎么了鹏控?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵致扯,是天一觀的道長。 經常有香客問我当辐,道長抖僵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任缘揪,我火速辦了婚禮耍群,結果婚禮上,老公的妹妹穿的比我還像新娘找筝。我一直安慰自己蹈垢,他們只是感情好,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布袖裕。 她就那樣靜靜地躺著曹抬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪急鳄。 梳的紋絲不亂的頭發(fā)上谤民,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天堰酿,我揣著相機與錄音,去河邊找鬼张足。 笑死胞锰,一個胖子當著我的面吹牛,可吹牛的內容都是我干的兢榨。 我是一名探鬼主播嗅榕,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吵聪!你這毒婦竟也來了凌那?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤吟逝,失蹤者是張志新(化名)和其女友劉穎帽蝶,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體块攒,經...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡励稳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了囱井。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驹尼。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖庞呕,靈堂內的尸體忽然破棺而出新翎,到底是詐尸還是另有隱情,我是刑警寧澤住练,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布地啰,位于F島的核電站,受9級特大地震影響讲逛,放射性物質發(fā)生泄漏亏吝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一盏混、第九天 我趴在偏房一處隱蔽的房頂上張望蔚鸥。 院中可真熱鬧,春花似錦括饶、人聲如沸株茶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽启盛。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間僵闯,已是汗流浹背卧抗。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鳖粟,地道東北人社裆。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像向图,于是被迫代替她去往敵國和親泳秀。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內容