Книга: Angular и TypeScript. Сайтостроение для профессионалов
Назад: 9. Модульное тестирование Angular-приложений
Дальше: Приложение А. Общее представление о ECMAScript 6

10. Упаковка и развертывание приложений с помощью Webpack

В этой главе:

упаковка приложений для развертывания с использованием Webpack;

• конфигурирование Webpack для упаковки Angular-приложений в dev и prod;

• встраивание средства для запуска тестов Karma в автоматизированный процесс сборки;

• создание сборки prod для онлайн-аукциона;

• автоматизация создания и упаковки с использованием Anguar CLI.

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

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

развертываемое веб-приложение должно быть небольшим по размеру (чтобы его можно было быстрее загрузить);

• при запуске браузер должен обойтись минимумом запросов к серверу (для скорейшей загрузки).

Когда браузер выполняет запрос к серверу, он получает HTML-документы, которые могут включать дополнительные файлы: CSS-таблицы, изображения, видеоклипы и т.д. Возьмем для примера онлайн-аукцион. В процессе запуска он отправляет сотни запросов к серверу, просто чтобы загрузить Angular с его зависимостями и компилятор TypeScript; в целом это составляет по объему 5,5 Мбайт. Добавьте сюда созданный вами код, составляющий пару десятков файлов HTML, TypeScript и CSS, не говоря уже об изображениях! Для такого небольшого приложения получается очень много загружаемого кода и слишком много запросов к серверу. Посмотрите на рис. 10.1, где показано содержимое вкладки Network (Сеть) панели Developer Tools (Инструменты разработчика) браузера Chrome после загрузки аукциона в ваш браузер: там огромное количество сетевых запросов и громадный объем приложения.

ch10_01.tif 

Рис. 10.1. Отслеживание версии развертывания приложения онлайн-аукциона

Настоящие приложения содержат сотни и даже тысячи файлов, требующих минимизации, оптимизации и создания из них единого пакета. Кроме того, для коммерческого применения приложения можно предварительно откомпилировать код в JavaScript, избавившись от необходимости загрузки в браузер компилятора TypeScript объемом 3 Мбайт.

Для развертывания веб-приложений на JavaScript имеется несколько популярных инструментальных средств. Все они используют Node и доступны в виде npm-пакетов. Эти средства можно отнести к двум основным категориям:

средства запуска задач;

• загрузчики и упаковщики модулей.

К весьма распространенным универсальным средствам запуска задач можно отнести Grunt () и Gulp (). Они совершенно не разбираются в приложениях JavaScript, но позволяют настраивать и запускать задачи, необходимые для развертывания приложений. Приспособить Grunt и Gulp под процессы сборки не так-то просто, поскольку их конфигурационные файлы имеют длину в несколько сотен строк.

В данной книге для запуска задач использовались npm-сценарии, а под задачей понимался сценарий или двоичный файл, который мог быть выполнен из командной строки. Настроить npm-сценарии намного проще, чем применять Grunt и Gulp, и мы продолжим задействовать эти сценарии в данной главе. Когда ваш проект станет сложнее, а количество npm-сценариев превысит разумные пределы, позволяющие с ними справиться, можно будет рассмотреть вопрос использования для запуска сборок Grunt или Gulp.

До сих пор для загрузки модулей применялось такое средство, как SystemJS. А к популярным упаковщикам можно отнести Browserify (), Webpack (), Broccoli () и Rollup (). Каждое из этих средств создает пакеты кода, используемые браузером. Самым простым, позволяющим конвертировать и объединять все ресурсы вашего приложения в пакеты с минимальными настройками, является Webpack. Краткое сравнение различных упаковщиков доступно на .

Упаковщик Webpack был создан специально для веб-приложений, запуска­емых в браузере, и многие обычные задачи, необходимые для подготовки сборок веб-приложений, поддерживаются изначально с минимальными настройками конфигурации и без необходимости установки дополнительных модулей. Эта глава начинается с введения в Webpack, а затем вы приготовите две разные сборки (dev и prod) для онлайн-аукциона. И наконец, запустите оптимизированную версию онлайн-аукциона и сравните размер приложения с тем, что показано на рис. 10.1.

Средство SystemJS в этой главе использоваться не будет: Webpack станет вызывать компилятор TypeScript в процессе пакетирования приложений. Процесс компиляции будет управляться специальным загрузчиком, внутри которого для транспиляции TypeScript в JavaScript используется tsc.

ПРИМЕЧАНИЕ

Командой разработчиков Angular создан интерфейс командной строки Angu­lar CLI () для автоматизации создания, тестирования и развертывания приложения. Angular CLI использует упаковщик Webpack на внутреннем уровне. Данный интерфейс будет представлен в этой главе чуть позже.

10.1. Знакомство с Webpack

Готовясь в путешествие, можно упаковать в пару чемоданов десятки разных вещей. Смекалистые путешественники используют специальные пакеты с вакуумным уплотнением, позволяющие положить в тот же самый чемодан еще больше вещей. По сути, Webpack является инструментом, действующим аналогично. Он представляет собой загрузчик и упаковщик модулей, позволяющий сгруппировать файлы вашего приложения в пакеты. Кроме этого, оно позволяет оптимизировать их размеры, чтобы в тот же самый пакет помещалось больше кода.

Например, для развертывания можно подготовить два пакета: все ваши файлы приложения сводятся в один пакет, а все требуемые среды и библиотеки сторонних разработчиков — в другой. Как показано на рис. 10.2, задействуя Webpack, можно подготовить отдельные пакеты для развертывания в двух разных целях: для разработки (режим dev) и реального применения (режим prod). В первом случае пакеты создаются в памяти, а во втором Webpack создаст настоящие файлы на диске.

75163.png 

Рис. 10.2. Средства развертывания dev и prod

Приложение удобно создавать в виде набора небольших модулей и сохранять в одном файле один модуль, но для развертывания требуется инструментальное средство для упаковки всех этих файлов в небольшое количество пакетов. Данному средству должно быть известно, как производить сборку дерева зависимостей модуля, избавляя вас от необходимости самому определять порядок загружаемых модулей. Webpack является именно таким средством, и согласно его философии все может быть модулем, включая CSS, изображения и HTML.

Процесс развертывания с помощью Webpack состоит из двух основных этапов.

1. Сборка пакетов (сюда может включаться оптимизация кода).

2. Копирование пакетов на нужный сервер.

Webpack распространяется в виде npm-пакета, и, как и все остальные инструментальные средства, его можно устанавливать либо глобально, либо локально. Начнем с глобальной установки:

npm install webpack -g

ПРИМЕЧАНИЕ

В данной главе используется Webpack 2.1.0, который на момент написания этих строк представлял собой бета-выпуск. Для его глобальной установки мы задействовали команду npm i -g.

Чуть позже Webpack будет установлен локально путем добавления его в раздел devDependencies файла package.json. Но его глобальная установка позволит быстро увидеть процесс превращения приложения в пакет.

СОВЕТ

Рекомендованный список ресурсов Webpack (документация, видео, библио­теки и т.д.) можно найти на GitHub. Обратите внимание на awesome-webpack: .

10.1.1. Hello World с Webpack

Познакомимся с Webpack через самый простой пример Hello World, состоящий из двух файлов — index.html и main.js. Файл index.html имеет следующий вид (листинг 10.1).

Листинг 10.1. Содержимое файла index.html

<!DOCTYPE html>

<html>

<body>

  <script src="main.js"></script>

</body>

</html>

Файл main.js гораздо короче (листинг 10.2).

Листинг 10.2. Содержимое файла main.js

document.write('Hello World!');

Откройте в каталоге, содержащем этот файл, окно командной строки и запустите следующую команду:

webpack main.js bundle.js

Файл main.js является исходным, а файл bundle.js в том же самом каталоге —выходным. Обычно в имя выходного файла включается слово bundle. Результат запуска предыдущей команды показан на рис. 10.3.

ch10_03.tif 

Рис. 10.3. Создание первого пакета

Обратите внимание: размер созданного файла bundle.js больше размера файла main.js, поскольку Webpack не просто копирует один файл в другой, но и добавляет другой код, требуемый этим пакетом. Создание пакета из одного файла вряд ли принесет какую-то пользу, поскольку тем самым увеличивается размер файла, а вот для приложения, состоящего из множества файлов, упаковка всех файлов в единый пакет имеет смысл. Вы сможете в этом убедиться, прочитав данную главу.

Теперь нужно внести изменения в тег <script> в файле HTML, чтобы включить bundle.js вместо main.js. Это крохотное приложение выведет то же самое сообщение Hello World!, что и его первоисточник.

Webpack позволяет указывать в командной строке разные ключи, но лучше все же сконфигурировать процесс упаковки, выполняемой Webpack, в файле webpack.config.js, являющемся файлом JavaScript. Простой файл конфигурации имеет следующий вид (листинг 10.3).

Листинг 10.3. Содержимое файла webpack.config.js

const path = require('path');

module.exports = {

  entry: "./main",

  output: {

    path: './dist',

    filename: 'bundle.js'

  }

};

Для создания пакета Webpack должен знать об основном модуле (точке входа) вашего приложения, который может иметь зависимости от других модулей или библиотек сторонних производителей (другие точки входа). В исходном состоянии упаковщик добавляет к имени входной точки, указанной в свойстве entry, расширение .js. Webpack загружает модуль входной точки и выстраивает в памяти дерево из всех модулей-зависимостей. Прочитав конфигурационный файл, показанный в листинге 10.3, Webpack будет знать, что вход в приложение расположен в файле ./main.js и получившийся в результате работы этого средства файл bundle.js должен быть сохранен в каталоге ./dist, имя которого часто используется для распространяемых пактов.

СОВЕТ

Сохранение выходных файлов в отдельном каталоге позволит настроить свою систему контроля версиями на исключение созданных файлов. Если применяется система контроля версиями Git, то добавьте каталог dist в файл .gitignore.

Можно указать более одной точки входа, предоставив в качестве значения свойства entry массив:

entry: ["./other-module", "./main"]

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

ПРИМЕЧАНИЕ

Для создания нескольких пакетов значения для свойства entry нужно указать в виде объектов, а не строк. Соответствующий пример, в котором код Angular будет помещен в один пакет, а код приложения в другой, мы приведем в этой главе чуть позже.

Если файл конфигурации Webpack находится в текущем каталоге, то предоставлять какие-либо параметры командной строки не нужно, и для создания пакетов можно просто воспользоваться командой webpack. Другой вариант предусматривает запуск упаковщика в режиме отслеживания с помощью ключа командной строки --watch или -w, чтобы при любом изменении файлов приложения Webpack выполнял автоматическую пересборку пакета:

webpack --watch

Можно также заставить Webpack запускать режим отслеживания путем добавления в webpack.config.js следующей записи:

watch: true

Использование webpack-dev-server

В предыдущих главах для обслуживания ваших приложений использовался live-сервер, но Webpack поставляется со своим собственным webpack-dev-сервером, который должен устанавливаться отдельно. Обычно этот упаковщик добавляют к существующему npm-проекту и устанавливают Webpack и его разработочный сервер локально путем запуска следующей команды:

npm install webpack webpack-dev-server --save-dev

Данная команда приведет к установке всех требуемых файлов в подкаталог node_modules и к добавлению webpack и webpack-dev-server к разделу devDependencies файла package.json.

Следующая версия Hello World находится в каталоге hello-world-devserver и включает показанный далее файл index.html (листинг 10.4).

Листинг 10.4. Содержимое файла hello-world-devserver/index.html

<!DOCTYPE html>

<html>

<body>

  <script src="/bundle.js"></script>

</body>

</html>

JavaScript-файл main.js остается прежним:

document.write('Hello World!');

Файл package.json в проекте hello-world-devserver выглядит следующим образом (листинг 10.5).

Листинг 10.5. Содержимое файла hello-world-devserver/package.json

{

  "name": "first-project",

  "version": "1.0.0",

  "description": "",

  "main": "main.js",

  "scripts": {

    "start": "webpack-dev-server"

  },

  "keywords": [],

  "author": "",

  "license": "ISC",

  "devDependencies": {

    "webpack": "^2.1.0-beta.25",

    "webpack-dev-server": "^2.1.0-beta.0"

  }

}

Обратите внимание: команда npm start настроена на запуск локального webpack-dev-сервера.

ПРИМЕЧАНИЕ

Когда приложение обслуживается webpack-dev-сервером, он запустится на порте по умолчанию 8080 и станет создавать пакеты в памяти без сохранения их в файле. После этого webpack-dev-сервер станет заново компилировать и обслуживать новые версии пакетов после каждого внесения изменений в код.

Вы можете добавить раздел конфигурации webpack-dev-сервера в раздел devServer файла webpack.config.js. Там можно поместить любые ключи, допускаемые webpack-dev-сервером в командной строке (их описание можно найти в документации по Webpack на ). Файлы, которые должны обслуживаться из текущего каталога, можно указать следующим образом:

devServer: {

  contentBase: '.'

}

Ниже показан весь конфигурационный файл для проекта hello-world-devserver (листинг 10.6), который может быть повторно использован обеими командами: как webpack, так и webpack-dev-server.

Листинг 10.6. Содержимое файла hello-world-devserver/webpack.config.js

const path = require('path');

module.exports = {

  entry: {

    'main': './main.js'

  },

  output: {

    path: './dist',

    filename: 'bundle.js'

  },

  watch: true,

  devServer: {

    contentBase: '.'

  }

};

В листинге 10.6 два ключа нужны, только если вы планируете запустить коман­ду webpack в отслеживающем режиме и настроить ее на создание выходных файлов на диске:

Node-модуль path разрешает в вашем проекте относительные пути (в данном случае в нем указывается каталог ./dist);

• watch: true запускает Webpack в отслеживающем режиме.

Если дать команду webpack-dev-server, то предыдущие два ключа не используются. Webpack-dev-сервер всегда запускается в отслеживающем режиме, не выводит файлы на диск и собирает пакеты в памяти. Свойство contentBase позволяет webpack-dev-серверу узнать местоположение вашего файла index.html.

Попробуем запустить приложение Hello World путем обслуживания приложения web-pack-dev-сервером. Для запуска этого сервера введите в окне команд команду npm start. На консоли webpack-dev-сервер станет регистрировать вывод, начинающийся с URL, который можно использовать с применением браузера, и по умолчанию имеющий вид .

Откройте в своем браузере этот веб-адрес и увидите окно, в котором выведено сообщение Hello World. Измените текст в main.js: Webpack автоматически пересоберет пакет, и сервер перезагрузит свежее содержимое.

Разрешение имен файлов

Все это хорошо, но вы пишете код в TypeScript, следовательно, Webpack нужно уведомить, что ваши модули могут быть не только в файлах с расширением .js, но и в файлах .ts. В файле webpack.config.js, показанном в листинге 10.6, имя указано с расширением: main.js. Но можно указывать только имена файлов без каких-либо расширений, поскольку в файле webpack.config.js имеется раздел resolve. Как указать упаковщику, что ваши модули могут находиться в файлах с расширениями .js или .ts, показано в следующем фрагменте кода:

resolve: {

  extensions: ['.js', '.ts']

}

Файлы TypeScript также должны пройти предварительную обработку (транспиляцию). Вам нужно сообщить Webpack о необходимости перед созданием пакетов транспилировать файлы вашего приложения с расширением .ts в файлы с расширением .js; как это делается, будет показано ниже.

Обычно средства автоматизации сборок предоставляются со способом указания дополнительных задач, которые необходимо выполнить в ходе процесса сборки. Webpack предлагает загрузчики и дополнительные модули, позволяющие настроить сборки под свои нужды.

10.1.2. Как использовать загрузчики

Загрузчики являются преобразователями, получающими на входе исходный файл и производящими на выходе другой файл (в памяти или на диске), при этом они одновременно ведут работу только с одним файлом. Загрузчик представляет собой небольшой модуль JavaScript с экспортируемой функцией, совершающей соответствующее преобразование. Например, загрузчик json-loader получает входной файл и проводит его синтаксический анализ, рассчитанный на формат JSON. Загрузчик base64-loader преобразует свой вход в строку, закодированную на основе алгоритма base64. Загрузчики играют роль, сходную с ролью задач, выполняемых в других средствах сборки. Некоторые загрузчики входят в состав Webpack, и их не нужно устанавливать отдельно; другие могут быть установлены из открытых репозиториев. В документации по Webpack на GitHub () можно найти перечень загрузчиков, а также посмотреть, как установить и использовать те, что нужны вам.

По сути, загрузчик — это функция, написанная на Node-совместимом JavaScript. Чтобы воспользоваться загрузчиком, не включенным в дистрибутив Webpack, нужно его установить, задействуя npm, и включить его в файл package.json вашего проекта. Можно либо добавить нужный загрузчик вручную в раздел devDependencies файла package.json, либо запустить команду npm install с ключом --save-dev. В случае применения загрузчика ts-loader команда должна иметь следующий вид:

npm install ts-loader --save-dev

Загрузчики перечислены в файле webpack.config.js в разделе module. Например, ts-loader можно добавить следующим образом:

module: {

  loaders: [

    {

      test: /\.ts$/,

      exclude: /node_modules/,

      loader: 'ts-loader'

    },

  ]

}

Эта конфигурация предписывает Webpack проверять (тестировать) каждое имя файла и, если оно соответствует регулярному выражению \.ts$, выполнять его предварительную обработку с помощью загрузчика ts-loader. В синтаксисе регулярных выражений знак доллара в конце является признаком того, что вас интересуют только файлы, чьи имена заканчиваются на .ts. Поскольку включать в пакет файлы Angular с таким расширением вам не нужно, каталог node_modules исключается. Ссылаться на загрузчики можно либо по их полным именам (например, ts-loader), либо по сокращенным, опуская суффикс -loader (например, ts). Если в вашем шаблоне применяются относительные пути (например, template: "./home.html"), то нужно воспользоваться загрузчиком angular2-template-loader.

ПРИМЕЧАНИЕ

Загрузчик SystemJS ни в одном из проектов, представленных в данной главе, не используется. Webpack загружает и трансформирует все файлы проекта с помощью одного или нескольких загрузчиков, сконфигурированных на основе типа файла в webpack.config.js.

Использование загрузчиков для файлов HTML и CSS

В предыдущих главах компоненты Angular, хранящие HTML и CSS в отдельных файлах, были указаны в аннотации @Component в виде templateUrl и styleUrls соответственно. Рассмотрим пример:

@Component({

  selector: 'my-home',

  styleUrls: ['app/components/home.css')],

  templateUrl: 'app/components/home.html'

})

Обычно HTML- и CSS-файлы хранятся в том же каталоге, где расположен код компонента. Можно ли указать путь относительно текущего каталога?

Webpack позволяет сделать это:

@Component({

  selector: 'my-home',

  styles: [home.css'],

  templateUrl: 'home.html'

})

При создании пакетов Webpack автоматически добавляет инструкции require() для загрузки файлов CSS и HTML, заменяя предыдущий код следующим:

@Component({

  selector: 'my-home',

  styles: [require('./home.css')],

  templateUrl: require('./home.html')

})

Затем он проверяет каждую инструкцию require() и заменяет ее содержимым требуемого файла, применяя загрузчики, указанные для соответствующих типов файлов. Используемая здесь инструкция require() не похожа на такую же инструкцию из CommonJS: это внутренняя функция упаковщика, оповещающая его о том, что указанные файлы являются зависимостями. Функция require() не только загружает файлы, но и может перезагрузить их при изменениях (если запустить данное средство в режиме отслеживания или применить webpack-dev-сервер).

Относительные пути в шаблонах при использовании SystemJS

Хорошо, что в Webpack поддерживаются относительные пути. Но как быть, если нужно иметь возможность загружать одно и то же приложение с помощью либо SystemJS, либо Webpack?

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

Но если используется SystemJS и код компонента и его файлов HTML и CSS хранится в одном и том же каталоге, то можно задействовать специальное свойство moduleId. После присвоения этому свойству специальной привязки __moduleName SystemJS

загрузит файлы, имеющие отношение к текущему модулю, без необходимости указания полного пути:

declare var __moduleName: string;

@Component({

  selector: 'my-home',

  moduleId:__moduleName,

  templateUrl: './home.html',

  styleUrls: ['./home.css']

})

Дополнительные сведения об относительных путях можно найти в документации по Angular в разделе Component-Relative Paths на .

В dev-режиме для обработки HTML вы будете использовать загрузчик raw-loader, выполняющий преобразование файлов с расширением .html в строки. Чтобы установить этот загрузчик и сохранить его в разделе devDependencies файла package.json, запустите следующую команду:

npm install raw-loader --save-dev

В prod-режиме вы будете применять загрузчик html-loader, удаляющий из HTML-файлов лишние пробелы, символы новой строки и комментарии:

npm install html-loader --save-dev

Для обработки CSS вы задействуете загрузчики css-loader и style-loader; все связанные CSS-файлы будут встроены в процессе сборки. Загрузчик css-loader проводит синтаксический разбор CSS-файлов и сводит количество стилей к минимуму. Загрузчик style-loader встраивает CSS в виде тега <style> в страницу в динамическом режиме в ходе выполнения кода. Чтобы установить эти загрузчики и сохранить их в разделе devDependencies файла package.json, запустите следующую команду:

npm install CSS-loader style-loader --save-dev

Загрузчики могут быть выстроены в цепочку с помощью символа конвейера. Следующий фрагмент кода взят из файла webpack-config.js, который включает массив загрузчиков. Когда загрузчики указаны в виде массива, они выполняются снизу вверх (то есть загрузчик ts в этом примере будет выполнен первым). Фрагмент, приведенный ниже, взят из учебного проекта, рассматриваемого в следующем разделе, где файлы CSS расположены в двух папках (src и node_modules):

75185.png 

Сначала исключаются CSS-файлы, находящиеся в каталоге node_modules, чтобы это преобразование применялось только к элементам приложения. Здесь выстроены в цепочку загрузчики to-string и css. Загрузчик css выполняется первым, превращая CSS в модуль JavaScript, а затем его выход передается по конвейеру загрузчику to-string для извлечения строки из созданного кода JavaScript. Получившаяся строка встраивается в соответствующие компоненты в аннотации @Component вместо require(), чтобы Angular мог применить верную стратегию ViewEncapsulation.

Затем нужно, чтобы Webpack встроил сторонние файлы CSS, находящиеся в каталоге node_modules (а не в каталоге src). Загрузчик css-loader считывает CSS, создает модуль JavaScript и передает его загрузчику style-loader, который генерирует теги <style> с загруженным кодом CSS и встраивает их в раздел <head> документа HTML. И наконец, файлы HTML превращаются в строки, и транспилируется код TypeScript.

СОВЕТ

В Angular нужно, чтобы код CSS был инкапсулирован в компоненты для получения преимуществ ViewEncapsulation, в соответствии с объяснениями, приведенными в главе 6. Именно поэтому код CSS встраивается в код JavaScript. Но есть способ сборки отдельного пакета, содержащего только CSS, путем использования дополнительного модуля ExtractTextPlugin. Если применяется препроцессор CSS, нужно установить и задействовать загрузчик sass-loader или less-loader.

Для чего нужны предзагрузчики и постзагрузчики

Иногда непосредственно перед тем, как загрузчик начнет свои преобразования, требуется выполнить дополнительную обработку файла. Например, может понадобиться прогнать ваши TypeScript-файлы через утилиту TSLint, чтобы проверить код на читаемость, обслуживаемость и наличие функциональных ошибок. Для этого к конфигурационному файлу Webpack нужно добавить раздел preLoaders:

preLoaders: [

  {

    test: /\.ts$/,

    exclude: /node_modules/,

    loader: "tslint"

  }

]

Предзагрузчики всегда запускаются перед загрузчиками, и если при их работе произойдут какие-либо ошибки, то отчет о них выводится в командную строку. Можно также настроить постобработку путем добавления в файл webpack.config.js раздела postLoaders.

10.1.3. Как использовать дополнительные модули

Если загрузчики Webpack преобразуют файлы поочередно, то дополнительные модули имеют доступ ко всем файлам и могут обрабатывать их до или после запуска загрузчиков. Например, дополнительный модуль CommonsChunkPlugin позволяет создать отдельный пакет для общих модулей, которые требуются различным сценариям в вашем приложении. Дополнительный модуль CopyWebpackPlugin может копировать в каталог сборки build либо отдельные файлы, либо целые каталоги. Дополнительный модуль UglifyJSPlugin выполняет минификацию кода всех транспилированных файлов.

Предположим, что вам нужно разбить код приложения на два пакета, main и admin, и каждый из этих модулей задействует среду Angular. Если только указать две точки входа (main и admin), то каждый пакет будет включать и код приложения, и свою собственную копию Angular. Во избежание такой ситуации код можно обработать дополнительным модулем CommonsChunkPlugin. Тогда средство Webpack не станет включать код Angular в пакеты main и admin; оно создаст отдельный совместно используемый пакет, содержащий только код Angular. Тем самым общий размер вашего приложения будет уменьшен, поскольку в него будет включена только одна копия Angular, совместно применяемая двумя модулями приложения. В этом случае файл HTML должен включать сначала пакет поставщика, а затем пакет приложения.

Дополнительный модуль UglifyJSPlugin является оболочкой для минимизатора UglifyJS, который получает код JavaScript и выполняет различные оптимизации. Например, сжимает код, объединяя последовательно указанные инструкции var; удаляет неиспользуемые переменные и код, к которому отсутствуют обращения, а также оптимизирует инструкции if. Имеющаяся в нем утилита mangler переименовывает локальные переменные, давая им однобуквенные имена. Полное описание UglifyJS можно найти на странице GitHub данного модуля (). Эти и другие дополнительные модули будут показаны в следующих разделах.

10.2. Создание базовой конфигурации Webpack для Angular

Мы обсудили основы Webpack, а теперь посмотрим, как упаковать простое Angular-приложение, написанное на TypeScript. Мы создали небольшое приложение, состоящее из компонентов Home и About и не использующее внешние шаблоны или CSS-файлы. Этот проект находится в каталоге basic-webpack-starter, а его структура показана на рис. 10.4.

ch10_04.tif 

Рис. 10.4. Проект basic-webpack-starter

 

Сценарий main.ts выполняет начальную загрузку ком­понентов AppModule и MyApp, настраивающих маршрутизатор, и имеет две ссылки для навигации — либо к HomeComponent, либо к AboutComponent. Каждый из этих элементов выводит простое сообщение — в контексте данной главы их фактические функциональные возможности не имеют значения. Наше внимание будет сконцентрировано на создании двух пакетов: одного для среды Angular и ее зависимостей и другого — для кода приложения.

Размер у файла vendor.ts небольшой, в нем просто используются операторы import, необходимые среде Angular. Это сделано для создания ситуации с двумя точками входа (main.ts и vendor.ts), содержащими общий код Angular, который будет помещен в отдельный пакет (листинг 10.7).

Листинг 10.7. Содержимое файла basic-webpack-starter/vendor.ts

import 'zone.js/dist/zone';

import 'reflect-metadata/Reflect.js';

import '@angular/';

import '@angular/router';

import '@angular/core';

import '@angular/common';

Поскольку эти операторы import могут также использоваться в main.ts, будет задействован дополнительный модуль CommonsChunkPlugin, чтобы избежать включения кода Angular в оба пакета. Вместо такого включения будет создан отдельный пакет Angular, совместно применяемый основной точкой входа и любой другой точкой входа, если будет принято решение разбить код приложения на мелкие фрагменты.

ПРИМЕЧАНИЕ

Файл vendor.ts должен импортировать все модули, которые нужно включить в общий пакет и убрать из кода приложения. Webpack встроит в общий пакет весь код, необходимый для импортируемых модулей.

Ниже показано содержимое конфигурационного файла webpack.config.js (листинг 10.8).

Листинг 10.8. Содержимое файла basic-webpack-starter/webpack.config.js

75202.png 

77432.png 

Поскольку модуль CommonsChunkPlugin поставляется вместе с Webpack, в отдельной установке он не нуждается. После установки дополнительного модуля copy-webpack-plugin упаковщик найдет его в каталоге node_modules.

В коде, показанном в листинге 10.8, имеются две точки входа: main.ts, содержащая код приложения плюс Angular, и vendor.ts, в которой имеется только код Angular. Следовательно, код Angular является общим для обеих точек входа, и этот дополнительный модуль станет извлекать его из main.ts и сохранять только в vendor .bundle.js.

Хотя для развертывания было бы неплохо упаковать весь код вашего приложения в файл JavaScript, отладку легче выполнять, имея код, который находится в исходном состоянии, в виде отдельных файлов. Добавляя source-map к webpack.config.js, вы предписываете Webpack создать отображения на исходный код, чтобы можно было увидеть источники файлов JavaScript, CSS и HTML, даже если браузер выполняет код из файла пакета bundle.js.

В файле tsconfig.json используется режим "sourceMap": true, позволяющий создавать отображения на исходный код TypeScript. Браузеры загружают файлы отображения на исходный код, только если на них открыта консоль инструментов разработчика, так что создание отображений на исходный код пригодится даже для развертывания коммерческой сборки. Следует иметь в виду: транспиляция кода будет выполняться загрузчиком ts-loader, поэтому создание кода компилятором tsc можно выключить, установив "noEmit": true в файле tsconfig.json.

Теперь посмотрим, как будет изменен npm-файл package.json с целью включения содержимого, связанного с Webpack. В базовую версию package.json в раздел scripts будут добавлены две строки.

Раздел devDependencies будет включать webpack, webpack-dev-server, а также требуемые загрузчики и дополнительные модули (листинг 10.9).

Листинг 10.9. Содержимое файла basic-webpack-starter/package.json

75211.png 

ПРИМЕЧАНИЕ

Для обработки файлов определения типов мы задействовали NPM-пакеты из пространства имен @types и режим работы компилятора TypeScript @types. Чтобы применить режим работы @types, требуется установка TypeScript 2.0 или более поздней версии.

Сценарием start, как и сценарием build, используется одна и та же конфигурация Webpack из файла webpack.config.js. Посмотрим, чем эти сценарии отличаются друг от друга.

10.2.1. Команда npm run build

После запуска команды npm run build окно команд приобретает вид, показанный на рис. 10.5. Приложение имеет объем 2,5 Мбайт и делает для загрузки всего три запроса.

ch10_05.tif 

Рис. 10.5. Запуск команды npm run build

Webpack создает два пакета (bundle.js и vendor.bun­dle.js) и два соответствующих файла отображения на исходный код (bundle.js.map и vendor.bundle.js.map), а также копирует index.html в выходной каталог dist, показанный на рис. 10.6.

ch10_06.tif 

Рис. 10.6. Содержимое каталога dist

 

В файл index.html не входят теги <script> для загрузки Angular. Все, что нужно приложению, находится в двух пакетах, включенных в два тега <script> (листинг 10.10).

Листинг 10.10. Содержимое файла basic-webpack-starter/index.html

<!DOCTYPE html>

<html>

<head>

  <meta charset=UTF-8>

  <title>Basic Webpack Starter</title>

  <base href="/">

</head>

<body>

  <my-app>Loading...</my-app>

  <script src="vendor.bundle.js"></script>

  <script src="bundle.js"></script>

</body>

</html>

Можно открыть окно команд в каталоге dist и запустить уже известный вам сервер live-сервер для наблюдения этого простого приложения в работе. Снимок экрана, показанный на рис. 10.7, был сделан после остановки приложения на контрольной точке, поставленной в коде main.ts, чтобы показать отображение на исходный код в действии. Несмотря на то что браузер выполняет код из пакетов JavaScript, у вас, благодаря созданию отображений на исходный код, по-прежнему имеется возможность отладки кода конкретного модуля TypeScript.

ch10_07.tif 

Рис. 10.7. Установка контрольной точки в модуле TypeScript

10.2.2. Команда npm start

Если вместо команды npm run build запустить команду npm start, то каталог dist не будет создан, и webpack-dev-сервер выполнит сборку (включая создание отображения на исходный код), а также обслужит приложение прямо из памяти. Нужно просто открыть в браузере адрес localhost://8080, и ваше приложение обработается. Сейчас оно имеет объем 2,7 Мбайт, но к концу главы станет намного меньше.

Базовый проект Webpack, представленный в этом разделе, хорош для демонстрационных целей. Но настоящим приложениям требуется более серьезная работа с Webpack, и речь о ней пойдет в следующем разделе.

10.3. Создание конфигураций для разработки и для коммерческого применения

В этом разделе будут показаны две версии конфигурационных файлов Webpack (одна для разработки, а другая — для коммерческого применения), которые могут использоваться в качестве отправной точки ваших настоящих Angular-проектов. Весь код, показанный здесь, находится в каталоге angular2-webpack-starter. Приложение то же, что и в предыдущем разделе, и состоит из двух компонентов — Home и About.

В данном проекте в файле package.json указано больше npm-сценариев, и в него включены два конфигурационных файла: webpack.config.js — для разработочной сборки и webpack.prod.config.js — для сборки с расчетом на коммерческое применение.

10.3.1. Конфигурация для разработки

Начнем с файла разработочной конфигурации webpack.config.js, который, по сравнению с файлом из предыдущего раздела, получил незначительные дополнения (листинг 10.11). В него добавлен новый дополнительный модуль, DefinePlugin, позволяющий создавать переменные, видимые из кода вашего приложения и доступные для использования средством Webpack в ходе сборки.

Листинг 10.11. Дополненный файл angular2-webpack-starter/webpack.config.js

75268.png 

77441.png 

Переменная NODE_ENV используется средой Node.js. Чтобы получить доступ к значению NODE_ENV из JavaScript, применяется специальная переменная pro­cess.env.NODE_ENV. В листинге 10.11 для константы ENV устанавливается значение переменной среды NODE_ENV, если данная переменная определена, или в противном случае значение development. Аналогичным образом задействуются константы HOST и PORT, и все эти значения будут сохранены в объекте metadata.

Переменная ENV используется в main.ts для вызова Angular-функции if (web­pack.ENV === 'production') enableProdMode();. Когда режим коммерческой сборки включен, модуль обнаружения изменений, имеющийся в Angular, не выполняет дополнительного прохода с целью удостовериться в отсутствии изменений пользовательского интерфейса в обработчиках жизненного цикла компонентов.

ПРИМЕЧАНИЕ

Даже если в окне команд не устанавливать значение переменной среды Node, у нее имеется значение по умолчанию development, установленное в файле webpack.config.js.

10.3.2. Конфигурация для коммерческого применения

Теперь посмотрим на код файла конфигурации коммерческого применения webpack.prod.config.js, который использует следующие дополнительные модули: CompressionPlugin, DedupePlugin, OccurrenceOrderPlugin и UglifyJsPlugin (листинг 10.12).

Листинг 10.12. Содержимое файла angular2-webpack-starter/webpack.prod.config.js

75282.png 

Процесс сборки будет начинаться с использования команд npm-сценария, включенных в файл package.json, в котором содержится больше команд, чем в предыдущем таком же файле, рассмотренном в разделе 10.2. Обратите внимание: в файле package.json в команде build явно указывается файл webpack.prod.config.js, содержащий конфигурацию для коммерческого применения, а команда start будет задействовать разработочную конфигурацию из файла webpack.config.js, чье имя применяется разработочным сервером упаковщика Webpack (листинг 10.13).

Листинг 10.13. Содержимое файла angular2-webpack-starter/package.json

75293.png 

Обычно после запуска команды npm install используются следующие команды (значение для переменной среды NODE_ENV в командной строке устанавливаться не будет).

npm start — запускает разработочный сервер упаковщика Webpack в режиме разработки и обслуживает неоптимизированное приложение. Если открыть в браузере инструменты разработчика, то можно увидеть, что приложение запущено в разработочном режиме, поскольку у переменной ENV имеется значение development, в соответствии с установкой в файле webpack.config.js.

• npm run serve:dist — выполняет команду npm run build для создания оптимизированных пакетов в каталоге dist, а также запускает статический веб-сервер и обслуживает оптимизированную версию приложения. Если открыть в брау­зере инструменты разработчика, то мы не увидим сообщения о том, что приложение запущено в разработочном режиме, поскольку оно запущено в режиме для коммерческого применения. Значением переменной ENV является production в соответствии с установкой в файле webpack.prod.config.js.

• npm run serve:dist:aot — запускает ngc-компилятор Angular для компиляции перед выполнением (ahead-of-time, AoT), проводимой до создания пакетов. Тем самым устраняется надобность во включении ngc в код приложения и в дальнейшей оптимизации размера пакета.

СОВЕТ

Файлы сценариев разработки и коммерческого применения мы храним в отдельных файлах, несмотря на то что один и тот же файл можно использовать повторно за счет выборочного применения тех или иных разделов конфигурации на основе значения переменной среды. Кто-то определяет два файла и повторно задействует разработочную конфигурацию в конфигурации для коммерческого применения (например, var devWebpackConfig = require('./webpack.config.js';)). Исходя из нашего опыта, это затрудняет чтение сценария конфигурации, так что мы храним все сборочные конфигурации целиком в отдельных файлах.

ПРИМЕЧАНИЕ

В каждом из примеров, и в webpack.config.js, и в webpack.prod.config.js, менее 60 строк. Если для подготовки подобной конфигурации сборки предполагается использование Gulp, то она будет включать пару сотен строк кода.

10.3.3. Специальный файл определения типов

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

Webpack будет выполнять загрузку и преобразование всех ваших файлов CSS и HTML. В ходе преобразования упаковщик станет заменять все появления кода, похожего на этот:

styles: ['home.css'],

template: require('./home.html')

преобразованным содержимым:

styles: [require('./home.css')],

template: require('./home.html')

Это собственная Webpack-функция require(), а не такая же функция Node.js. Если запускать компилятор tsc, то предыдущий код станет причиной ошибки компиляции, поскольку компилятор не распознает require() с такой сигнатурой.

• Приложение использует константу ENV, определенную в конфигурационных файлах Webpack:

if (webpack.ENV === 'production') {

  enableProdMode();

}

Чтобы компилятор не противился этой переменной, создайте специальный файл определения типов typings.d.ts со следующим содержимым (листинг 10.14).

Листинг 10.14. Содержимое файла angular2-webpack-starter/typings.d.ts

declare function require(path: string);

declare const webpack: {

  ENV: string

};

Определения типов, начинающиеся с ключевого слова declare, не имеют прямой ссылки на фактическую реализацию переменных (таких как функция require() и константа ENV), и в случае изменения кода на вас ложится ответственность за обновление предыдущего файла (например, если вы решите переименовать ENV в ENVIRONMENT).

На рис. 10.8 показан снимок экрана, который был сделан после запуска команды npm run serve:dist и открытия приложения в браузере. Обратите внимание: эта версия приложения делает только три запроса к серверу, и общий размер приложения составляет 180 Кбайт. Оно немного меньше приложения-аукциона, но все же можно сравнить количество запросов к серверу и размер, показанные на рис. 10.1, чтобы увидеть разницу и оценить работу, проделанную Webpack.

ch10_08.tif 

Рис. 10.8. После запуска npm run serve:dist

Теперь посмотрим, как на размер приложения повлияет компиляция перед выполнением. Запустите команду npm run serve:dist:aot. Размер приложения стал всего лишь 100 Кбайт! Впечатляет, не правда ли?

ПРИМЕЧАНИЕ

На момент написания этих строк (Angular 2.1.0) AoT-компиляция производила объем меньше, чем JIT, только для небольших приложений.

Организация непрерывности

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

Непрерывная интеграция (Continuous integration, CI) — организация процесса, запускающего сценарии сборки несколько раз в день, например после каждого объединения кода в репозитории исходного кода. Сценарии сборки включают модульные тесты, минификацию и упаковку. Вам следует установить и настроить CI-сервер, чтобы обеспечить постоянное пребывание ведущей ветви кода приложения в работоспособном состоянии и чтобы никогда не приходилось отвечать на вопрос: «Кто загубил сборку?»

Непрерывная доставка (Continuous delivery, CD) — процесс, подготавливающий ваше приложение к развертыванию. CD заключается в предложении вашим пользователям дополнительных функциональных возможностей и исправлений.

Непрерывное развертывание (Continuous deployment) — процесс развертывания новой версии приложения, подготовленной при выполнении фазы CD. Позволяет получать от ваших пользователей частые отзывы, гарантирующие, что ваша команда разрабатывает продукт, реально необходимый пользователям.

Фронтенд-разработчики зачастую сотрудничают с командой, создающей серверную сторону приложения. У этой команды уже могут быть организованы процессы CI и CD, и вам следует научиться интегрировать вашу сборку с помощью инструментов, используемых для серверной стороны.

Если вы до сих пор верите, что развертывание вручную не является преступлением, то почитайте о произошедшем с Knight Capital Group, обанкротившейся за 45 минут из-за человеческой ошибки в ходе развертывания. В 2014 г. Дуг Севен (Doug Seven) написал статью Knightmare: A DevOps Cautionary Tale, в которой описан этот инцидент (/). Подводя черту, стоит отметить: процессы сборки и развертывания должны быть автоматизированы и носить характер повторяемости, а также не могут зависеть от одного-единственного представителя технического персонала.

СОВЕТ

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

10.4. Что такое Angular CLI

Изначально попасть в мир Angular-разработки было довольно сложно, поскольку требовалось изучить и вручную сконфигурировать множество инструментов. Даже чтобы приступить к разработке простого приложения, надо было владеть языком TypeScript и уметь им пользоваться, знать компилятор TypeScript, модули ES6, SystemJS, npm и разработочный веб-сервер. Для работы с настоящим проектом требовалось также изучить порядок тестирования и упаковки приложения.

Для резкого старта процесса разработки команда Angular создала утилиту под названием Angular CLI (), являющуюся инструментом командной строки, охватывающим все стадии жизненного цикла Angular-приложения, от разработки временной платформы и создания исходного приложения до реализации шаблона для компонентов, модулей, сервисов и т.д. Созданный код также включает предварительно сконфигурированные файлы для модульного тестирования и упаковки с использованием Webpack.

Глобальную установку Angular CLI можно выполнить, написав следующую команду:

npm install -g angular-cli

Angular CLI и Webpack

Angular CLI усиливается упаковщиком Webpack. Внутренне сгенерированный с помощью CLI, проект включает файлы конфигурации Webpack, подобные рассмотренным в данной главе.

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

10.4.1. Запуск нового проекта с Angular CLI

После установки CLI в вашей переменной среды PATH открывается доступ к выполняемому двоичному файлу ng. Команда ng будет использоваться для вызова команд Angular CLI.

Чтобы создать Angular-приложение, применяется команда new:

ng new basic-cli

CLI создаст новый каталог basic-cli, в который будут включены все файлы, требуемые для простого приложения. Для его запуска наберите команду ng serve и откройте в окне своего браузера адрес . Будет показана страница с выведенным на ней сообщением app works!. CLI автоматически установит все требуемые зависимости и создаст в папке локальный git-репозиторий. Сгенерированная структура проекта показана на рис. 10.9.

ch10_09.tif 

Рис. 10.9. Структура CLI-проекта

 

Вкратце рассмотрим основные файлы и каталоги проекта.

e2e — каталог для сквозных тестов.

• src/app — основной каталог для кода приложения. Здесь обычно создаются подкаталоги для маршрутов и дочерних компонентов, но CLI не выставляет это обязательным требованием.

• src/assets — все в данном каталоге в процессе сборки должно быть скопировано как есть в каталог dist.

• src/environments — здесь указываются настройки, относящиеся к среде. Можно создать произвольное количество специализированных сред, например контроля качества — QA, обкатки и коммерческого применения.

• angular-cli.json — основной конфигурационный файл Angular CLI. Здесь можно настроить местоположение файлов и каталогов, от которых зависит CLI, например глобальных CSS-файлов и ресурсов.

10.4.2. Команды CLI

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

Таблица 10.1. Наиболее востребованные команды CLI

Команда

Описание

ng serve

Приводит к сборке пакета в памяти и запускает включенный в среду webpack-dev-сервер, который будет запускаться и пересобирать пакеты после каждого изменения, вносимого в код приложения

ng generate

Приводит к созданию различных итоговых продуктов вашего проекта. Можно также воспользоваться сокращенной версией команды: ng g. Для вывода списка всех доступных ключей нужно ввести команду ng help generate.

Примеры:

ng g c <название-компонента> — приводит к созданию для компонента четырех файлов: TypeScript-файла для исходного кода, TypeScript-файла для модульных тестов элемента, HTML-файла для шаблона и CSS-файла для стилевых настроек представления компонента. Если нужно встроить CSS и шаблоны в создаваемый элемент, то эту команду следует запустить с ключами: например, ng g c product --inline-styles --inline-template. При отсутствии надобности создавать файл спецификаций следует воспользоваться ключом --spec=false;

ng g s <название-сервиса> — приводит к созданию двух TypeScript-файлов: одного для исходного кода и второго — для модульных тестов

ng test

Запускает модульные тесты, задействуя средство для запуска тестов Karma

ng build

Приводит к созданию JavaScript-пакетов с транспилированным кодом приложения и всеми встроенными зависимостями. Пакеты сохраняются в каталоге dist

Чтобы выполнить сборку оптимизированной версии приложения, команду ng build запускают, как правило, с ключами -prod ––aot. Без указания этих ключей размер создаваемых пакетов основного генерируемого приложения равен около 2,5 Мбайт. Создание пакетов с помощью команды ng build -prod сокращает размер пакета до 800 Кбайт, а также приводит к созданию версии пакета, сжатой средством gzip (190 Кбайт).

Чтобы оптимизировать пакет под развертывание с целью коммерческого применения, настройте компиляцию перед выполнением (ahead-of-time compilation), запустив команду ng build --prod --aot. Теперь размер пакета составит примерно 450 Кбайт, а его версия, сжатая средством gzip, уменьшится до 100 Кбайт. На рис. 10.10 показано содержимое каталога dist приложения basic-cli после запуска этой команды.

ch10_10.tif 

Рис. 10.10. Каталог dist после сборки для коммерческого применения

ПРИМЕЧАНИЕ

Чтобы увидеть данный пакет размером 100 Кбайт загруженным в браузер, нужно воспользоваться веб-сервером, поддерживающим обслуживание файлов, которые были сжаты средством gzip непосредственно перед сборкой. Можно, к примеру, задействовать статический node-сервер, применявшийся в подразделе 10.3.2.

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

Может возникнуть вопрос: «Зачем изучать внутреннее устройство Webpack, если Angular CLI сконфигурирует Webpack за нас?» Мы хотели объяснить, как все это работает, на случай, если вы решите вручную создавать свои сборки и проводить их тонкую настройку без использования Angular CLI. К тому же в будущем Angular CLI может позволить вам вносить изменения в автоматически создаваемые файлы конфигурации Webpack, и было бы неплохо знать о том, как это делается.

СОВЕТ

Для ускорения установки зависимостей проектов Angular CLI воспользуйтесь вместо npm диспетчером пакетов Yarn. Подробности можно найти на /.

10.5. Практикум: развертывание онлайн-аукциона с помощью Webpack

В данном упражнении никакой новый код приложения разрабатываться не будет. Целью ставится использование Webpack для сборки и развертывания оптимизированной версии онлайн-аукциона. В процесс сборки будет также интегрировано средство для запуска тестов Karma.

Для этой главы мы переработали проект аукциона из главы 8, который применяет тот же самый файл package.json между клиентской и серверной частями приложения. Теперь клиент и сервер станут отдельными приложениями со своими собственными файлами package.json. Раздельное содержание клиентского и серверного кода упрощает автоматизацию процесса сборки.

Angular и безопасность

В Angular имеется встроенная защита против наиболее распространенных уязвимостей и атак веб-приложений. В частности, для предотвращения межсайтовых сценарных атак выполняется блокировка вредоносного кода, препятствующая его входу в DOM-модель. Что касается изображений, злоумышленник может заменить их вредоносным кодом в атрибуте src тега <img>.

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

Именно поэтому в элементы аукциона, которые задействуют изображения, поступа­ющие со стороннего сервера, добавляется код, предотвращающий обезвреживание изображений. Например, конструктор для компонента ProductItemComponent выглядит следующим образом:

constructor(private sanitizer: DomSanitizer) {

    this.imgHtml = sanitizer.bypassSecurityTrustHtml(`

        <img src="">`);

}

Подробности мер обеспечения безопасности Angular можно найти в документации на .

В файле package.json из каталога client имеются npm-сценарии, необходимые для коммерческой сборки пакета, а также для запуска webpack-dev-сервера в режиме разработки. В каталоге server имеется свой собственный файл package.json с npm-сценариями для запуска Node-сервера аукциона, здесь упаковка не нужна. С технической точки зрения у вас имеются два независимых приложения со своими собственными отдельно сконфигурированными зависимостями. Этот проект-практикум начинается с использования исходного кода, который находится в каталоге auction главы 10.

10.5.1. Запуск Node-сервера

Серверный файл package.json выглядит следующим образом (листинг 10.15).

Листинг 10.15. Содержимое файла auction/package.json

{

  "name": "ng2-webpack-starter",

  "description": "Angular 2 Webpack starter project suitable for a production

    grade application",

  "homepage": "-

    typescript",

  "private": true,

  "scripts": {

    "tsc": "tsc",

    "startServer": "node build/auction.js",

    "dev": "nodemon build/auction.js"

  },

  "dependencies": {

    "express": "^4.13.3",

    "ws": "^1.0.1"

  },

  "devDependencies": {

    "@types/compression": "0.0.29",

    "@types/es6-shim": "0.0.27-alpha",

    "@types/express": "^4.0.28-alpha",

    "@types/ws": "0.0.26-alpha",

    "compression": "^1.6.1",

    "nodemon": "^1.8.1",

    "typescript": "^2.0.0"

  }

}

Обратите внимание: здесь определяется сценарий tsc в целях гарантировать использование локальной версии TypeScript 2.0, даже если у вас имеется старая версия компилятора, установленная глобально. Перейдите в командной строке в каталог server и запустите команду npm install, чтобы получить все необходимые зависимости для серверной части приложения.

Для применения локального компилятора нужно запустить команду npm run tsc, которая приведет к транспиляции серверного кода и к созданию файлов auction.js и model.js (и их отображений на исходный код) в каталоге build, в соответствии с конфигурацией, указанной в файле tsconfig.json. Это код для сервера аукциона.

Запустите сервер путем ввода команды npm run startServer. Он выведет в консоли сообщение Listening on 127.0.0.1:8000.

10.5.2. Запуск клиента аукциона

Клиентскую часть аукциона можно запустить либо в разработочном режиме, либо в режиме коммерческой сборки, используя различные команды npm-сценариев. Раздел npm-сценариев клиентского файла package.json имеет следующие команды:

"scripts": {

  "clean": "rimraf dist",

  "prebuild": "npm run clean && npm run test",

  "build": "webpack --config webpack.prod.config.js --progress --profile",

  "startWebpackDevServer": "webpack-dev-server --inline --progress --

     port 8080",

  "test": "karma start karma.conf.js",

  "predeploy": "npm run build && rimraf ../server/build/public && mkdirp

    ../server/build/public",

  "deploy": "copyup dist/* ../server/build/public"

}

Большинство команд должно быть вам знакомо, поскольку они использовались в файле webpack.prod.config.js, показанном в листинге 10.12. Здесь добавлена новая команда deploy, в которой применена команда copyup, предназначенная для копирования файлов из клиентского каталога dist в серверный каталог build/public. В данном случае задействована команда copyup из npm-пакета copyfiles (). Когда дело касается копирования файлов, этот пакет используется для кросс-платформенной совместимости. Кроме того, добавлена команда test для запуска тестов с помощью программы Karma (см. подраздел 10.5.3).

Поскольку здесь имеется команда predeploy, она будет автоматически запускаться при каждом вводе команды npm run deploy. В свою очередь, команда predeploy запустит команду build, которая автоматически включит команду prebuild. Последняя запустит команды clean и test, и только после того, как выполнение всех команд успешно завершится, команда build приведет к сборке. И наконец, команда copyup приведет к копированию пакетов из каталога dist в каталог server/build/public.

Перед запуском клиентской части аукциона нужно открыть отдельное окно команд, перейти в каталог client и запустить команду npm install. Затем следует запустить приложение-аукцион в разработочном режиме путем ввода команды npm run startWebpackDevServer. Webpack-dev-сервер привяжется к вашему Angular-приложению и начнет отслеживать запросы браузера, отправляемые через порт 8080. Введите в браузер адрес и увидите знакомый пользовательский интерфейс приложения-аукциона.

ПРИМЕЧАНИЕ

Разработочная сборка выполнена в памяти, и приложение-аукцион доступно на порте 8080, который является портом, сконфигурированным в файле webpack.config.js.

Откройте вкладку Network (Сеть) на панели Developer Tools (Инструменты разработчика) браузера Chrome. Вы увидите следующее: приложение загружает только что собранные пакеты и его размер еще довольно велик.

Проверьте в консоли журнал Webpack и увидите, какие файлы в какой пакет (или фрагмент) попали. В данном случае собираются два фрагмента: bundle.js и vendor.js. На рис. 10.11 показан небольшой фрагмент журнала Webpack, но вы можете увидеть размер каждого файла. Упакованное приложение (bundle.js) имеет объем 285 Кбайт, а код поставщика (vendor.bundle.js) — 3,81 Мбайт.

ch10_11.tif 

Рис. 10.11. Фрагмент вывода на консоль

В верхней части показано несколько файлов шрифтов, которые не попали в пакет, так как в файле webpack.config.js указан параметр limit, чтобы исключить вставку в пакет крупных шрифтов:

{test: /\.woff$/, loader: "url?limit=10000&minetype=application/font-woff"},

{test: /\.woff2$/, loader: "url?limit=10000&minetype=application/font-woff"},

{test: /\.ttf$/, loader: "url?limit=10000&minetype=application/

  octet-stream"},

{test: /\.svg$/, loader: "url?limit=10000&minetype=image/svg+xml"},

{test: /\.eot$/, loader: "file"}

В последней строке загрузчику file-loader предписывается копировать шрифты с расширением .eot в каталог сборки. Если прокрутить вывод в консоли, то можно будет увидеть, что весь код приложения попал в фрагмент {0}, а весь код поставщика — в фрагмент {1}.

ПРИМЕЧАНИЕ

В режиме разработки Angular-приложение под Node-сервером не развертывается. Node-сервер запущен с привязкой к порту 8000, а клиент приложения-аукциона получает обслуживание через порт 8080 и обменивается данными с Node-сервером с помощью протоколов HTTP и WebSocket. Развертывание Angular-приложения под Node-сервером будет выполнено на следующем этапе.

Теперь остановите webpack-dev-сервер (но не Node-сервер), нажав сочетание клавиш Ctrl+C, находясь в окне команд, из которого запускался клиент. Запустите коммерческую сборку, введя команду npm run deploy. Эта команда приведет к подготовке оптимизированной сборки и к копированию ее файлов в каталог ../server/build/public, к которому принадлежит все статическое содержимое Node-сервера.

Node-сервер перезапускать не нужно, поскольку здесь выполняется развертывание только статического кода. Но, чтобы посмотреть версию аукциона, предназначенную для коммерческого применения, вам нужно воспользоваться портом 8000, на котором запущен Node-сервер.

Откройте в окне браузера адрес . Там вы увидите приложение-аукцион, обслуживаемое Node-сервером. Откройте панель Developer Tools (Инструменты разработчика) браузера Chrome на вкладке Network (Сеть) и обновите вывод приложения. Вы увидите, что размер оптимизированного приложения существенно уменьшился. На рис. 10.12 показано: общий размер приложения составляет 349 Кбайт (по сравнению с 5,5 Мбайт в показанной ранее на рис. 10.1 неупакованной версии).

Чтобы загрузить index.html, два пакета и черно-белые изображения, представляющие товары, браузер делает к серверу девять запросов. Кроме того, можно увидеть запрос на данные о товарах, который клиент выполнил, используя имеющийся в Angular Http-запрос. Строка, оканчивающаяся на .woff2, является шрифтом, загруженным Twitter-фреймворком Bootstrap.

ch10_12.tif 

Рис. 10.12. Все, что загружено в prod-версии аукциона

Загрузчик url-loader работает примерно также, как и file-loader, но может встраивать файлы меньше указанного лимита в CSS, где они определены. В качестве лимита для файлов с именами, оканчивающимися на .woff, .woff2, .ttf и .svg, был указан размер 10 000 байт. Один из более крупных файлов (17,9 Кбайт), встроен не был.

Каждый шрифт представлен в нескольких форматах, таких как .eot, .woff, .woff2, .ttf и .svg. Для работы со шрифтами существует несколько невзаимо­исключающих вариантов:

встроить их все в пакет и позволить браузеру выбрать для использования один из них;

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

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

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

10.5.3. Запуск тестов с помощью Karma

В главе 9 были разработаны три спецификации для модульного тестирования: ApplicationComponent, StarsComponent и ProductService. Здесь будут повторно использоваться те же самые спецификации, но запускаться они будут с применением Karma в качестве составной части процесса сборки.

Поскольку теперь клиент и сервер являются npm-проектами, файлы конфигурации Karma karma.conf.js и karma-test-runner.js расположены в клиентском каталоге (листинг 10.16).

Листинг 10.16. Содержимое файла auction/client/karma.conf.js

75364.png 

Файл karma.conf.js намного короче, чем такой же файл из главы 9, поскольку вам не нужно больше конфигурировать файлы для SystemJS, — теперь загрузчиком является Webpack, и файлы уже сконфигурированы в webpack.test.config.js. В листинге 10.17 показан код сценария karma-test-runner.js, используемого для запуска Karma.

Листинг 10.17. Содержимое файла auction/client/karma-test-runner.js

Error.stackTraceLimit = Infinity;

require('reflect-metadata/Reflect.js');

require('zone.js/dist/zone.js');

require('zone.js/dist/long-stack-trace-zone.js');

require('zone.js/dist/proxy.js');

require('zone.js/dist/sync-test.js');

require('zone.js/dist/jasmine-patch.js');

require('zone.js/dist/async-test.js');

require('zone.js/dist/fake-async-test.js');

var testing = require('@angular/core/testing');

var browser = require('@angular/platform-browser-dynamic/testing');

testing.TestBed.initTestEnvironment(

  browser.BrowserDynamicTestingModule,

  browser.platformBrowserDynamicTesting());

Object.assign(global, testing);

var testContext = require.context('./src', true, /\.spec\.ts/);

function requireAll(requireContext) {

  return requireContext.keys().map(requireContext);

}

var modules = requireAll(testContext);

Далее показан код, содержащийся в файле webpack.test.config.js (листинг 10.18). Он упрощен для проведения тестирования, поскольку в ходе проверки создавать пакеты не нужно. Имеющийся в Webpack dev-сервер не нужен, так как в качестве сервера выступает само средство Karma.

Листинг 10.18. Содержимое файла auction/client/webpack.test.config.js

const path         = require('path');

const DefinePlugin = require('webpack/lib/DefinePlugin');

const ENV  = process.env.NODE_ENV = 'development';

const HOST = process.env.HOST || 'localhost';

const PORT = process.env.PORT || 8080;

const metadata = {

  env : ENV,

  host: HOST,

  port: PORT

};

module.exports = {

  debug: true,

  devtool: 'source-map',

  module: {

    loaders: [

      {test: /\.css$/,  loader: 'raw', exclude: /node_modules/},

      {test: /\.css$/,  loader: 'style!css?-minimize', exclude: /src/},

      {test: /\.html$/, loader: 'raw'},

      {test: /\.ts$/,   loaders: [

        {loader: 'ts', query: {compilerOptions: {noEmit: false}}},

        {loader: 'angular2-template'}

      ]}

    ]

  },

  plugins: [

    new DefinePlugin({'webpack': {'ENV': JSON.stringify(metadata.env)}})

  ],

  resolve: { extensions: ['.ts', '.js']}

  }

};

В файле client/package.json находится следующее содержимое, имеющее отношение к Karma (листинг 10.19).

Листинг 10.19. Содержимое файла auction/client/package.json

"scripts": {

...

"test": "karma start karma.conf.js"

}

...

"devDependencies": {

...

    "karma": "^1.2.0",

    "karma-chrome-launcher": "^2.0.0",

    "karma-firefox-launcher": "^1.0.0",

    "karma-jasmine": "^1.0.2",

    "karma-mocha-reporter": "^2.1.0",

    "karma-webpack": "^1.8.0",

}

Для запуска тестов вручную введите команду npm test в окне команд, находясь при этом в каталоге client. Вы увидите вывод, похожий на тот, что показан на рис. 10.13.

ch10_13.tif 

Рис. 10.13. Запуск Karma

Чтобы интегрировать запуск Karma в процесс сборки, можно изменить команду npm prebuild, придав ей следующий вид:

"prebuild": "npm run clean && npm run test"

"build": "webpack ...",

Теперь запуск команды npm run build повлечет старт команды prebuild, которая приведет к очистке каталога output, запустит тесты, а затем выполнит сборку. Если какой-либо из тестов окажется не пройден, то команда сборки build запущена не будет.

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

ПРИМЕЧАНИЕ

В исходный код, сопровождающий эту главу, включен каталог под названием extras, содержащий еще одну реализацию аукциона, в которой часть, относящаяся к Angular, была создана с применением Angular CLI. Загляните в раздел scripts файла angular-cli.json, чтобы увидеть, как добавить библиотеки сторонних разработчиков к проекту Angular CLI.

10.6. Резюме

В данной главе речь шла не о создании кода. Целью ставилось изучение вопросов оптимизации и упаковки кода для его развертывания. В сообществе приверженцев JavaScript имеется несколько весьма популярных утилит для автоматизации сборки и упаковки веб-приложений, но наш выбор пал на сочетание Webpack и npm-сценариев.

Webpack является весьма технологичным и сложным инструментом, но мы представили небольшие комбинации загрузчиков и дополнительных модулей, выполнявших наши задачи. Если вы ищете более сложную конфигурацию Webpack, то попробуйте angular2-webpack-starter ().

Вот основные выводы этой главы.

Процесс подготовки пакетов для развертывания и запуска сборки должен быть автоматизирован на ранних стадиях разработки.

• Чтобы свести к минимуму количество запросов, выполняемых браузером для загрузки вашего приложения, объединяйте код в небольшое количество пакетов для развертывания.

• Избегайте упаковки одного и того же кода более чем в один пакет. Храните программные среды сторонних разработчиков в отдельном пакете, чтобы другие пакеты вашего приложения могли им совместно пользоваться.

• Всегда создавайте отображения на исходный код, поскольку они позволят вести отладку исходного кода в TypeScript. Такие отображения не увеличивают размер кода приложения и создаются, только если в браузере открыта панель Developer Tools (Инструменты разработчика). Поэтому использование отображений на исходный код приветствуется даже в коммерческих сборках.

• Чтобы запустить задачи по сборке и развертыванию, воспользуйтесь npm-сценариями, поскольку создавать их довольно просто, а диспетчер пакетов npm уже установлен. Если ведется работа по подготовке сборки для более крупного и сложного проекта и вы чувствуете потребность в языке сценариев для описания различных сценариев сборки, то введите в обиход своего проекта такое средство, как Gulp.

• Для быстрого старта в разработке в среде Angular и TypeScript создайте свой первый проект, используя Angular CLI.

Назад: 9. Модульное тестирование Angular-приложений
Дальше: Приложение А. Общее представление о ECMAScript 6

32
32
Alex
32
fagmefs
kamagra 100 mg on line
lasix other names
Amoxicillin 250 5ml
dragzolotoru
Ювелирные изделия, те что покупатели имеем или приобретаем именно на подарок родным несут внутри себе большое число увлекательных данных, которые реально просто изучить, когда Вы нажмет на источник новых публикаций касательно драгоценных изделий виды плетение цепочек. Интернет страничка золотых украшений ознакомит любителей из спектр полезными умений, с пособием их Вы могут хорошо ориентироваться у высококачества металлических плюс популярных изделий, к тому же дорогих породах камней плюс металлах. Онлайн сайт всякой проверенных данных о украшения ежедневно увеличивает сведения, те что смогут Вам вернее разбираться какие именно изделия совмещаются в стиле, как же требуется чистить про ювелирными предметами, и что сегодня актуально. Переходите на сайт, читайте также лайкайте подходящие вам новости или же делитесь ссылки на странички соц. сети, здесь на сайте мы будем систематически дополнять существующие библиотеку постов ювелирных украшений.
CharlesTut
импорт товаров
JacobBal
Каждый человек способен испытать испуг в разных жизненных ситуациях. Это – абсолютно обычное явление, помогающее нам спасти себе жизнь в момент угрозы жизни. Правда большинство страхов это просто напросто иррациональное переживание. Например страх летать на самолёте. С этими страхами возможно будет бороться успешно, существуют очень действенные технологии, про них узнаете в анализе ссылка на статью Также можно почувствовать страх опасности, именно это ощущение помогает спасти человеческую жизнь в угрожающих ситуациях. Соответственно страх это адекватное состояние, проблема может возникнуть только лишь если подобный страх часто сводит с ума. В этом случае надо предпринимать определенные меры, обращаться в психологический центр.
Caseybup
електро тепла підлога
Howardton
электро полы с подогревом