Недавно, когда я попытался настроить среду разработки и тестирования в ядре ASP.Net с Angular 2 и сеткой пользовательского интерфейса Kendo с использованием Webpack 2 и Karma, я обнаружил, что это довольно сложно и не так просто. Мне не удалось найти все ресурсы в одном месте. Так что мне потребовалось некоторое время, чтобы настроить мой проект Angular 2 в ASP.NET Core. Я решил записать это где-нибудь, что может быть полезно некоторым из моих друзей.

  • Приложения ASP.NET Core выполняются в процессе, отдельном от рабочего процесса IIS. Ядром среды выполнения ASP.NET Core является новый управляемый веб-сервер под названием Kestrel, который построен на основе libuv (та же библиотека используется в Node.js). Модуль ASP.Net Core - это собственный модуль IIS, который подключается к конвейеру IIS и перенаправляет трафик во внутреннее приложение ASP.NET Core. ANCM принимает управление только тогда, когда для запроса выбран обработчик, а отображение обработчика определяется в файле web.config приложения.

1. Создайте новый проект .NET Core с помощью шаблона webAPI в Visual Studio 2017. Я выбрал Visual Studio 2017, потому что я могу отлаживать свой файл машинописного текста, который мне не удалось выполнить в Visual Studio 2015. На этом этапе в нашем решении будет только папка Controllers, файлы appsettings.json, startup.cs и program.cs.

2. Добавьте файл web.config в проект и раскомментируйте узел system.webserver, потому что мы будем использовать базовый модуль ASP.Net для нашего приложения.

3. На вашем компьютере должен быть установлен npm. В противном случае установите его сейчас, а затем добавьте файл package.json со следующим содержимым. При сохранении файла он загрузит все зависимости, которые мы собираемся использовать в приложении. Мы будем выполнять сценарии в узле Scripts в package.json, используя npm.

Package.json:

{

«Версия»: «1.0.0»,

«Имя»: «asp.net»,

«Основной»: «wwwroot / index.html»,

«Автор»: «»,

«Лицензия»: «ISC»,

«Зависимости»: {

«@ Angular / common»: «2.4.10»,

«@ Angular / compiler»: «^ 2.4.10»,

«@ Angular / compiler-cli»: «^ 2.4.10»,

«@ Angular / core»: «^ 2.4.10»,

«@ Angular / http»: «^ 2.4.10»,

«@ Angular / platform-browser»: «2.4.10»,

«@ Angular / platform-browser-dynamic»: «2.4.10»,

«@ Angular / upgrade»: «2.4.10»,

«@ Angular / router»: «^ 3.4.5»,

«@ Angular / forms»: «2.4.10»,

«Angular-in-memory-web-api»: «0.3.1»,

«Core-js»: «^ 2.4.1»,

«Отразить-метаданные»: «0.1.3»,

«Rxjs»: «5.0.1»,

«Systemjs»: «^ 0.19.37»,

«Typings»: «^ 1.3.2»,

«Zone.js»: «^ 0.7.2»,

«@ Angular / platform-server»: «2.4.10»,

«Bootstrap»: «^ 3.3.7»,

«Ie-shim»: «~ 0.1.0»,

«@ Progress / kendo-angular-intl»: «^ 0.11.0»,

«@ Progress / kendo-angular-l10n»: «^ 0.2.0»,

«@ Progress / kendo-angular-grid»: «0.22.3»,

«@ Progress / kendo-data-query»: «^ 0.5.0»,

«@ Progress / kendo-angular-dropdowns»: «^ 0.37.0»,

«@ Progress / kendo-angular-inputs»: «^ 0.21.0»,

«@ Telerik / kendo-theme-default»: «*»

},

«DevDependencies»: {

«@ Types / jasmine»: «^ 2.5.47»,

«@ Types / node»: «7.0.8»,

«Angular-router-loader»: «^ 0.5.0»,

«Angular2-template-loader»: «0.6.2»,

«Awesome-typescript-loader»: «3.1.2»,

«Clean-webpack-plugin»: «^ 0.1.15»,

«Одновременно»: «^ 3.4.0»,

«Copy-webpack-plugin»: «^ 4.0.1»,

«Css-loader»: «^ 0.27.1»,

«Файл-загрузчик»: «^ 0.10.1»,

«Html-webpack-plugin»: «^ 2.28.0»,

«Extract-text-webpack-plugin»: «^ 2.1.0»,

«Жасминовое ядро»: «^ 2.5.2»,

«Jquery»: «^ 3.1.1»,

«Json-loader»: «^ 0.5.4»,

«Карма»: «^ 1.5.0»,

«Карма-хром-пусковая установка»: «^ 2.0.0»,

«Карма-кли»: «^ 1.0.1»,

«Karma-htmlfile-reporter»: «^ 0.3.4»,

«Карма-жасмин»: «^ 1.1.0»,

«Карма-жасмин-html-репортер»: «^ 0.2.2»,

«Karma-sourcemap-loader»: «^ 0.3.7»,

«Karma-webpack»: «^ 2.0.3»,

«Node-sass»: «^ 4.5.0»,

«Raw-loader»: «^ 0.5.1»,

«Римраф»: «^ 2.6.1»,

«Sass-loader»: «^ 6.0.3»,

«Source-map-loader»: «^ 0.2.0»,

«Style-loader»: «^ 0.13.2»,

«Ts-helpers»: «^ 1.1.2»,

«Tslint»: «^ 4.5.1»,

«Tslint-loader»: «^ 3.4.3»,

«Машинописный текст»: «2.2.1»,

«Url-loader»: «^ 0.5.8»,

«Vs-fix-sourcemaps»: «1.0.1»,

«Webpack»: «^ 2.3.2»,

«Webpack-dev-server»: «2.4.1»

},

«-Vs-binding»: {

«ProjectOpened»: [

«Смотреть-webpack-dev»

]

}

}

УСТАНОВКА KENDO UI ДЛЯ ANGULAR 2: мы будем использовать сетку Kendo UI для отображения списка элементов в нашем приложении. Итак, вам необходимо установить Kendo UI. Но перед установкой вам необходимо зарегистрироваться на сайте Kendo UI для получения пробной или коммерческой лицензии и следовать инструкциям здесь.

На этом этапе ваше приложение должно выглядеть так.

НАПИСАНИЕ И РАЗРАБОТКА КОМПОНЕНТОВ, МОДУЛЕЙ И УСЛУГ ANGULAR 2:

Наше приложение Angular 2: Приложение, которое мы создадим, будет очень простым приложением, которое будет просто отображать сетку Kendo-UI, заполненную значениями из нашей службы angular 2. Служба Angular 2 будет вызывать наш контроллер веб-API для значений. Вот как это будет выглядеть.

На стороне сервера. Давайте сначала спроектируем компоненты на стороне сервера -

1. Модель: создайте папку ViewModels и создайте файл класса ItemViewModel.cs со следующим кодом:

используя Систему;

using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;

с помощью Newtonsoft.Json;

using System.ComponentModel;

пространство имен testwebapp.ViewModels

{

открытый класс ItemViewModel

{

#region Constructor

общедоступный ItemViewModel ()

{

}

#endregion Конструктор

#region Properties

public int Id {получить; набор; }

общедоступная строка Заголовок {получить; набор; }

общедоступная строка Описание {получить; набор; }

общедоступная строка Text {получить; набор; }

общедоступная строка Notes {get; набор; }

[DefaultValue (0)]

общедоступный тип int {получить; набор; }

[DefaultValue (0)]

общедоступные int Flags {получить; набор; }

общедоступная строка UserId {получить; набор; }

[JsonIgnore]

общедоступный int ViewCount {получить; набор; }

общедоступные DateTime CreatedDate {получить; набор; }

общедоступные DateTime LastModifiedDate {получить; набор; }

#endregion Properties

}

}

2. Создание контроллера веб-API: добавьте файл класса контроллера (ItemsController.cs) со следующим кодом.

используя Систему;

using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;

с использованием Microsoft.AspNetCore.Mvc;

с помощью Newtonsoft.Json;

using testwebapp.ViewModels;

// Для получения дополнительной информации о включении веб-API для пустых проектов посетите https://go.microsoft.com/fwlink/?LinkID=397860

пространство имен testwebapp.Controllers

{

[Маршрут («api / [контроллер]»)]

открытый класс ItemsController: Controller {

#region Частные участники

///

/// Сгенерируйте образец массива исходных элементов для имитации базы данных (только для целей тестирования).

///

/// ‹param name =” num ”› Количество генерируемых элементов: по умолчанию 999 ‹/param›

/// ‹returns› определенное количество фиктивных элементов (только для целей тестирования) ‹/returns›

частный список ‹ItemViewModel› GetSampleItems (int num = 999)

{

Список ‹ItemViewModel› lst = new Список ‹ItemViewModel› ();

DateTime date = новое DateTime (2015, 12, 31) .AddDays (-num);

для (int id = 1; id ‹= num; id ++)

{

lst.Add (новый ItemViewModel ()

{

Id = id,

Title = String.Format («Заголовок элемента {0}», id.ToString ()),

Description = String.Format («Это пример описания элемента {0}.», Id.ToString ()),

CreatedDate = date.AddDays (id),

LastModifiedDate = date.AddDays (id),

ViewCount = num - id

});

}

return lst;

}

}

}

3. Клиентская сторона: Теперь мы будем разрабатывать наши клиентские компоненты.

i) index.html, где будет размещено наше приложение:

‹! Doctype html›

‹Html›

‹Base href =” / ”›

‹Мета-кодировка =” utf-8 / ›

‹Meta name =” viewport ”content =” width = device-width, initial-scale = 1.0 / ›

‹Title› Угловой шаблон веб-пакета ‹/title›

‹Meta http-Equiv =« тип-контента »контент =» текст / html; charset = utf-8 / ›

‹Openitemlist› Загрузка… ‹/openitemlist›

‹/Html›

Угловые компоненты:

ii) КОРНЕВОЙ КОМПОНЕНТ: добавьте файл app.component.ts в папку Scripts \ app. Это будет корневой компонент, который Angular будет использовать для начальной загрузки нашего приложения.

импортировать {Компонент} из «@ angular / core»;

@Компонент({

селектор: «openitemlist»,

шаблон: `‹h1› {{title}} ‹/h1›

‹Itemlist› ‹/itemlist› `

})

export class AppComponent {

title = «itemList»;

}

iii) Добавьте файл item-list.component.ts. Этот компонент будет использоваться для отображения списка элементов. Нам это нужно для подключения к корневому компоненту, который мы создали выше. Он вызовет метод getLatest () при инициализации. В методе getLatest () мы вызываем метод getLatest () службы, который будет извлекать все записи из нашего контроллера веб-API. Методы groupChange () и sortChange () вызываются сеткой Kendo Grid.

импортировать {Component, OnInit} из ‘@ angular / core’;

импортировать {Item} из "./item";

импортировать {ItemService} из "./item.service";

импортировать {GridModule} из ‘@ progress / kendo-angular-grid’;

импортировать {GroupDescriptor, process} из «@ progress / kendo-data-query»;

импортировать {SortDescriptor, orderBy} из ‘@ progress / kendo-data-query’;

импортировать {GridDataResult} из ‘@ progress / kendo-angular-grid’;

импортировать «@ telerik / kendo-theme-default / dist / all.css»;

@Компонент({

селектор: ‘itemlist’,

templateUrl: ‘someItems.component.html’,

стили: [

`

ul.items li {

курсор: указатель;

}

ul.items li.selected {

цвет фона: #cccccc;

}

`]

})

класс экспорта ItemListComponent реализует OnInit {

selectedItem: Item;

items: Item [];

errorMessage: строка;

группы: GroupDescriptor [];

sort: SortDescriptor [] = [];

gridView: GridDataResult;

конструктор (закрытый itemService: ItemService) {}

ngOnInit () {

this.getLatest ();

}

getLatest () {

this.itemService.getLatest (5)

.subscribe (latestItems = ›{

console.log (latestItems);

this.items = latestItems;

this.gridView = {

данные: orderBy (this.items, this.sort),

всего: this.items.length

};

}, error = ›{

console.log («ошибка»);

this.errorMessage = ‹any› ошибка;

});

}

public groupChange (groups: GroupDescriptor []): void {

this.groups = группы;

this.getLatest ();

}

public sortChange (sort: SortDescriptor []): void {

this.sort = sort;

this.getLatest ();

}

iv) Добавьте файл someitems.component.html. Это файл шаблона, который будет использоваться ItemListComponent. Здесь мы просто настроили сетку кендо с 3 столбцами.

‹! DOCTYPE html›

‹Html›

‹Мета-кодировка =” utf-8 / ›

‹Title› ‹/title›

‹Kendo-grid [groupable] =» true »[group] =« groups »[selectable] =« true »(groupChange) =» groupChange ($ event) »[sort] =« sort »

(sortChange) = ”sortChange ($ event)” [sortable] = ”{mode:‘ multiple ’}” [data] = ”gridView” ›

‹Kendo-grid-column title =" Id "field =" Id "› ‹/kendo-grid-column›

‹Kendo-grid-column title = поле” Title ”=” Title ”› ‹/kendo-grid-column›

‹Kendo-grid-column title =” Description ”field =” Description ”› ‹/kendo-grid-column›

‹/Kendo-grid›

‹/Html›

4. Создайте службу: Добавьте службу Angular item.service.ts. Здесь у нас есть только один метод getLatest (), который будет вызывать метод веб-API.

импортировать {Injectable} из «@ angular / core»;

импортировать {Http, Response} из «@ angular / http»;

импортировать {Observable} из «rxjs / Observable»;

импортировать {Item} из «./item»;

импортировать «rxjs / add / operator / catch»;

импортировать «rxjs / add / operator / map»;

@Injectable ()

export class ItemService {

частный baseUrl = ‘api / items /’; // URL веб-API

конструктор (частный http: Http) {}

getLatest (число ?: число) {

let url = this.baseUrl + ‘GetLatest /’;

если (число! = ноль) {url + = число; }

вернуть this.http.get (url)

.map (response = ›response.json ())

.catch (this.handleError);

}

private handleError (error: Response) {

console.error (ошибка);

return Observable.throw (error.json (). error || «Ошибка сервера»);

}

}

5. Создайте файл item.ts, чтобы сопоставить ответ службы с объектом элемента. Необязательно иметь все свойства. Для простоты здесь всего 3 свойства.

export class Item {

конструктор(

публичный Id: номер,

публичный Заголовок: строка,

общедоступное Описание: строка

) { }

}

AppModule:

КОРНЕВОЙ МОДУЛЬ: теперь создайте файл app.module.ts. Это @NgModule приложения, в котором мы будем регистрировать наши компоненты и импортировать наши модули.

/// ‹путь ссылки =” ../../ typings / index.d.ts ”/›

импортировать {NgModule} из «@ angular / core»;

импортировать {BrowserModule} из «@ angular / platform-browser»;

импортировать {HttpModule} из «@ angular / http»;

импортировать «rxjs / Rx»;

импортировать {AppComponent} из «./app.component»;

импортировать {ItemListComponent} из «./item-list.component»;

импортировать {ItemService} из «./item.service»;

импортировать {GridModule} из ‘@ progress / kendo-angular-grid’;

@NgModule ({

// директивы, компоненты и каналы

объявления: [

AppComponent,

ItemListComponent

],

импорт: [

BrowserModule,

HttpModule,

GridModule

],

поставщики: [

ItemService

],

бутстрап: [

AppComponent

]

})

экспортный класс AppModule {}

Загрузите модуль:

Мы будем использовать загрузку браузера для начальной загрузки нашего модуля приложения. В AppModule зарегистрирован AppComponent. Webpack поддерживает «горячую замену модуля», которая работает, отслеживая изменения в файлах, а затем информируя браузер о необходимости замены определенных модулей или функций, но не перезагружая всю страницу. Поэтому нам нужно было добавить код горячей замены в наш файл main.ts. Но перед тем, как эту функцию можно будет использовать, ее необходимо включить. Есть много вариантов, и один из них - включить его в package.json, добавив сценарий типа «start»: «concurrently \» webpack-dev-server - hot –inline

Main.ts

/// ‹путь ссылки =” ../ typings / index.d.ts ”/›

импортировать {platformBrowserDynamic} из «@ angular / platform-browser-dynamic»;

импортировать {AppModule} из «./App/app.module»;

// Включает горячую замену модуля.

объявить модуль var: любой;

if (module.hot) {

module.hot.accept ();

}

platformBrowserDynamic (). bootstrapModule (AppModule);

Добавьте полифиллы и сторонние зависимости:

Мы хотим разделить наше приложение на 3 выходных пакета: приложение, поставщик и полифиллы. Полифиллы будут действовать как прокладка для API браузера. Этот файл необходимо добавить, чтобы приложение angular могло обрабатывать нужные ему поллифиллы. Пакет приложений будет содержать только код приложения, а пакет поставщика будет содержать только код поставщика. Если мы не разделяем их, то будет только один выходной пакет app.js, который может быть очень большим и на его загрузку в браузере уйдет много времени. Но поскольку наш код разделен на 3 пакета, они будут загружаться в браузер параллельно. Поэтому, когда импорт появляется как в vendor.ts, так и в коде приложения, сторонняя библиотека будет удалена из кода приложения и помещена вместо этого в комплект поставщика.

Polyfills.ts

импорт «ie-shim»; // Поддержка Internet Explorer 9.

импортировать «core-js / es6 / symbol»;

импортировать core-js / es6 / object;

импортировать «core-js / es6 / function»;

импортировать core-js / es6 / parse-int;

импортировать core-js / es6 / parse-float;

импортировать core-js / es6 / number;

импортировать core-js / es6 / math;

импортировать core-js / es6 / string;

импортировать core-js / es6 / date;

импортировать core-js / es6 / array;

импортировать core-js / es6 / regexp;

импортировать core-js / es6 / map;

импортировать core-js / es6 / set;

импортировать core-js / es6 / weak-map;

импортировать core-js / es6 / weak-set;

импортировать core-js / es6 / typed;

импортировать «core-js / es6 / отражать»;

импортировать «core-js / es7 / отражать»;

импортировать «zone.js / dist / zone»;

импортные «ц-хелперы»;

Vendor.ts

импортировать «jquery / src / jquery»;

импортировать «bootstrap / dist / js / bootstrap»;

импортировать «bootstrap / dist / css / bootstrap.css»;

импортировать «bootstrap / dist / css / bootstrap-theme.css»;

НАСТРОЙКА КОМПИЛЯТОРА TYPESCRIPT ДЛЯ JIT (точно вовремя) И AoT (опережая время):

В приложении Angular 2 компилятор машинописного текста сначала компилирует файлы машинописного текста в javascript. Затем, когда файлы Javascript загружаются в браузер, они компилируются в соответствующий двоичный файл, который затем интерпретируется и выполняется браузером. В JIT обе компиляции происходят в браузере. Но в AoT компиляция выполняется заранее, и модуль @ angular / compiler удаляется из пакета. Это увеличивает производительность, а также снижает полезную нагрузку, поскольку компилятор angular 2 почти вдвое меньше пакета angular 2.

Параметры компиляции для машинописного текста настраиваются в tsConfig.json для JIT и tsConfig-aot.json для AOT. Компилятор Typescript скомпилирует файлы машинописного текста на основе этого файла. Цель указывает версию ECMAScript, которую будет выводить компилятор. Мы берем es5, потому что es6 еще не поддерживается многими браузерами. Здесь мы хотим, чтобы наш компилятор машинописного текста транслировал файлы .ts в модули es6. Es6 такой же, как es2015. Webpack 2 имеет встроенную поддержку модулей ES2015 (псевдонимы модулей гармонии), а также обнаружение экспорта неиспользуемых модулей, которое также называется встряхиванием дерева. OutDir будет местом, куда будут отправлены перенесенные файлы. Итак, для нашей разработки мы будем использовать tsConfig.json, а для производственной сборки мы будем использовать tsConfig-aot.json.

tsConfig.json

{

«CompileOnSave»: ложь,

«CompilerOptions»: {

«Цель»: «es5»,

«Lib»: [«es2015», «dom»],

«Типы»: [«узел», «жасмин»],

«Модуль»: «es2015»,

«ModuleResolution»: «узел»,

«SourceMap»: истина,

«EmitDecoratorMetadata»: истина,

«ЭкспериментальныеДекораторы»: правда,

«RemoveComments»: правда,

«NoImplicitAny»: правда,

«SkipLibCheck»: правда,

«NoEmitOnError»: ложь,

«OutDir»: «опубликованный_iis»

},

«Файлы»: [

«Скрипты / main.ts»

],

«AwesomeTypescriptLoaderOptions»: {

«UseWebpackText»: true

}

}

tsConfig-aot.json

{

«CompilerOptions»: {

«Цель»: «es5»,

«Модуль»: «es2015»,

«ModuleResolution»: «узел»,

«SourceMap»: истина,

«EmitDecoratorMetadata»: истина,

«ЭкспериментальныеДекораторы»: правда,

«RemoveComments»: правда,

«NoImplicitAny»: правда,

«SuppressImplicitAnyIndexErrors»: истина,

«SkipLibCheck»: правда,

«Lib»: [

“es2015”,

«Дом»

]

},

«Файлы»: [

«Скрипты / app / app.module.ts»,

«Скрипты / main-aot.ts»

],

"исключать": [

«Node_modules»,

«Wwwroot»

],

«AngularCompilerOptions»: {

«GenDir»: «Скрипты / .aot»,

«SkipMetadataEmit»: true

},

«CompileOnSave»: ложь,

«BuildOnSave»: ложь

}

tslint.json:. Этот файл будет использоваться загрузчиком tslint для проверки качества кода машинописного текста, а загрузчик будет настроен в webpack2.

{

"правила": {

«Имя-класса»: правда,

«Формат комментария»: [

истинный

],

«Кудрявый»: правда,

«Eofline»: ложь,

«Forin»: ложь,

«Отступ»: [

ложный,

«Пробелы»

],

«Позиция-метки»: истина,

«Максимальная длина строки»: [

истинный,

200

],

«Доступ к члену»: ложь,

«Упорядочивание участников»: [

истинный,

«Статический-до-экземпляра»,

«Переменные перед функциями»

],

«No-arg»: правда,

«Без побитового»: истина,

«Без консоли»: [

истинный,

"отлаживать",

"Информация",

"время",

«TimeEnd»,

"след"

],

«No-construct»: правда,

«Без отладчика»: правда,

«No-duplicate-variable»: истина,

«No-empty»: ложь,

«No-eval»: правда,

«No-inferrable-types»: [true],

«Без тени-переменной»: ложь,

«Без строкового-литерала»: ложь,

«No-switch-case-fall-through»: ложь,

«Без завершающих пробелов»: истина,

«No-unused-expression»: правда,

«No-use-before-declare»: правда,

«No-var-keyword»: правда,

«Объект-литерал-ключи сортировки»: ложь,

"одна линия": [

истинный,

«Проверить-открыть-скобку»,

«Чек-улов»,

«Чек-еще»,

«Проверить-пробел»

],

«Quotemark»: [

истинный,

"не замужем"

],

«Основание»: ложь,

"точка с запятой": [

"всегда"

],

«Тройное равенство»: [

ложный,

«Разрешить нулевую проверку»

],

«Typedef-whitespace»: [

истинный,

{

«Index-signature»: «nospace»,

«Параметр»: «космический»,

«Property-Declaration»: «nospace»,

«Объявление-переменной»: «без космоса»

}

],

«Имя-переменной»: ложь,

«Пробел»: [

истинный,

«Чек-филиал»,

«Чек-декла»,

«Чек-оператор»,

«Чек-разделитель»,

«Чек-тип»

]

}

}

НАСТРОЙКА WEBPACK ДЛЯ РАЗРАБОТКИ, ТЕСТИРОВАНИЯ И ПРОИЗВОДСТВА

Теперь мы настроим веб-пакет для всех трех сред. В файле webpack.config.js мы проверим, предназначена ли создаваемая нами сборка для разработки, производства или тестирования. Если он предназначен для разработки, то выходные файлы должны создаваться в папке wwwroot решения. Но если это для производства, вам необходимо

а. Добавьте к имени сгенерированного файла хеш-значение, которое генерируется плагином webpack. Это будет действовать как средство блокировки кеша, которое заставит браузер перезагрузить страницу.

б. Нам также необходимо настроить веб-пакет для AoT-компиляции компонентов и модулей Angular. При использовании AoT браузер загружает предварительно скомпилированную версию приложения. Браузер загружает исполняемый код, чтобы он мог немедленно отобразить приложение, не дожидаясь предварительной компиляции приложения. Модуль начальной загрузки также отличается для AoT. Мы будем использовать main-aot.ts вместо main.ts для начальной загрузки нашего модуля приложения. Компиляторы JIT и AoT создают класс AppModuleNgFactory из одного и того же исходного кода AppModule. Компилятор JIT создает фабричный класс на лету, в памяти, в браузере, тогда как компилятор AoT выводит фабрику в физический файл, который нам нужно импортировать в файл main-Aot.js.

Как вы можете видеть ниже, webpack использует разные плагины для разных целей, например

· UglifyJSPlugin - который минимизирует весь вывод Javascript фрагментов;

· CommonsChunkPlugin - создает отдельные блоки, состоящие из общих модулей, совместно используемых несколькими точками входа. Отделяя общие модули от пакетов, полученный фрагментированный файл может быть загружен один раз изначально и сохранен в кеше для последующего использования.

· ExtracttextPlugin - этот плагин генерирует отдельный пакет css, а также объединяет несколько файлов css в один. Этот плагин поставляется с загрузчиком, который обрабатывает процесс извлечения. Затем плагин получает результат, агрегированный загрузчиком, и выдает отдельный файл;

· LoaderOptionsPlugin - это предназначено для преодоления разрыва между веб-пакетами 1 и 2. Однако до тех пор, пока загрузчик не будет обновлен, чтобы зависеть от параметров, передаваемых им напрямую, LoaderOptionsPlugin существует для преодоления разрыва. Вы можете настроить глобальные / общие параметры загрузчика с помощью этого плагина, и все загрузчики получат эти параметры.

Как вы могли заметить, tsConfig-aot.json использует main-aot.json для начальной загрузки appModule для компиляции AoT. main-aot.json показан ниже.

Main-aot.js

/// ‹путь ссылки =” ../ typings / index.d.ts ”/›

импортировать {platformBrowser} из «@ angular / platform-browser»;

импортировать {AppModule} из «./App/app.module»;

импортировать {enableProdMode} из ‘@ angular / core’;

импортировать {AppModuleNgFactory} из «./App/app.module.ngfactory»;

platformBrowser (). bootstrapModuleFactory (AppModuleNgFactory);

Файлы конфигурации webpack находятся ниже. Мы будем запускать разные файлы конфигурации в зависимости от среды сборки, будь то разработка, производство или тестирование. Мы используем AoT только для производственной среды. Также для разработки наши скомпилированные файлы должны быть сгенерированы под wwwroot. Но для производства он должен быть сгенерирован в определяемой пользователем папке, которую позже необходимо развернуть в IIS.

А. Webpack.config.js

var environment = (process.env.NODE_ENV || «разработка»). trim ();

console.log («среда» + process.env.NODE_ENV)

если (среда == «тест»)

{

module.exports = require (‘./ webpack.test.js’);

}

else if (environment === «разработка») {

module.exports = require (‘./ webpack.dev.js’);

} еще {

module.exports = require (‘./ webpack.prod.js’);

}

Б. Webpack.dev.js

var path = require («путь»);

var webpack = require («webpack»);

var HtmlWebpackPlugin = require (‘html-webpack-plugin’);

var CopyWebpackPlugin = require ('copy-webpack-plugin');

var CleanWebpackPlugin = require ('clean-webpack-plugin');

var helpers = require (‘./ webpack.helpers’);

console.log (‘@@@@@@@@@ ИСПОЛЬЗОВАНИЕ РАЗРАБОТКИ @@@@@@@@@@@@@@@’);

module.exports = {

devtool: «исходная карта»,

представление: {

подсказки: ложь

},

Вход: {

‘Polyfills’: ‘./Scripts/polyfills.ts’,

‘Vendor’: ‘./Scripts/vendor.ts’,

‘App’: ‘./Scripts/main.ts’

},

вывод: {

путь: __dirname + ‘/ wwwroot /’,

имя файла: ‘dist / [name] .bundle.js’,

chunkFilename: ‘dist / [id] .chunk.js’,

publicPath: ‘/’

},

разрешить: {

расширения: [‘.ts’, ‘.js’, ‘.json’, ‘.css’, ‘.scss’, ‘.html’]

},

devServer: {

historyApiFallback: правда,

contentBase: path.join (__ dirname, ‘/ wwwroot /’),

watchOptions: {

aggregateTimeout: 300,

опрос: 1000

}

},

модуль: {

правила: [

{

тест: /\.ts$/,

загрузчики: [

‘Awesome-typescript-loader’,

"Угловой-роутер-загрузчик",

‘Angular2-template-loader’,

‘Source-map-loader’,

«Цлинт-загрузчик»

]

},

{

test: /\.(png|jpg|gif|woff|woff2|ttf|svg|eot)$/,

загрузчик: ‘file-loader? name = assets / [name] - [hash: 6]. [ext]’

},

{

тест: /favicon.ico$/,

загрузчик: ‘file-loader? name = / [name]. [ext]’

},

{

тест: /\.css$/,

загрузчик: "style-loader! css-loader"

},

{

тест: /\.scss$/,

исключить: / node_modules /,

загрузчики: [‘style-loader’, ‘css-loader’, ‘sass-loader’]

},

{

тест: /\.html$/,

загрузчик: raw-loader

}

],

exprContextCritical: false

},

плагины: [

новый webpack.optimize.CommonsChunkPlugin ({name: [‘vendor’, ‘polyfills’]}),

новый CleanWebpackPlugin (

[

‘./Wwwroot/dist’,

‘./Wwwroot/assets’

]

),

новый HtmlWebpackPlugin ({

имя файла: ‘index.html’,

вводить: «тело»,

шаблон: ‘Scripts / index.html’

}),

новый CopyWebpackPlugin ([

{from: ‘./Scripts/images/*.*’, to: ‘assets /’, flatten: true}

])

]

};

В. Webpack.prod.js

/**

* конфигурационный файл webpack2 для компиляции ng2 AOT

* расположение: config / webpack.aot.js

*/

var webpack = require («webpack»);

var HtmlWebpackPlugin = require (‘html-webpack-plugin’);

var ExtractTextPlugin = require («extract-text-webpack-plugin»);

var path = require («путь»);

const ENV = process.env.NODE_ENV = process.env.ENV = «производство»;

module.exports = {

devtool: «исходная карта»,

Вход: {

‘Polyfills’: ‘./Scripts/polyfills.ts’,

‘App’: ‘./Scripts/main-aot.ts’

},

разрешить: {

расширения: [‘*’, ‘.ts’, ‘.js’]

},

вывод: {

путь: path.join (__ dirname, ‘.dist / web / aot /’),

имя файла: ‘js / [name] - [hash: 8] .bundle.js’,

chunkFilename: ‘js / [id] - [hash: 8] .chunk.js’

},

devServer: {

historyApiFallback: правда,

статистика: «минимальная»,

outputPath: path.join (__ dirname, ‘wwwroot /’)

},

модуль: {

правила: [

{

тест: /\.ts$/,

использовать: [

‘Awesome-typescript-loader’,

‘Angular-router-loader? Aot = true & genDir = Scripts / .aot /’

]

},

{

тест: /\.html$/,

использование: ‘raw-loader’

},

{

test: /\.(png|jpg|gif|ico|woff|woff2|ttf|svg|eot)$/,

загрузчик: ‘file-loader? name = assets / [name] - [hash: 6]. [ext]’,

},

{

тест: /\.css$/,

загрузчик: ExtractTextPlugin.extract («css-loader»)

}

],

exprContextCritical: false

},

плагины: [

новый webpack.optimize.CommonsChunkPlugin ({

name: [‘app’, ‘polyfills’]

}),

новый webpack.LoaderOptionsPlugin ({

свести к минимуму: правда,

опции: {

htmlLoader: {

минимизировать: ложь

}

}

}),

новый HtmlWebpackPlugin ({

имя файла: ‘index.html’,

вводить: «тело»,

шаблон: ‘./Scripts/index.html’

}),

новый webpack.optimize.UglifyJsPlugin ({

compress: {

предупреждения: ложные,

drop_console: правда

},

вывод: {

комментарии: ложь

}

}),

новый ExtractTextPlugin (‘[имя]. [hash] .css’),

новый webpack.DefinePlugin ({

‘Process.env’: {

‘ENV’: JSON.stringify (ENV)

}

})

]

};

НАСТРОЙКА ЗАПУСКА ЗАДАЧ NPM: Это может показаться удивительным, но это правда, что npm также может запускать задачи из package.json. Команда npm run помогает запускать любые инструкции в узле scripts файла package.json. Так что вам может вообще не понадобиться кряхтение или глоток. Средство запуска задач NPM, которое можно скачать здесь, можно использовать в качестве проводника запуска задач. Он покажет все сценарии, указанные в Package.json, как показано здесь.

Итак, давайте добавим узел сценариев в наш package.json. Это будет как

«Скрипты»: {

«Ngc»: «ngc -p ./tsconfig-aot.json»,

«Start»: «одновременно \» webpack-dev-server - hot - inline - open - port 8080 \ »\» dotnet run \ »«,

«Webpack-dev»: «установить NODE_ENV = development && webpack»,

«Webpack-production»: «установить NODE_ENV = production && webpack»,

«Build-dev»: «npm run webpack-dev»,

«Build-production»: «npm run ngc && npm run webpack-production»,

«Watch-webpack-dev»: «установить NODE_ENV = development && webpack - watch - color»,

«Watch-webpack-production»: «npm run build-production - watch - color»,

«Publish-for-iis»: «npm run build-production && dotnet publish -c Release»,

«Postinstall»: «typings install dt ~ core-js - global»,

«Test»: «одновременно‘ npm run tsc: w ’‘ karma start karma.conf.js ’»,

«Test-once»: «установить NODE_ENV = test && tsc && karma start karma.conf.js - single-run»,

«Tsc»: «tsc»,

«Tsc: w»: «tsc -w»

}

НАСТРОЙКА СРЕДЫ ТЕСТИРОВАНИЯ МОДУЛЯ

Karma - это средство запуска модульных тестов, способное запускать тестовый сервер и понимать, как найти тестовые файлы и передать их браузеру тестов. Жасмин - это тестовый фреймворк. Он интегрируется с Karma и понимает, как проводить тест. Наш тестовый браузер - Chrome. Массив files определяет, какие файлы включаются в браузер и какие файлы просматриваются и обслуживаются Karma . Файлы, загружаемые в браузер, настраиваются в отдельном файле конфигурации karma.entry.js. Тестирование компонентов основывается на тестовой программе утилиты тестирования Angular 2, которая настроена в karma.entry.js. TestBed.InitTestEnvironment создает среду тестирования, которую необходимо создать, уничтожить и при необходимости сбросить перед каждым модульным тестом. В Angular 2 помощник testBed используется для настройки и инициализации среды для модульного тестирования с помощью модуля angular.

НАСТРОЙКА KARMA:

А. Karma.conf.js

var path = require («путь»);

var webpackConfig = require (‘./ webpack.config’);

module.exports = function (config) {

config.set ({

basePath: ‘’,

рамки: [

«Жасмин»

],

файлы: [

‘Karma.entry.js’

],

плагины: [

требовать (‘karma-webpack’),

«Карма-жасмин»,

«Карма-хром-пусковая установка»,

«Карма-жасмин-HTML-репортер»,

"Карма-htmlfile-репортер",

"Карма-загрузчик карт исходных текстов"

],

// предварительная обработка совпадающих файлов перед их отправкой в ​​браузер

// доступные препроцессоры: https://npmjs.org/browse/keyword/karma-preprocessor

препроцессоры: {

‘Karma.entry.js’: [‘webpack’, ‘sourcemap’]

},

webpack: webpackConfig,

репортеры: [«прогресс»],

порт: 9876,

цвета: правда,

logLevel: config.LOG_INFO,

autoWatch: ложь,

браузеры: [‘Chrome’],

singleRun: правда,

concurrency: Infinity // сколько браузеров запускает Karma параллельно.

})

}

Karma.entry.js

требуется (‘es6-shim’);

требовать («отразить-метаданные»);

требуется (‘zone.js / dist / zone’);

требуется (‘zone.js / dist / long-stack-trace-zone’);

требуется (‘zone.js / dist / async-test’);

требуется (‘zone.js / dist / fake-async-test’);

требуется (‘zone.js / dist / sync-test’);

требуется (‘zone.js / dist / proxy’);

требуется (‘zone.js / dist / jasmine-patch’);

const browserTesting = require (‘@ angular / platform-browser-dynamic / testing’);

const coreTesting = require (‘@ angular / core / testing’);

const context = require.context (‘./ src /’, true, /\.spec\.ts$/);

Error.stackTraceLimit = Бесконечность;

жасмин.DEFAULT_TIMEOUT_INTERVAL = 2000;

coreTesting.TestBed.resetTestEnvironment ();

coreTesting.TestBed.initTestEnvironment (

browserTesting.BrowserDynamicTestingModule,

browserTesting.platformBrowserDynamicTesting ()

);

context.keys (). forEach (контекст);

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

Webpack.test.js

«Использовать строгое»;

const path = require («путь»);

const webpack = require («webpack»);

console.log (‘@@@@@@@@@ ИСПОЛЬЗОВАНИЕ ТЕСТА @@@@@@@@@@@@@@@@’);

module.exports = {

devtool: 'inline-source-map',

разрешить: {

расширения: [‘.ts’, ‘.js’, ‘.json’, ‘.css’, ‘.scss’, ‘.html’],

псевдоним: {src: path.resolve (__ dirname, ‘./Scripts’)}

},

модуль: {

правила: [

{

тест: /\.ts$/,

загрузчики: [

‘Awesome-typescript-loader’,

"Угловой-роутер-загрузчик",

‘Angular2-template-loader’,

‘Source-map-loader’,

«Цлинт-загрузчик»

]

},

{

test: /\.(png|jpg|gif|woff|woff2|ttf|svg|eot)$/,

загрузчик: ‘file-loader? name = assets / [name] - [hash: 6]. [ext]’

},

{

тест: /favicon.ico$/,

загрузчик: ‘file-loader? name = / [name]. [ext]’

},

{

тест: /\.css$/,

загрузчик: "style-loader! css-loader"

},

{

тест: /\.scss$/,

исключить: / node_modules /,

загрузчики: [‘style-loader’, ‘css-loader’, ‘sass-loader’]

},

{

тест: /\.html$/,

загрузчик: raw-loader

}

],

exprContextCritical: false

}

};

НАПИСАТЬ ИСПЫТАТЕЛЬНЫЙ КОМПЛЕКТ:

Мы будем использовать Jasmine для написания модульного теста. В папке Scripts создайте подпапку «test». Добавьте файл spec.ts с именем item.component.spec.ts file. Мы настроили модуль тестирования, передав ему компонент, который мы хотим протестировать, вместе с зависимостями компонента. Затем мы создали приспособление компонента, которое является дескриптором тестовой среды, окружающей созданный компонент. Элемент отладки - это класс angular 2, который содержит все виды ссылок и методы, необходимые для исследования компонента. Мы создали шпионскую функцию для метода getLatest класса itemService, который будет возвращать наблюдаемый список элементов.

Поскольку наш компонент ItemListComponent полагается на ItemService, который является асинхронной задачей, нам нужно использовать асинхронную функцию Angular 2. Async не позволит запускать следующий тест, пока async не завершит все свои задачи.

Итак, у нас есть один набор тестов «Async». Внутри сюиты есть блок «it ()». Это спецификация, в которой указано, что будет делать этот блок. Здесь мы проверяем, правильно ли заполнено количество элементов в компоненте itemList.

импортировать {async, ComponentFixture, TestBed} из «@ angular / core / testing»;

импортировать {CUSTOM_ELEMENTS_SCHEMA} из ‘@ angular / core’;

импортировать {ItemListComponent} из ‘../App/item-list.component’;

импортировать {ItemService} из ‘../App/item.service’;

импортировать {GridModule} из ‘@ progress / kendo-angular-grid’;

импортировать {HttpModule, Http} из «@ angular / http»;

импортировать {Observable} из «rxjs / Rx»;

импортировать {Provider} из ‘@ angular / core’;

импортировать «rxjs / add / observable / of»;

описать (‘Component: ItemList’, () = ›{

пусть приспособление: ComponentFixture ‹ItemListComponent›;

описать (‘Async’, () = ›{

beforeEach (async (() = ›{

TestBed.configureTestingModule ({

объявления: [

ItemListComponent

],

поставщики: [

ItemService

],

импорт: [

GridModule, HttpModule

],

схемы: [CUSTOM_ELEMENTS_SCHEMA]

});

fixture = TestBed.createComponent (ItemListComponent);

itemService = fixture.debugElement.injector.get (ItemService);

spyOn (itemService, ‘getLatest’)

.and.returnValue (Observable.of (

[{‘Id’: 1, ‘Title’: ‘test1’, ‘Description’: ‘test12’}]

))

fixture.detectChanges ();

}));

afterEach (() = ›{

fixture = undefined; // разборка

});

it ('должен получить предметы', () = ›{

fixture.debugElement.componentInstance.ngOnInit ();

ожидать (fixture.debugElement.componentInstance.items.length) .toEqual (1);

ожидать (fixture.debugElement.componentInstance.items [0] .Title) .toEqual («test1»);

});

});

});

Теперь, если вы запустите сценарий test-once из Task runner, и если все прошло нормально, вы увидите, что тесты прошли, как показано ниже:

Итак, наконец, наше решение выглядит так, как показано ниже

Использованная литература:

1. https://marketplace.visualstudio.com/items?itemName=MadsKristensen.NPMTaskRunner

2. https://offering.solutions/blog/articles/2017/02/26/moving-from-systemjs-to-webpack-angular-2/

3. https://www.typescriptlang.org/docs/handbook/tsconfig-json.html

4. https://webpack.js.org/guides/tree-shaking/

5. https://angular.io/docs/ts/latest/cookbook/aot-compiler.html

6. https://webpack.js.org/plugins/commons-chunk-plugin/

7. https://webpack.js.org/plugins/loader-options-plugin/

8. http://karma-runner.github.io/1.0/config/files.html

9. http://chariotsolutions.com/blog/post/testing-angular-2-components-unit-tests-testcomponentbuilder/