试读angular源码第一章:开场与platformBrowserDynamic

直接看人话总结html

开场来个自我介绍

angular 源码阅读vue

项目地址react

该文章项目地址git

angular 版本:8.0.0-rc.4github

欢迎看看个人类angular框架typescript

文章列表

试读angular源码第一章:开场与platformBrowserDynamicbootstrap

试读angular源码第二章:引导模块bootstrapModule后端

试读angular源码第三章:初始化zone设计模式

试读angular源码第四章:angular模块及JIT编译模块浏览器

关于为何写这么一个项目

声明:仅仅为我的阅读源码的理解,不必定彻底正确,还须要大佬的指点。

其实市面上不少关于 vue和react 的源码阅读,可是基本上没有看到关于 angular 系统性地源码阅读。

并且大部分人一据说 angular 就会本能地避开。

angular三连

但其实不是的,在我眼里 angular 只是套用了不少后端已有的概念,好比 DI,好比 AOT 等。

以前我写过一个类 angular 的框架 InDiv,基本上实现了大多数 ng 的装饰器。

并且在写这个项目的时候,我从 angular 上学到了不少。

此次,则但愿经过阅读 angular 的源代码,学习到更多谷歌在设计模式上的运用,学习到更多代码优化和结构的运用。

也有一点私心,但愿更多人说 ng大法好 ,哈哈。

一块儿学习angular

前提

但愿看以前读者能先了解一下 typescripy 和 angular 的基础概念,由于文章里会出现大量的 DI,服务商啊这类词

  1. typescript
  2. angular文档

angular的基础架构

项目结构

项目下只有三个文件夹:angular docs 和 my-demo

- angular: 注释版angular的ts源代码
- docs: 文档位置
- my-demo: 启动的一个demo项目
复制代码

经过 tsconfig 把 angular 别名设置到 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都是由 platformBrowserDynamic() 提供的 bootstrapModule 方法引导根模块或主模块启动的。

platform平台

angular 抽象出 platform,来实现跨平台。

实例化 angular 根模块的 bootstrapModule 的方法在浏览器端来自 @angular/platform-browser-dynamic

其实除了 @angular/platform-browser-dynamic 以外还有 @angular/platform-browser

这两个模块的主要区别是编译方式的不一样, platform-browser-dynamic 提供 JIT 编译,也就是说编译在浏览器内完成,而 platform-browser 提供 AOT 编译,编译在本地完成。

至于区别

platformBrowserDynamic

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

/** * @publicApi */
export const platformBrowserDynamic = createPlatformFactory(
    platformCoreDynamic, 'browserDynamic', INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS);
复制代码

platformBrowserDynamic 方法很简单,就是调用建立平台的工厂方法 createPlatformFactory 返回的一个返回值是平台实例 PlatformRef 的函数

createPlatformFactory

angular/packages/core/src/application_ref.ts

/**
 * Creates a factory for a platform
 * 
 * 1. 判断是否已经建立过了
 * 2. 判断是否有父 `Factory`
 * 3. 若是有父 `Factory` 就把调用 `Factory` 时传入的 `Provider` 和调用 `createPlatformFactory` 传入的 `Provider` 合并,而后调用父 `Factory`
 * 4. 若是没有父 `Factory` ,先建立一个 `Injector` ,而后去建立 `PlatformRef` 实例
 *
 * @publicApi
 */
export function createPlatformFactory(
    parentPlatformFactory: ((extraProviders?: StaticProvider[]) => PlatformRef) | null,
    name: string, providers: StaticProvider[] = []): (extraProviders?: StaticProvider[]) =>
    PlatformRef {
  const desc = `Platform: ${name}`;
  const marker = new InjectionToken(desc);
  return (extraProviders: StaticProvider[] = []) => {
    let platform = getPlatform();
    // 注释:判断是否存在平台实例
    if (!platform || platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
      if (parentPlatformFactory) {
        // 注释:调用父平台方法
        parentPlatformFactory(
            providers.concat(extraProviders).concat({provide: marker, useValue: true}));
      } else {
        const injectedProviders: StaticProvider[] =
            providers.concat(extraProviders).concat({provide: marker, useValue: true});
        // 注释:Injector.create建立平台实例,并获取设置为全局平台实例
        createPlatform(Injector.create({providers: injectedProviders, name: desc}));
      }
    }
    return assertPlatform(marker);
  };
}
复制代码

该方法接受三个参数:

  1. parentPlatformFactory: ((extraProviders?: StaticProvider[]) => PlatformRef) | null 返回父平台工厂实例的方法

  2. name: string 平台的名字

  3. providers: StaticProvider[] = [] DI的服务提供者

  4. 首先经过 InjectionToken 建立一个 Platform: ${name}值提供商

  5. 而后返回一个方法,接受服务提供者 extraProviders?: StaticProvider[],返回一个平台实例 PlatformRef

createPlatformFactory 返回的方法

  1. 获取当前平台实例
  2. 若是当前平台实例不存在而且不存在 AllowMultipleToken 这个容许多个令牌的服务提供者
    1. 父级平台工厂方法 parentPlatformFactory 存在,则合并服务提供商并递归调用 parentPlatformFactory
    2. 父级平台工厂方法 parentPlatformFactory 不存在,则使用注入器建立实例方法 Injector.create 建立实例平台实例并用 createPlatform 设置为全局的平台实例
  3. 调用 assertPlatform 确认 IOC 容器中存在 该 marker 的平台实例并返回

因此建立平台实例的顺序上,应该是 合并 browserDynamic 的 provider => 合并 coreDynamic 的 provider => 合并 provider 并建立 core

大概用人话描述就是:

  1. 判断是否已经建立过了
  2. 判断是否有父 Factory
  3. 若是有父 Factory 就把调用 Factory 时传入的 Provider 和调用 createPlatformFactory 传入的 Provider 合并,而后调用父 Factory
  4. 若是没有父 Factory ,先建立一个 Injector ,而后去建立 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);
  // 注释:初始化平台时将执行的函数,平台browserDynamic提供
  const inits = injector.get(PLATFORM_INITIALIZER, null);
  if (inits) inits.forEach((init: any) => init());
  return _platform;
}
复制代码

_platform 是全局的惟一平台实例。

建立平台实例关键方法,传入服务注入器实例 injector 返回平台实例:

  1. 确认全局的平台实例存在,状态不是被销毁,而且不存在多个平台实例
  2. 从注入器中获取平台实例
  3. injector.get(PLATFORM_INITIALIZER, null) 获取初始化平台时须要执行的函数并执行

回过头看 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 究竟提供了什么服务?

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实现:

  1. PLATFORM_INITIALIZER 是初始化须要执行的方法集合 这个很重要
  2. DOCUMENT 浏览器端的 document_document 工厂方法返回 document

在上面,createPlatform 的时候,会 const inits = injector.get(PLATFORM_INITIALIZER, null); if (inits) inits.forEach((init: any) => init()); 依次执行 PLATFORM_INITIALIZER 注入的工厂方法。

那么来看看 initDomAdapter 吧:

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

export function initDomAdapter() {
  BrowserDomAdapter.makeCurrent();
  BrowserGetTestability.init();
}
复制代码
  1. BrowserDomAdapter.makeCurrent(); 经过 BrowserDomAdapter 的静态方法实例化一个 BrowserDomAdapter 全局DOM适配器 ,具体就是实现并封装了一些在浏览器端的方法,具体的能够看 angular/packages/platform-browser/src/browser/browser_adapter.ts 中的 class BrowserDomAdapter extends GenericBrowserDomAdapter
  2. BrowserGetTestability.init(); 则是初始化 angular 的测试,这个就没看了

回过头看下,在建立 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 又传入了

  1. 来自 @angular/core 的 平台核心 platformCore
  2. 平台名 coreDynamic
  3. 2个静态服务提供者:编译选项 COMPILER_OPTIONSplatformDynamicJIT编译器工厂 JitCompilerFactory

JitCompilerFactory

重点来了

一块儿看下 JitCompilerFactory

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

/** * @publicApi */
export class JitCompilerFactory implements CompilerFactory {
  private _defaultOptions: CompilerOptions[];

  /* @internal */
  constructor(defaultOptions: CompilerOptions[]) {
    const compilerOptions: CompilerOptions = {
      useJit: true,
      defaultEncapsulation: ViewEncapsulation.Emulated,
      missingTranslation: MissingTranslationStrategy.Warning,
    };

    this._defaultOptions = [compilerOptions, ...defaultOptions];
  }
  createCompiler(options: CompilerOptions[] = []): Compiler {
    const opts = _mergeOptions(this._defaultOptions.concat(options));
    const injector = Injector.create([
      // 注释:编译器 Compiler 在这里被替换成 CompilerImpl 
      COMPILER_PROVIDERS, {
        provide: CompilerConfig,
        useFactory: () => {
          return new CompilerConfig({
            // let explicit values from the compiler options overwrite options
            // from the app providers
            useJit: opts.useJit,
            jitDevMode: isDevMode(),
            // let explicit values from the compiler options overwrite options
            // from the app providers
            defaultEncapsulation: opts.defaultEncapsulation,
            missingTranslation: opts.missingTranslation,
            preserveWhitespaces: opts.preserveWhitespaces,
          });
        },
        deps: []
      },
      opts.providers !
    ]);
    return injector.get(Compiler);
  }
}
复制代码

编译器在 COMPILER_PROVIDERS 做为服务提供商被提供给注射器这里很重要,后面的编译器会大量用到

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

/** * A set of providers that provide `JitCompiler` and its dependencies to use for * template compilation. */
export const COMPILER_PROVIDERS = <StaticProvider[]>[
  // 注释:这里也是一个核心点-编译反射器
  {provide: CompileReflector, useValue: new JitReflector()},
  // 注释:ResourceLoader- 资源加载器
  {provide: ResourceLoader, useValue: _NO_RESOURCE_LOADER},
  // 注释:jit 摘要解析器
  {provide: JitSummaryResolver, deps: []},
  // 注释:摘要解析器
  {provide: SummaryResolver, useExisting: JitSummaryResolver},
  {provide: Console, deps: []},
  // 注释:语法分析器
  {provide: Lexer, deps: []},
  // 注释:解析器器
  {provide: Parser, deps: [Lexer]},
  // 注释:基本的HTML解析器
  {
    provide: baseHtmlParser,
    useClass: HtmlParser,
    deps: [],
  },
  // 注释:国际化的HTML解析器
  {
    provide: I18NHtmlParser,
    useFactory: (parser: HtmlParser, translations: string | null, format: string,
                 config: CompilerConfig, console: Console) => {
      translations = translations || '';
      const missingTranslation =
          translations ? config.missingTranslation ! : MissingTranslationStrategy.Ignore;
      // 注释:new 国际化的HTML解析器
      return new I18NHtmlParser(parser, translations, format, missingTranslation, console);
    },
    deps: [
      baseHtmlParser,
      [new Optional(), new Inject(TRANSLATIONS)],
      [new Optional(), new Inject(TRANSLATIONS_FORMAT)],
      [CompilerConfig],
      [Console],
    ]
  },
  {
    provide: HtmlParser,
    useExisting: I18NHtmlParser,
  },
  // 注释:模板解析器
  {
    provide: TemplateParser, deps: [CompilerConfig, CompileReflector,
    Parser, ElementSchemaRegistry,
    I18NHtmlParser, Console]
  },
  { provide: JitEvaluator, useClass: JitEvaluator, deps: [] },
  // 注释:指令规范器
  { provide: DirectiveNormalizer, deps: [ResourceLoader, UrlResolver, HtmlParser, CompilerConfig]},
  // 注释:元数据解析器
  { provide: CompileMetadataResolver, deps: [CompilerConfig, HtmlParser, NgModuleResolver,
                      DirectiveResolver, PipeResolver,
                      SummaryResolver,
                      ElementSchemaRegistry,
                      DirectiveNormalizer, Console,
                      [Optional, StaticSymbolCache],
                      CompileReflector,
                      [Optional, ERROR_COLLECTOR_TOKEN]]},
  DEFAULT_PACKAGE_URL_PROVIDER,
  // 注释:样式编译器
  { provide: StyleCompiler, deps: [UrlResolver]},
  // 注释:view 编译器
  { provide: ViewCompiler, deps: [CompileReflector]},
  // 注释:NgModule编译器
  { provide: NgModuleCompiler, deps: [CompileReflector] },
  // 注释:编译器配置项目
  { provide: CompilerConfig, useValue: new CompilerConfig()},
  // 注释:JIT时,Compiler的服务供应商 CompilerImpl
  { provide: Compiler, useClass: CompilerImpl, deps: [Injector, CompileMetadataResolver,
                                TemplateParser, StyleCompiler,
                                ViewCompiler, NgModuleCompiler,
                                SummaryResolver, CompileReflector, JitEvaluator, CompilerConfig,
                                Console]},
  // 注释:DOM schema
  { provide: DomElementSchemaRegistry, deps: []},
  // 注释:Element schema
  { provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},
  // 注释:URL解析器
  { provide: UrlResolver, deps: [PACKAGE_ROOT_URL]},
  // 注释:指令解析器
  { provide: DirectiveResolver, deps: [CompileReflector]},
  // 注释:管道解析器
  { provide: PipeResolver, deps: [CompileReflector]},
  // 注释:模块解析器
  { provide: NgModuleResolver, deps: [CompileReflector]},
];
复制代码

最后,其实也是建立了一个 injector,而后获取了 编译器实例 Compiler,因此:

大概就是 @angular/platform-browser-dynamic 提供 JIT 编译 的缘由了吧。

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 则是建立了一个返回根平台工厂实例的方法,并设置了4个基础的DI的服务提供者

  1. PLATFORM_ID 平台id
  2. PlatformRef 在这里 PlatformRef 被加入了 injector 并在后续的 createPlatformFactory 中经过 createPlatform(Injector.create({providers: injectedProviders, name: desc})); 平台实例会被实例化
  3. TestabilityRegistry 可测试性注册表 测试相关
  4. Console 颇有意思 angular 把 Console 做为服务注入了DI,可是 Console 只实现了 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<M>(moduleFactory: NgModuleFactory<M>, options?: BootstrapOptions):
      Promise<NgModuleRef<M>> {
        ...
  }

  bootstrapModule<M>(
      moduleType: Type<M>, compilerOptions: (CompilerOptions&BootstrapOptions)|
      Array<CompilerOptions&BootstrapOptions> = []): Promise<NgModuleRef<M>> {
    // 注释:bootstrapModule` 首先经过 `optionsReducer` 递归 reduce 将编译器选项 `compilerOptions` 拍平为对象
    const options = optionsReducer({}, compilerOptions);
    // 注释:这里获取到编译后的模块工厂,而后返回给 bootstrapModuleFactory建立模块
    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 就是平台实例的类,有一些方法和属性等,例如几个关键的方法

  1. bootstrapModule 引导根模块的方法
  2. bootstrapModuleFactory 实例模块的工厂方法,会运行 zone.js 并监听事件
  3. destroy 销毁平台实例的方法

这个咱们放到后文去说吧

总结

调用 platformBrowserDynamic() 并生成平台实例 PlatformRef 时大概经历了这些:

  1. 调用 createPlatformFactory 合并平台 browserDynamicproviders 并触发父级平台 coreDynamic 的平台工厂函数 平台 browserDynamic 提供了 PLATFORM_INITIALIZER 平台初始化函数和 BrowserDomAdapter 全局DOM适配器这个服务供应商
  2. 调用 createPlatformFactory 合并平台 coreDynamicproviders 并触发父级平台 core 的平台工厂函数 平台 coreDynamic 提供了 JitCompilerFactory 运行时编译器,JitCompilerFactory 又经过建立 COMPILER_PROVIDERS 建立了编译器实例 因此 @angular/platform-browser-dynamic 提供 JIT运行时 编译
  3. 平台 core 提供了 PlatformRef 平台实例这个服务供应商
  4. 因为平台 core 无父级平台,调用 Injector.create 建立 PlatformRef 实例,并赋值给全局惟一的平台实例 _platform
  5. createPlatform 建立 PlatformRef 的时候,实例化一个 BrowserDomAdapter 全局DOM适配器 ,具体就是实现并封装了一些在浏览器端的方法
  6. 最后断言,确认存在 PlatformRef 实例,并返回 PlatformRef 实例

因此大概,@angular/platform-browser-dynamic 提供了运行时编译,实现并封装了浏览器方法

相关文章
相关标签/搜索