Попытка понять, как Angular v4.4 на самом деле работает с platformBrowserDynami c и PlatformRef - PullRequest
1 голос
/ 24 апреля 2020

Я унаследовал приложение Angular 4.4, используя Webpack v3.5 и TypeScript v2.3.3. Я изо всех сил пытался понять, что на самом деле делает код, который я вижу (импортировал), и отчасти я не понимаю, как он может быть правильным. Обратите внимание, я выполнил некоторое упрощение в следующем коде, чтобы упростить фокусировку на вопросе.

Мой main.ts содержит следующие утверждения (среди прочих):

import { platformBrowserDynamic }  from '@angular/platform-browser-dynamic;'
return platformBrowserDynamic().bootstrapModule(AppModule).then...;

Итак , platformBrowserDynami c - это функция, которая вызывается там. Между тем, в @ angular / platform-browser-dynamic / platform-browser-dynamici c .d.ts это утверждение

export * from './src/platform-browser-dynamic';

В @ angular / platform-browser-dynamic / src / platform- browser-dynamic / platform-browser-dynamici c .d.ts - это следующие операторы:

import { PlatformRef, Provider } from '@angular/core';
export declare const platformBrowserDynamic: (extraProviders?: Provider[] | undefined) => PlatformRef;

Другими словами, platformBrowserDynami c - это функция, возвращающая значение PlatformRef. В @ angular / core / core.d.ts этот экспорт:

export * from './public_api';

В @ angular / core / public_api.d.ts равен

export * from './src/core';

In @ angular / core /src/core.d.ts это

export { createPlatform, assertPlatform, destroyPlatform, getPlatform, PlatformRef, ApplicationRef, enableProdMode, isDevMode, createPlatformFactory, NgProbeToken } from './application_ref';

Итак, вот откуда пришло определение PlatformRef. В @ angular / core / src / application_ref.d.ts эти операторы:

export declare abstract class PlatformRef { 
   abstract bootstrapModuleFactory<M>(
...
}

export declare class PlatformRef_ extends PlatformRef { ... }

PlatformRef определен как абстрактный базовый класс. Теперь PlatformRef_ (с добавленным подчеркиванием) определен как «конкретный» класс, но на это имя класса нет ссылки. НО, https://www.typescriptlang.org/docs/handbook/classes.html#abstract -классы говорят:

Абстрактные классы - это базовые классы, из которых могут быть получены другие классы. Они не могут быть созданы напрямую.

Итак, ЧТО на самом деле возвращает вызов platformBrowserDynami c ()? AB C? Или вызов AB C, который не может произойти, согласно документам? И, если AB C, то история еще хуже, поскольку ссылка на platformBrowserDynami c фактически немедленно вызывает абстрактный метод этого AB C, который, конечно, не определен. И, конечно, это рабочее приложение, но я не знаю, как оно могло бы работать.

Ответы [ 3 ]

3 голосов
/ 28 апреля 2020

Если вы посмотрите на исходный код angular 4.4 для платформы-браузера-динамика c, вы увидите следующее

export const platformBrowserDynamic = createPlatformFactory(
    platformCoreDynamic, 'browserDynamic', INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS);

Итак platformBrowserDynamic это вызов функции createPlatformFactory, то есть это будет экземпляр фабричного класса.

Фабричный класс используется для создания экземпляра объекта без обязательного знания того, какой фактический класс будет использоваться, или когда создание объекта - трудоемкий процесс (например, создание компонента в angular).

В этом примере при вызове функции / фабрики platformBrowserDynamic создается экземпляр дочернего класса, расширяющего PlatformRef, а не экземпляр самого класса PlatformRef (поскольку он является абстрактным, как вы указали)

Однако, как сказал в комментариях bryan60, вам не нужно беспокоиться обо всем этом

Редактировать

Я копнул немного глубже, чтобы ответить на ваш вопрос относительно того, где создается фактический экземпляр.

Дочерний класс, расширяющий класс PlatformRef, называется PlatformRef_ и определен здесь .

Вы можете видеть, что он реализует все необходимые методы из абстрактного родительского класса.

Обратите внимание, что вы не найдете ни одного экземпляра new PlatformRef_(). Класс PlatformRef_ помечен как @Injectable и в конечном итоге будет создан с помощью инжектора angular. Для этого его необходимо определить в поставщиках, что делается здесь для базовой платформы. Другие платформы включают браузер, сервер и веб-работника.

Метод, который в конечном итоге создаст новую платформу на основе поставщиков, - это метод, возвращаемый createPlatformFactory

As to " почему фабричная функция, запрограммированная на возврат абстрактного класса, может возвращать дочерний класс, расширяющий этот класс », это является частью принципов объектно-ориентированного программирования (абстракция, наследование и полиморфизм). PlatformRef - это контракт, вам не нужно заботиться о реализации. Это просто означает, что фабрика вернет что-то, что реализует PlatformRef. Может быть, вам будет понятнее, если он возвращает интерфейс, тогда абстрактные классы и классы реализации останутся скрытыми?

1 голос
/ 30 апреля 2020

хотел бы прокомментировать это, но оно становится длинным ....

у вас есть пара фундаментальных недоразумений машинописи и того, как она работает в отношении javascript и js распространения пакетов в целом .

first .. .d.ts Файлы расширения являются только файлами объявлений типов для удобства использования машинописного текста. у них нет реального выполняемого кода, они просто сообщают транспортеру, какие функции и переменные типа и т. д. c ... они (необязательно) генерируются транспилером, когда вы связываете свой код для распространения, или иногда поддерживаются отдельно для библиотеки, которые изначально не являются TS для совместимости с TS. Вы можете поместить файл my-function.d.ts в ваше приложение root прямо сейчас со следующим:

declare function myFunction(a: string): string;

, и машинопись с радостью примет ваше слово, что myFunction существует, и позволит вам запустить его в любом месте вашего спроектировать и сообщить вам типизацию для него, даже если вы на самом деле не реализовали его, и это приведет к ошибкам во время выполнения. Потому что иногда библиотеки js загружаются таким образом, о котором машинопись не может знать, и вот как вы говорите, что они есть. Это также то, как вы говорите это для набранных ранее пакетов, которые вы получили из npm, которые теперь находятся в JS. Подробнее о файлах объявлений: https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html

ядро ​​package.json in angular указывает на фактический файл js, который содержит реальный переданный исходный код, который веб-пакет будет захватывать и используйте в своем комплекте, но он едва читаем, так как генерирует код из машинописного текста и веб-пакета. материал, который вы получаете из npm, не является исходным кодом, на который вы должны обратить внимание, чтобы выяснить внутреннюю работу angular, так как npm, как правило, поставляет связанные пакеты для эффективности вместе с файлами объявлений типов для удобства пользователей машинописного текста. , чтобы найти нетранслируемый / связанный источник машинописного текста, вы должны посетить angular github (https://github.com/angular/angular). все это с открытым исходным кодом, и вы можете клонировать репо и исследовать сколько угодно, но вы пытаетесь сделать javascript эквивалент чтения двоичных файлов или пакетов из nuget прямо сейчас.

секунда ... когда он говорит, что типом возврата является PlatformRef, который является абстрактным классом, это не означает, что он возвращает экземпляр PlatformRef, это означает, что он возвращает экземпляр класса, который расширяет или реализует PlatformRef. Это может быть любое количество различных конкретных реализаций PlatformRef. Это полезно, поскольку позволяет разработчикам иметь разные ссылки на платформы для разных платформ, например, браузер или мобильное приложение, поэтому привязки данных и событий можно абстрагировать и заменить на самом низком уровне. Это делает код angular чрезвычайно переносимым. Это также означает, что большинству angular разработчиков не нужно беспокоиться о конкретной реализации PlatformRef, пока они кодируют свое приложение. Им просто нужно знать, как использовать инструменты, предоставляемые angular, для создания переносимого приложения, поскольку все это было хорошо отведено, чтобы позволить разработчикам приложений сосредоточиться на приложении, а не на особенностях браузеров и мобильных платформ.

Я повторю свою точку зрения из моих комментариев здесь, PlatformRef просто определяет контекст приложения, чтобы angular мог правильно связывать события и данные. Он должен знать, работает ли он в браузере или мобильном контексте, чтобы он мог зацепиться за нажатие кнопок, прокрутку или жесты или что угодно, используя правильные методы для контекста, обеспечивая при этом согласованный API для разработчика. Это просто абстракция, поэтому вы, как разработчик, всегда можете связать событие click с <button (click)="myFunction()">, не беспокоясь о контексте приложения. Лично я почти полностью разбирался в источнике angular почти за десять лет работы с ним, и эта информация была мне полезна, может быть, несколько раз в очень далеком сценарии ios. Angular Источник действительно большой и сложный, так как он делает много всего. Это не очень хорошая отправная точка для angular, есть бесчисленные руководства и руководства по разработке, а затем, если вам все еще интересно, перейдите к исходному тексту.

Вам нужно было бы узнать об этом больше, если, скажем, вы надеялись, что ваше приложение будет работать как интеллектуальный телевизор / холодильник / тостер / что угодно. Тогда вам очень нужно написать собственную конкретную реализацию PlatformRef и написать фабричную функцию для ее доставки.

1 голос
/ 29 апреля 2020

Я начну этот ответ, указав, как другие говорили в комментариях, что это вовсе не обязательно для понимания Angular основ и для его работы. Тем не менее, я понимаю страсть копаться в источниках библиотек и пытаюсь набрать asp их "волхвов c" , поэтому я постараюсь объяснить это как можно лучше.

Во-первых, абстрактный класс полезен точно для того, чтобы использовать его здесь. Абстрактный класс - это, по сути, контракт (подобно интерфейсу, но потенциально содержащий некоторые базовые реализации, используемые наследующими дочерними классами ).

С учетом этого контракта Angular обещает создать и вернуть вам экземпляр из PlatformRef. Как следует из названия, это может отличаться в зависимости от платформы, на которой вы находитесь, цитируя документы (выделено мной):

В терминологии Angular платформа - это контекст, в котором Angular приложение работает. Наиболее распространенной платформой для Angular приложений является веб-браузер, но также может быть операционной системой для мобильного устройства или веб-сервером

Angular не говоря о том, что он вернет вам экземпляр абстрактного класса . Он говорит, что он даст вам экземпляр класса, который уважает PlatformRef контракт . Вам не нужно знать, что это такое, и вы можете использовать методы PlatformRef в контракте, не беспокоясь, потому что это Angular ответственность за проверку платформы и предоставление вам правильной реализации PlatformRef, с учетом платформы, на которой вы находитесь .

С другой стороны, если вы думаете о точке зрения разработчиков Angular, они должны были дать вам несколько методов, которые могут работать в каждый платформа . Когда вы устанавливаете Angular, , вы являетесь их клиентами, а абстрактный класс является их контрактом с вами. Или просто Angular самому разработчику нужен этот контракт для реализации других Angular функций , не беспокоясь о платформе.

В источнике вы видели:

export const platformBrowserDynamic = createPlatformFactory(
    platformCoreDynamic, 'browserDynamic', INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS)

Это означает, что есть некоторые внутренне зарегистрированные провайдеры , а createPlatformFactory (функция, возвращающая фабричную функцию) отвечает за PlatformRef создание экземпляра контракта. Чтобы дать вам чрезвычайно упрощенный пример того, как это может работать, давайте подумаем об этом:

function createPlatform(): PlatformRef {
  if (typeof window !== 'undefined') { // I'm clearly joking here..
    return new BrowserPlatformRef();
  } 
  // ..
}

class BrowserPlatformRef extends PlatformRef { /* ... */ }

Как вы сказали, PlatformRef само по себе не инстанцируемо, означая, что createPlatform is принудительно возвращает дочерний класс, производный от PlatformRef, поскольку new PlatformRef() не допускается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...