成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

試讀angular源碼第一章:開場與platformBrowserDynamic

RiverLi / 1079人閱讀

摘要:而且大部分人一聽說就會本能地避開。至于啟動項目,都是這一行開始的。應(yīng)用是模塊化的,它擁有自己的模塊化系統(tǒng),稱作。

開場來個自我介紹

angular 源碼閱讀

項目地址

文章地址

angular 版本:8.0.0-rc.4

歡迎看看我的類angular框架

關(guān)于為什么寫這么一個項目

聲明:僅僅為個人閱讀源碼的理解,不一定完全正確,還需要大佬的指點。

其實市面上很多關(guān)于 vue和react 的源碼閱讀,但是基本上沒有看到關(guān)于 angular 系統(tǒng)性地源碼閱讀。

而且大部分人一聽說 angular 就會本能地避開。

但其實不是的,在我眼里 angular 只是套用了很多后端已有的概念,比如 DI,比如 AOT 等。

之前我寫過一個類 angular 的框架 InDiv,基本上實現(xiàn)了大多數(shù) ng 的裝飾器。

而且在寫這個項目的時候,我從 angular 上學(xué)到了很多。

這次,則希望通過閱讀 angular 的源代碼,學(xué)習(xí)到更多谷歌在設(shè)計模式上的運用,學(xué)習(xí)到更多代碼優(yōu)化和結(jié)構(gòu)的運用。

也有一點私心,希望更多人說 ng大法好 ,哈哈。

前提

希望看之前讀者能先了解一下 typescripy 和 angular 的基礎(chǔ)概念,因為文章里會出現(xiàn)大量的 DI,服務(wù)商啊這類詞

    typescript

    angular文檔

項目結(jié)構(gòu)

項目下只有三個文件夾:angular docs 和 my-demo

- angular: 注釋版angular的ts源代碼
- docs: 文檔位置
- my-demo: 啟動的一個demo項目

通過 tsconfig 把 angular 別名設(shè)置到 angular這個文件夾,來閱讀下 ts 版本的源碼。

啟動app

在瀏覽器端,每個 angular app都是從 main.ts 開始的。

import { enableProdMode } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";

import { AppModule } from "./app/app.module";
import { environment } from "./environments/environment";

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

至于啟動項目,都是這一行 platformBrowserDynamic().bootstrapModule(AppModule) 開始的。

在 angular 的世界中,所有的app都是由 bootstrapModule 根模塊或主模塊啟動的。

Angular 應(yīng)用是模塊化的,它擁有自己的模塊化系統(tǒng),稱作 NgModule。

關(guān)于 NgModule

一個 NgModule 就是一個容器,用于存放一些內(nèi)聚的代碼塊,這些代碼塊專注于某個應(yīng)用領(lǐng)域、某個工作流或一組緊密相關(guān)的功能。

它可以包含一些組件、服務(wù)提供商或其它代碼文件,其作用域由包含它們的 NgModule 定義。 它還可以導(dǎo)入一些由其它模塊中導(dǎo)出的功能,并導(dǎo)出一些指定的功能供其它 NgModule 使用。

每個 Angular 應(yīng)用都至少有一個 NgModule 類,也就是根模塊,它習(xí)慣上命名為 AppModule,并位于一個名叫 app.module.ts 的文件中。

引導(dǎo)這個根模塊就可以啟動你的應(yīng)用

當(dāng) bootstrap(引導(dǎo))根模塊之后,NgModule 會繼而實例化元數(shù)據(jù)中 bootstrap

bootstrap 應(yīng)用的主視圖,稱為根組件。它是應(yīng)用中所有其它視圖的宿主。只有根模塊才應(yīng)該設(shè)置這個 bootstrap 屬性

platform

angular 抽象出 platform,來實現(xiàn)跨平臺。

實例化 angular 根模塊的 bootstrapModule 的方法在瀏覽器端來自 @angular/platform-browser-dynamic。

其實除了 @angular/platform-browser-dynamic 之外還有 @angular/platform-browser。

這兩個模塊的主要區(qū)別是編譯方式的不同, platform-browser-dynamic 提供 JIT 編譯,也就是說編譯在瀏覽器內(nèi)完成,而 platform-browser 提供 AOT 編譯,編譯在本地完成。

至于區(qū)別

platformBrowserDynamic

angular/packages/platform-browser-dynamic/src/platform-browser-dynamic.ts

/**
 * @publicApi
 */
export const platformBrowserDynamic = createPlatformFactory(
    platformCoreDynamic, "browserDynamic", INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS);

platformBrowserDynamic 方法很簡單,就是調(diào)用創(chuàng)建平臺的工廠方法 createPlatformFactory 返回的一個返回值是平臺實例 PlatformRef 的函數(shù)。

createPlatformFactory

angular/packages/core/src/application_ref.ts

/**
 * Creates a factory for a platform
 *
 * @publicApi
 */
export function createPlatformFactory(
    parentPlatformFactory: ((extraProviders");

該方法接受三個參數(shù):

    parentPlatformFactory: ((extraProviders"); 返回父平臺工廠實例的方法

    name: string 平臺的名字

    providers: StaticProvider[] = [] DI的服務(wù)提供者

    首先通過 InjectionToken 創(chuàng)建一個 Platform: ${name} 的值提供商

    然后返回一個方法,接受服務(wù)提供者 extraProviders");,返回一個平臺實例 PlatformRef

createPlatformFactory 返回的方法

    獲取當(dāng)前平臺實例

    如果當(dāng)前平臺實例不存在并且不存在 AllowMultipleToken 這個允許多個令牌的服務(wù)提供者

      父級平臺工廠方法 parentPlatformFactory 存在,則合并服務(wù)提供商并遞歸調(diào)用 parentPlatformFactory

      父級平臺工廠方法 parentPlatformFactory 不存在,則使用注入器創(chuàng)建實例方法 Injector.create 創(chuàng)建實例平臺實例并用 createPlatform 設(shè)置為全局的平臺實例

    調(diào)用 assertPlatform 確認(rèn) IOC 容器中存在 該 marker 的平臺實例并返回

所以創(chuàng)建平臺實例的順序上,應(yīng)該是 合并 browserDynamic 的 provider => 合并 coreDynamic 的 provider => 合并 provider 并創(chuàng)建 core

大概用人話描述就是:

    判斷是否已經(jīng)創(chuàng)建過了

    判斷是否有父 Factory

    如果有父 Factory 就把調(diào)用 Factory 時傳入的 Provider 和調(diào)用 createPlatformFactory 傳入的 Provider 合并,然后調(diào)用父 Factory

    如果沒有父 Factory ,先創(chuàng)建一個 Injector ,然后去創(chuàng)建 PlatformRef 實例

createPlatform

angular/packages/core/src/application_ref.ts

let _platform: PlatformRef;

/**
 * Creates a platform.
 * Platforms have to be eagerly created via this function.
 *
 * @publicApi
 */
export function createPlatform(injector: Injector): PlatformRef {
  if (_platform && !_platform.destroyed &&
      !_platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
    throw new Error(
        "There can be only one platform. Destroy the previous one to create a new one.");
  }
  _platform = injector.get(PlatformRef);
  const inits = injector.get(PLATFORM_INITIALIZER, null);
  if (inits) inits.forEach((init: any) => init());
  return _platform;
}

_platform 是全局的唯一平臺實例。

創(chuàng)建平臺實例關(guān)鍵方法,傳入服務(wù)注入器實例 injector 返回平臺實例:

    確認(rèn)全局的平臺實例存在,狀態(tài)不是被銷毀,并且不存在多個平臺實例

    從注入器中獲取平臺實例

    injector.get(PLATFORM_INITIALIZER, null) 獲取初始化平臺時需要執(zhí)行的函數(shù)并執(zhí)行

回過頭看 platformBrowserDynamic

angular/packages/platform-browser-dynamic/src/platform-browser-dynamic.ts

/**
 * @publicApi
 */
export const platformBrowserDynamic = createPlatformFactory(
    platformCoreDynamic, "browserDynamic", INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS);

重點來了:INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS

這個 providers 究竟提供了什么服務(wù)?

angular/packages/platform-browser-dynamic/src/platform_providers.ts

/**
 * @publicApi
 */
export const INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS: StaticProvider[] = [
  INTERNAL_BROWSER_PLATFORM_PROVIDERS,
  {
    provide: COMPILER_OPTIONS,
    useValue: {providers: [{provide: ResourceLoader, useClass: ResourceLoaderImpl, deps: []}]},
    multi: true
  },
  {provide: PLATFORM_ID, useValue: PLATFORM_BROWSER_ID},
];

除了 COMPILER_OPTIONSPLATFORM_ID,大概重點就是 INTERNAL_BROWSER_PLATFORM_PROVIDERS 了吧。

INTERNAL_BROWSER_PLATFORM_PROVIDERS 來自 @angular/platform-browser

angular/packages/platform-browser/src/browser.ts

export const INTERNAL_BROWSER_PLATFORM_PROVIDERS: StaticProvider[] = [
  {provide: PLATFORM_ID, useValue: PLATFORM_BROWSER_ID},
  {provide: PLATFORM_INITIALIZER, useValue: initDomAdapter, multi: true},
  {provide: PlatformLocation, useClass: BrowserPlatformLocation, deps: [DOCUMENT]},
  {provide: DOCUMENT, useFactory: _document, deps: []},
];

@angular/platform-browser 提供了一些瀏覽器端的ng實現(xiàn):

    PLATFORM_INITIALIZER 是初始化需要執(zhí)行的方法集合 這個很重要

    DOCUMENT 瀏覽器端的 document_document 工廠方法返回 document

在上面,createPlatform 的時候,會 const inits = injector.get(PLATFORM_INITIALIZER, null); if (inits) inits.forEach((init: any) => init()); 依次執(zhí)行 PLATFORM_INITIALIZER 注入的工廠方法。

那么來看看 initDomAdapter 吧:

angular/packages/platform-browser/src/browser.ts

export function initDomAdapter() {
  BrowserDomAdapter.makeCurrent();
  BrowserGetTestability.init();
}

    BrowserDomAdapter.makeCurrent(); 通過 BrowserDomAdapter 的靜態(tài)方法實例化一個 BrowserDomAdapter 全局DOM適配器 ,具體就是實現(xiàn)并封裝了一些在瀏覽器端的方法,具體的可以看 angular/packages/platform-browser/src/browser/browser_adapter.ts 中的 class BrowserDomAdapter extends GenericBrowserDomAdapter

    BrowserGetTestability.init(); 則是初始化 angular 的測試,這個就沒看了

回過頭看下,在創(chuàng)建 platformBrowserDynamic 時候,傳入了返回父平臺實例的方法 platformCoreDynamic

platformCoreDynamic

angular/packages/platform-browser-dynamic/src/platform_core_dynamic.ts

import {COMPILER_OPTIONS, CompilerFactory, PlatformRef, StaticProvider, createPlatformFactory, platformCore} from "@angular/core";
import {JitCompilerFactory} from "./compiler_factory";

/**
 * A platform that included corePlatform and the compiler.
 *
 * @publicApi
 */
export const platformCoreDynamic = createPlatformFactory(platformCore, "coreDynamic", [
  {provide: COMPILER_OPTIONS, useValue: {}, multi: true},
  {provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]},
]);

platformCoreDynamic 又傳入了

    來自 @angular/core 的 平臺核心 platformCore

    平臺名 coreDynamic

    2個靜態(tài)服務(wù)提供者:編譯選項 COMPILER_OPTIONSplatformDynamic 的JIT編譯器工廠 JitCompilerFactory

platformCore

angular/packages/core/src/platform_core_providers.ts

import {PlatformRef, createPlatformFactory} from "./application_ref";
import {PLATFORM_ID} from "./application_tokens";
import {Console} from "./console";
import {Injector, StaticProvider} from "./di";
import {TestabilityRegistry} from "./testability/testability";

const _CORE_PLATFORM_PROVIDERS: StaticProvider[] = [
  // Set a default platform name for platforms that don"t set it explicitly.
  {provide: PLATFORM_ID, useValue: "unknown"},
  // 在這里 PlatformRef 被加入了 injector 并在 createPlatformFactory 中實例化
  {provide: PlatformRef, deps: [Injector]},
  {provide: TestabilityRegistry, deps: []},
  {provide: Console, deps: []},
];

/**
 * This platform has to be included in any other platform
 *
 * @publicApi
 */
export const platformCore = createPlatformFactory(null, "core", _CORE_PLATFORM_PROVIDERS);

platformCore 則是創(chuàng)建了一個返回根平臺工廠實例的方法,并設(shè)置了4個基礎(chǔ)的DI的服務(wù)提供者

    PLATFORM_ID 平臺id

    PlatformRef 在這里 PlatformRef 被加入了 injector 并在后續(xù)的 createPlatformFactory 中通過 createPlatform(Injector.create({providers: injectedProviders, name: desc})); 平臺實例會被實例化

    TestabilityRegistry 可測試性注冊表 測試相關(guān)

    Console 很有意思 angular 把 Console 作為服務(wù)注入了DI,但是 Console 只實現(xiàn)了 log和warn兩個方法

PlatformRef

angular/packages/core/src/application_ref.ts

@Injectable()
export class PlatformRef {
  private _modules: NgModuleRef<any>[] = [];
  private _destroyListeners: Function[] = [];
  private _destroyed: boolean = false;

  /** @internal */
  constructor(private _injector: Injector) {}

  bootstrapModuleFactory(moduleFactory: NgModuleFactory, options");Promise> {
        ...
  }

  bootstrapModule(
      moduleType: Type, compilerOptions: (CompilerOptions&BootstrapOptions)|
      Array = []): Promise> {
    const options = optionsReducer({}, compilerOptions);
    return compileNgModuleFactory(this.injector, options, moduleType)
        .then(moduleFactory => this.bootstrapModuleFactory(moduleFactory, options));
  }

  private _moduleDoBootstrap(moduleRef: InternalNgModuleRef<any>): void {
    ...
  }

  onDestroy(callback: () => void): void { this._destroyListeners.push(callback); }

  get injector(): Injector { return this._injector; }

  destroy() {
    if (this._destroyed) {
      throw new Error("The platform has already been destroyed!");
    }
    this._modules.slice().forEach(module => module.destroy());
    this._destroyListeners.forEach(listener => listener());
    this._destroyed = true;
  }

  get destroyed() { return this._destroyed; }
}

PlatformRef 就是平臺實例的類,有一些方法和屬性等,例如幾個關(guān)鍵的方法

    bootstrapModule 引導(dǎo)根模塊的方法

    bootstrapModuleFactory 實例模塊的工廠方法,會運行 zone.js 并監(jiān)聽事件

    destroy 銷毀平臺實例的方法

這個我們放到后文去說吧

總結(jié)

調(diào)用 platformBrowserDynamic() 并生成平臺實例 PlatformRef 時大概經(jīng)歷了這些:

    調(diào)用 createPlatformFactory 合并平臺 browserDynamicproviders 并觸發(fā)父級平臺 coreDynamic 的平臺工廠函數(shù)

    調(diào)用 createPlatformFactory 合并平臺 coreDynamicproviders 并觸發(fā)父級平臺 core 的平臺工廠函數(shù)

    由于平臺 core 無父級平臺,調(diào)用 Injector.create 創(chuàng)建 PlatformRef 實例,并賦值給全局唯一的平臺實例 _platform

    createPlatform 創(chuàng)建 PlatformRef 的時候,實例化一個 BrowserDomAdapter 全局DOM適配器 ,具體就是實現(xiàn)并封裝了一些在瀏覽器端的方法

    最后斷言,確認(rèn)存在 PlatformRef 實例,并返回 PlatformRef 實例

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/7881.html

相關(guān)文章

  • angular源碼分析之platformBrowserDynamic

    摘要:生成項目后,中的代碼這里調(diào)用了包中導(dǎo)出的函數(shù)這個函數(shù)是瀏覽器平臺的工廠函數(shù)執(zhí)行會返回瀏覽器平臺的實例函數(shù)是通過函數(shù)創(chuàng)建的這個函數(shù)接收個參數(shù)父平臺工廠函數(shù)平臺名稱服務(wù)提供商的數(shù)組顧名思義函數(shù)的作用是創(chuàng)建平臺工廠的函數(shù)在框架被加 cli生成項目后,main.ts中的代碼 import { enableProdMode } from @angular/core; import { platf...

    FWHeart 評論0 收藏0
  • angular源碼分析之platformBrowserDynamic

    摘要:生成項目后,中的代碼這里調(diào)用了包中導(dǎo)出的函數(shù)這個函數(shù)是瀏覽器平臺的工廠函數(shù)執(zhí)行會返回瀏覽器平臺的實例函數(shù)是通過函數(shù)創(chuàng)建的這個函數(shù)接收個參數(shù)父平臺工廠函數(shù)平臺名稱服務(wù)提供商的數(shù)組顧名思義函數(shù)的作用是創(chuàng)建平臺工廠的函數(shù)在框架被加 cli生成項目后,main.ts中的代碼 import { enableProdMode } from @angular/core; import { platf...

    zhoutao 評論0 收藏0
  • Angular 2.x 從0到1 (一)史上最簡單的Angular2教程

    摘要:官方支持微軟出品,是的超集,是的強類型版本作為首選編程語言,使得開發(fā)腳本語言的一些問題可以更早更方便的找到。第一個組件那么我們來為我們的增加一個吧,在命令行窗口輸入。引導(dǎo)過程通過在中引導(dǎo)來啟動應(yīng)用。它們的核心就是。 第一節(jié):Angular 2.0 從0到1 (一)第二節(jié):Angular 2.0 從0到1 (二)第三節(jié):Angular 2.0 從0到1 (三) 第一章:認(rèn)識Angular...

    tuniutech 評論0 收藏0
  • 從安裝認(rèn)識Angular 2

    摘要:首先,要確認(rèn)安裝了,并且創(chuàng)建了目錄并執(zhí)行初始化。想必看見上面的那么多包會一臉懵逼,沒關(guān)系,我第一眼看見這些的那刻,和你現(xiàn)在的表情一樣,下面在適當(dāng)?shù)臅r候我會逐個解釋的,你只需要相信我上面的包都是跑所必須的,缺一不可。 關(guān)于介紹,只說一句:Angular 2是一個強大、全面、龐大的MVVM框架。 安裝 安裝,也算是一個坎,因為你需要安裝一大堆東西,卻不知道每個東西是做什么的,盡管有Angu...

    xietao3 評論0 收藏0
  • angular2初入眼簾之-多components協(xié)作

    摘要:我們使用了模式書寫,并引入了思想,這些以前只在里見到的設(shè)計,現(xiàn)在里也有體現(xiàn),并且在本章中會著重講解多的協(xié)作。如果之前寫過,那對于這種書寫方式一定無比熟悉。每次數(shù)據(jù)的變更,無論是還是,都將變化冒泡到,然后由再向下逐級推送各組件是否重繪。 前集回顧 在上一章里我們講了如何在angular2下開發(fā)一個component(還沒做的趕緊去學(xué)吧)。我們使用了Unidirectional Data ...

    dreamans 評論0 收藏0

發(fā)表評論

0條評論

RiverLi

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<