摘要:而且大部分人一聽(tīng)說(shuō)就會(huì)本能地避開(kāi)。至于啟動(dòng)項(xiàng)目,都是這一行開(kāi)始的。應(yīng)用是模塊化的,它擁有自己的模塊化系統(tǒng),稱(chēng)作。
開(kāi)場(chǎng)來(lái)個(gè)自我介紹angular 源碼閱讀
項(xiàng)目地址
文章地址
angular 版本:8.0.0-rc.4
歡迎看看我的類(lèi)angular框架
關(guān)于為什么寫(xiě)這么一個(gè)項(xiàng)目聲明:僅僅為個(gè)人閱讀源碼的理解,不一定完全正確,還需要大佬的指點(diǎn)。
其實(shí)市面上很多關(guān)于 vue和react 的源碼閱讀,但是基本上沒(méi)有看到關(guān)于 angular 系統(tǒng)性地源碼閱讀。
而且大部分人一聽(tīng)說(shuō) angular 就會(huì)本能地避開(kāi)。
但其實(shí)不是的,在我眼里 angular 只是套用了很多后端已有的概念,比如 DI,比如 AOT 等。
之前我寫(xiě)過(guò)一個(gè)類(lèi) angular 的框架 InDiv,基本上實(shí)現(xiàn)了大多數(shù) ng 的裝飾器。
而且在寫(xiě)這個(gè)項(xiàng)目的時(shí)候,我從 angular 上學(xué)到了很多。
這次,則希望通過(guò)閱讀 angular 的源代碼,學(xué)習(xí)到更多谷歌在設(shè)計(jì)模式上的運(yùn)用,學(xué)習(xí)到更多代碼優(yōu)化和結(jié)構(gòu)的運(yùn)用。
也有一點(diǎn)私心,希望更多人說(shuō) ng大法好 ,哈哈。
前提
希望看之前讀者能先了解一下 typescripy 和 angular 的基礎(chǔ)概念,因?yàn)槲恼吕飼?huì)出現(xiàn)大量的 DI,服務(wù)商啊這類(lèi)詞
typescript
angular文檔
項(xiàng)目結(jié)構(gòu)
項(xiàng)目下只有三個(gè)文件夾:angular docs 和 my-demo
- angular: 注釋版angular的ts源代碼 - docs: 文檔位置 - my-demo: 啟動(dòng)的一個(gè)demo項(xiàng)目
通過(guò) tsconfig 把 angular 別名設(shè)置到 angular這個(gè)文件夾,來(lái)閱讀下 ts 版本的源碼。
啟動(dòng)app在瀏覽器端,每個(gè) angular app都是從 main.ts 開(kāi)始的。
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));
至于啟動(dòng)項(xiàng)目,都是這一行 platformBrowserDynamic().bootstrapModule(AppModule) 開(kāi)始的。
在 angular 的世界中,所有的app都是由 bootstrapModule 根模塊或主模塊啟動(dòng)的。
Angular 應(yīng)用是模塊化的,它擁有自己的模塊化系統(tǒng),稱(chēng)作 NgModule。
關(guān)于 NgModule
一個(gè) NgModule 就是一個(gè)容器,用于存放一些內(nèi)聚的代碼塊,這些代碼塊專(zhuān)注于某個(gè)應(yīng)用領(lǐng)域、某個(gè)工作流或一組緊密相關(guān)的功能。
它可以包含一些組件、服務(wù)提供商或其它代碼文件,其作用域由包含它們的 NgModule 定義。 它還可以導(dǎo)入一些由其它模塊中導(dǎo)出的功能,并導(dǎo)出一些指定的功能供其它 NgModule 使用。
每個(gè) Angular 應(yīng)用都至少有一個(gè) NgModule 類(lèi),也就是根模塊,它習(xí)慣上命名為 AppModule,并位于一個(gè)名叫 app.module.ts 的文件中。
引導(dǎo)這個(gè)根模塊就可以啟動(dòng)你的應(yīng)用。
當(dāng) bootstrap(引導(dǎo))根模塊之后,NgModule 會(huì)繼而實(shí)例化元數(shù)據(jù)中 bootstrap。
bootstrap 應(yīng)用的主視圖,稱(chēng)為根組件。它是應(yīng)用中所有其它視圖的宿主。只有根模塊才應(yīng)該設(shè)置這個(gè) bootstrap 屬性
platformangular 抽象出 platform,來(lái)實(shí)現(xiàn)跨平臺(tái)。
實(shí)例化 angular 根模塊的 bootstrapModule 的方法在瀏覽器端來(lái)自 @angular/platform-browser-dynamic。
其實(shí)除了 @angular/platform-browser-dynamic 之外還有 @angular/platform-browser。
這兩個(gè)模塊的主要區(qū)別是編譯方式的不同, platform-browser-dynamic 提供 JIT 編譯,也就是說(shuō)編譯在瀏覽器內(nèi)完成,而 platform-browser 提供 AOT 編譯,編譯在本地完成。
至于區(qū)別
platformBrowserDynamicangular/packages/platform-browser-dynamic/src/platform-browser-dynamic.ts
/**
* @publicApi
*/
export const platformBrowserDynamic = createPlatformFactory(
platformCoreDynamic, "browserDynamic", INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS);
platformBrowserDynamic 方法很簡(jiǎn)單,就是調(diào)用創(chuàng)建平臺(tái)的工廠方法 createPlatformFactory 返回的一個(gè)返回值是平臺(tái)實(shí)例 PlatformRef 的函數(shù)。
createPlatformFactoryangular/packages/core/src/application_ref.ts
/** * Creates a factory for a platform * * @publicApi */ export function createPlatformFactory( parentPlatformFactory: ((extraProviders");
該方法接受三個(gè)參數(shù):
parentPlatformFactory: ((extraProviders"); 返回父平臺(tái)工廠實(shí)例的方法
name: string 平臺(tái)的名字
providers: StaticProvider[] = [] DI的服務(wù)提供者
首先通過(guò) InjectionToken 創(chuàng)建一個(gè) Platform: ${name} 的值提供商
然后返回一個(gè)方法,接受服務(wù)提供者 extraProviders");,返回一個(gè)平臺(tái)實(shí)例 PlatformRef
createPlatformFactory 返回的方法
獲取當(dāng)前平臺(tái)實(shí)例
如果當(dāng)前平臺(tái)實(shí)例不存在并且不存在 AllowMultipleToken 這個(gè)允許多個(gè)令牌的服務(wù)提供者
父級(jí)平臺(tái)工廠方法 parentPlatformFactory 存在,則合并服務(wù)提供商并遞歸調(diào)用 parentPlatformFactory
父級(jí)平臺(tái)工廠方法 parentPlatformFactory 不存在,則使用注入器創(chuàng)建實(shí)例方法 Injector.create 創(chuàng)建實(shí)例平臺(tái)實(shí)例并用 createPlatform 設(shè)置為全局的平臺(tái)實(shí)例
調(diào)用 assertPlatform 確認(rèn) IOC 容器中存在 該 marker 的平臺(tái)實(shí)例并返回
所以創(chuàng)建平臺(tái)實(shí)例的順序上,應(yīng)該是 合并 browserDynamic 的 provider => 合并 coreDynamic 的 provider => 合并 provider 并創(chuàng)建 core
大概用人話描述就是:
判斷是否已經(jīng)創(chuàng)建過(guò)了
判斷是否有父 Factory
如果有父 Factory 就把調(diào)用 Factory 時(shí)傳入的 Provider 和調(diào)用 createPlatformFactory 傳入的 Provider 合并,然后調(diào)用父 Factory
如果沒(méi)有父 Factory ,先創(chuàng)建一個(gè) Injector ,然后去創(chuàng)建 PlatformRef 實(shí)例
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 是全局的唯一平臺(tái)實(shí)例。
創(chuàng)建平臺(tái)實(shí)例關(guān)鍵方法,傳入服務(wù)注入器實(shí)例 injector 返回平臺(tái)實(shí)例:
確認(rèn)全局的平臺(tái)實(shí)例存在,狀態(tài)不是被銷(xiāo)毀,并且不存在多個(gè)平臺(tái)實(shí)例
從注入器中獲取平臺(tái)實(shí)例
injector.get(PLATFORM_INITIALIZER, null) 獲取初始化平臺(tái)時(shí)需要執(zhí)行的函數(shù)并執(zhí)行
回過(guò)頭看 platformBrowserDynamic:
angular/packages/platform-browser-dynamic/src/platform-browser-dynamic.ts
/**
* @publicApi
*/
export const platformBrowserDynamic = createPlatformFactory(
platformCoreDynamic, "browserDynamic", INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS);
重點(diǎn)來(lái)了:INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS
這個(gè) 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_OPTIONS 和 PLATFORM_ID,大概重點(diǎn)就是 INTERNAL_BROWSER_PLATFORM_PROVIDERS 了吧。
INTERNAL_BROWSER_PLATFORM_PROVIDERS 來(lái)自 @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實(shí)現(xiàn):
PLATFORM_INITIALIZER 是初始化需要執(zhí)行的方法集合 這個(gè)很重要
DOCUMENT 瀏覽器端的 document ,_document 工廠方法返回 document
在上面,createPlatform 的時(shí)候,會(huì) const inits = injector.get(PLATFORM_INITIALIZER, null); if (inits) inits.forEach((init: any) => init()); 依次執(zhí)行 PLATFORM_INITIALIZER 注入的工廠方法。
那么來(lái)看看 initDomAdapter 吧:
angular/packages/platform-browser/src/browser.ts
export function initDomAdapter() {
BrowserDomAdapter.makeCurrent();
BrowserGetTestability.init();
}
BrowserDomAdapter.makeCurrent(); 通過(guò) BrowserDomAdapter 的靜態(tài)方法實(shí)例化一個(gè) BrowserDomAdapter 全局DOM適配器 ,具體就是實(shí)現(xiàn)并封裝了一些在瀏覽器端的方法,具體的可以看 angular/packages/platform-browser/src/browser/browser_adapter.ts 中的 class BrowserDomAdapter extends GenericBrowserDomAdapter
BrowserGetTestability.init(); 則是初始化 angular 的測(cè)試,這個(gè)就沒(méi)看了
回過(guò)頭看下,在創(chuàng)建 platformBrowserDynamic 時(shí)候,傳入了返回父平臺(tái)實(shí)例的方法 platformCoreDynamic
platformCoreDynamicangular/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 又傳入了
來(lái)自 @angular/core 的 平臺(tái)核心 platformCore
平臺(tái)名 coreDynamic
2個(gè)靜態(tài)服務(wù)提供者:編譯選項(xiàng) COMPILER_OPTIONS 和 platformDynamic 的JIT編譯器工廠 JitCompilerFactory
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 中實(shí)例化
{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)建了一個(gè)返回根平臺(tái)工廠實(shí)例的方法,并設(shè)置了4個(gè)基礎(chǔ)的DI的服務(wù)提供者
PLATFORM_ID 平臺(tái)id
PlatformRef 在這里 PlatformRef 被加入了 injector 并在后續(xù)的 createPlatformFactory 中通過(guò) createPlatform(Injector.create({providers: injectedProviders, name: desc})); 平臺(tái)實(shí)例會(huì)被實(shí)例化
TestabilityRegistry 可測(cè)試性注冊(cè)表 測(cè)試相關(guān)
Console 很有意思 angular 把 Console 作為服務(wù)注入了DI,但是 Console 只實(shí)現(xiàn)了 log和warn兩個(gè)方法
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 就是平臺(tái)實(shí)例的類(lèi),有一些方法和屬性等,例如幾個(gè)關(guān)鍵的方法
bootstrapModule 引導(dǎo)根模塊的方法
bootstrapModuleFactory 實(shí)例模塊的工廠方法,會(huì)運(yùn)行 zone.js 并監(jiān)聽(tīng)事件
destroy 銷(xiāo)毀平臺(tái)實(shí)例的方法
這個(gè)我們放到后文去說(shuō)吧
總結(jié)調(diào)用 platformBrowserDynamic() 并生成平臺(tái)實(shí)例 PlatformRef 時(shí)大概經(jīng)歷了這些:
調(diào)用 createPlatformFactory 合并平臺(tái) browserDynamic 的 providers 并觸發(fā)父級(jí)平臺(tái) coreDynamic 的平臺(tái)工廠函數(shù)
調(diào)用 createPlatformFactory 合并平臺(tái) coreDynamic 的 providers 并觸發(fā)父級(jí)平臺(tái) core 的平臺(tái)工廠函數(shù)
由于平臺(tái) core 無(wú)父級(jí)平臺(tái),調(diào)用 Injector.create 創(chuàng)建 PlatformRef 實(shí)例,并賦值給全局唯一的平臺(tái)實(shí)例 _platform
在 createPlatform 創(chuàng)建 PlatformRef 的時(shí)候,實(shí)例化一個(gè) BrowserDomAdapter 全局DOM適配器 ,具體就是實(shí)現(xiàn)并封裝了一些在瀏覽器端的方法
最后斷言,確認(rèn)存在 PlatformRef 實(shí)例,并返回 PlatformRef 實(shí)例
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/7881.html
摘要:生成項(xiàng)目后,中的代碼這里調(diào)用了包中導(dǎo)出的函數(shù)這個(gè)函數(shù)是瀏覽器平臺(tái)的工廠函數(shù)執(zhí)行會(huì)返回瀏覽器平臺(tái)的實(shí)例函數(shù)是通過(guò)函數(shù)創(chuàng)建的這個(gè)函數(shù)接收個(gè)參數(shù)父平臺(tái)工廠函數(shù)平臺(tái)名稱(chēng)服務(wù)提供商的數(shù)組顧名思義函數(shù)的作用是創(chuàng)建平臺(tái)工廠的函數(shù)在框架被加 cli生成項(xiàng)目后,main.ts中的代碼 import { enableProdMode } from @angular/core; import { platf...
摘要:生成項(xiàng)目后,中的代碼這里調(diào)用了包中導(dǎo)出的函數(shù)這個(gè)函數(shù)是瀏覽器平臺(tái)的工廠函數(shù)執(zhí)行會(huì)返回瀏覽器平臺(tái)的實(shí)例函數(shù)是通過(guò)函數(shù)創(chuàng)建的這個(gè)函數(shù)接收個(gè)參數(shù)父平臺(tái)工廠函數(shù)平臺(tái)名稱(chēng)服務(wù)提供商的數(shù)組顧名思義函數(shù)的作用是創(chuàng)建平臺(tái)工廠的函數(shù)在框架被加 cli生成項(xiàng)目后,main.ts中的代碼 import { enableProdMode } from @angular/core; import { platf...
摘要:官方支持微軟出品,是的超集,是的強(qiáng)類(lèi)型版本作為首選編程語(yǔ)言,使得開(kāi)發(fā)腳本語(yǔ)言的一些問(wèn)題可以更早更方便的找到。第一個(gè)組件那么我們來(lái)為我們的增加一個(gè)吧,在命令行窗口輸入。引導(dǎo)過(guò)程通過(guò)在中引導(dǎo)來(lái)啟動(dòng)應(yīng)用。它們的核心就是。 第一節(jié):Angular 2.0 從0到1 (一)第二節(jié):Angular 2.0 從0到1 (二)第三節(jié):Angular 2.0 從0到1 (三) 第一章:認(rèn)識(shí)Angular...
摘要:首先,要確認(rèn)安裝了,并且創(chuàng)建了目錄并執(zhí)行初始化。想必看見(jiàn)上面的那么多包會(huì)一臉懵逼,沒(méi)關(guān)系,我第一眼看見(jiàn)這些的那刻,和你現(xiàn)在的表情一樣,下面在適當(dāng)?shù)臅r(shí)候我會(huì)逐個(gè)解釋的,你只需要相信我上面的包都是跑所必須的,缺一不可。 關(guān)于介紹,只說(shuō)一句:Angular 2是一個(gè)強(qiáng)大、全面、龐大的MVVM框架。 安裝 安裝,也算是一個(gè)坎,因?yàn)槟阈枰惭b一大堆東西,卻不知道每個(gè)東西是做什么的,盡管有Angu...
摘要:我們使用了模式書(shū)寫(xiě),并引入了思想,這些以前只在里見(jiàn)到的設(shè)計(jì),現(xiàn)在里也有體現(xiàn),并且在本章中會(huì)著重講解多的協(xié)作。如果之前寫(xiě)過(guò),那對(duì)于這種書(shū)寫(xiě)方式一定無(wú)比熟悉。每次數(shù)據(jù)的變更,無(wú)論是還是,都將變化冒泡到,然后由再向下逐級(jí)推送各組件是否重繪。 前集回顧 在上一章里我們講了如何在angular2下開(kāi)發(fā)一個(gè)component(還沒(méi)做的趕緊去學(xué)吧)。我們使用了Unidirectional Data ...
閱讀 3542·2021-11-25 09:43
閱讀 2764·2021-09-22 15:54
閱讀 652·2019-08-30 15:55
閱讀 1034·2019-08-30 15:55
閱讀 2076·2019-08-30 15:55
閱讀 1802·2019-08-30 15:53
閱讀 3534·2019-08-30 15:52
閱讀 2107·2019-08-30 12:55