Вашему вниманию предлагается перевод туториала «5 min quickstart» от команды Angular. Туториал описывает процесс создания «Hello World»-приложения на новом фреймворке Angular 2, который недавно получил статус «бета».
Давайте начнём с нуля и построим суперпростое приложение Angular2 на TypeScript.
Запуск работающего примера — это самый лучший способ увидеть, как оживает приложение на Angular 2.
Нажатие этой ссылки открывает новую вкладку, загружает пример в plunker и отображает простое сообщение:
Вот файловая структура:
Функционально это index.html и два файла TypeScript в папке app/. Давайте сделаем это!
Конечно, мы не будем создавать приложения, которые будут запускаться только в plunker. Давайте сделаем всё как если бы мы делали это в реальной жизни:
Нам необходимо место (папка проекта), несколько библиотек, некоторое количество настроек TypeScript и редактор с поддержкой TypeScript на ваш выбор.
Мы рекомендуем использовать менеджер пакетов npm для загрузки и управления библиотеками.
Добавьте файл package.json в папку проекта и скопируйте в него следующий код:
Установите пакеты. Откройте окно терминала (командную строку в Windows) и запустите эту команду npm:
Нам необходимо довольно специфично настроить компилятор TypeScript.
Добавьте файл tsconfig.json в папку проекта и скопируйте в него следующее:
Итак, всё готово. Давайте напишем немного кода.
Компонент(Component) — это наиболее фундаментальная концепция Angular. Компонент настраивает отображение(view) — часть веб-страницы, где мы показываем информацию пользователю и реагируем на его действия.
Технически, компонент — это класс, который контролирует шаблон отображения. Мы будем писать много таких классов при создании приложений Angular. Это наша первая попытка, так что мы сделаем всё предельно просто.
Мы предпочитаем хранить код нашего приложения в подпапке под названием app/. Выполните следующую команду в консоли:
Теперь добавьте файл с именем app.component.ts и скопируйте в него следующий код:
Давайте подробно рассмотрим этот файл, начиная с самого конца, где мы определяем класс.
В самом низу файла располагается пустой и ничем не занятый класс под названием AppComponent. Когда мы будем готовы создать независимое приложение, мы сможем дополнить этот класс свойствами и логикой приложения. Наш класс AppComponent пуст, потому что нам не нужно, чтобы он что-то делал в этом QuickStart.
Приложения на Angular модульны. Они включают в себя множество файлов, каждый из которых имеет определённую цель.
Большинство файлов приложения экспортируют что-нибудь вроде компонента. Наш файл app.component экспортирует класс AppComponent.
Факт экспорта превращает обыкновенный файл в модуль. Имя файла (без его расширения), как правило, является именем модуля. Таким образом, 'app.component' — это имя нашего первого модуля.
Более сложные приложения могут иметь дочерние компоненты, которые наследуют AppComponent в визуальном дереве. Более сложные приложения будут иметь больше файлов и модулей, как минимум столько, сколько у них есть компонентов.
QuickStart не сложен: один компонент — это всё, что нам нужно. Однако модули играют фундаментальную организационную роль даже в таком маленьком приложении.
Модули полагаются на другие модули. В приложениях Angular, написанных на TypeScript, когда нам нужно что-то, предоставляемое иным модулем, мы импортируем его. Когда другому модулю требуется сослаться на AppComponent, он импортирует символ AppComponent следующим образом:
Angular также является коллекцией библиотечных модулей. Каждая библиотека представляет собой модуль, составленный из нескольких связанных общим направлением модулей.
Если нам требуется что-то из Angular, мы импортируем это из его библиотечного модуля. И прямо сейчас нам нужно кое-что, чтобы иметь возможность определить метаданные для нашего компонента.
Класс становится компонентом Angular, когда мы привязываем к нему метаданные. Angular нуждается в метаданных для того, чтобы понять, как нужно конструировать отображение, и как компонент взаимодействует с другими частями приложения.
Мы определяем метаданные компонента с помощью функции Component из Angular. Мы получаем доступ к этой функции, импортируя её из основной библиотеки Angular — angular2/core.
В TypeScirpt мы можем связать функцию и класс через превращение функции в декоратор. Для этого перед её названием нужно добавить символ @, и поместить её прямо перед объявлением класса.
@Component сообщает Angular, что данный класс является компонентом Angular. Объект конфигурации, отправляемый в @Component, имеет два поля: selector и template.
Свойство selector определяет обычный CSS селектор для HTML-элемента my-app, выступающего в качестве хоста. Angular создаёт и отображает экземпляр нашего AppComponent каждый раз, когда он сталкивается с my-app в родительском HTML.
Свойство template содержит шаблон компонента. Шаблон — это разновидность HTML, которая объясняет Angular, как рендерить отображение. Наш шаблон — это единственная строка HTML, объявляющая: «My First Angular App».
Теперь нам нужно как-то объяснить Angular, что этот компонент необходимо загрузить.
Добавьте новый файл, boot.ts, в папку app/, и скопируйте в него следующий код:
Нам требуются две вещи для того, чтобы запустить приложение:
Мы импортируем и то, и другое. Потом мы вызываем bootstrap и передаём в неё тип корневого компонента AppComponent.
Мы попросили Angular запустить приложение в браузере с нашим компонентом в качестве корневого. Однако, где же Angular его запустит?
Angular отображает наше приложение в специфическом месте на нашей странице index.html. Пришло время создать этот файл.
Мы не будем класть index.html в папку app/. Мы расположим его на один уровень выше, в корневой папке проекта.
Теперь создайте файл index.html и скопируйте в него следующее:
Здесь есть 3 секции HTML, которые необходимо отметить:
Что-то должно найти и загрузить модули нашего приложения. Мы используем для этого SystemJS. Есть много вариантов, и нельзя сказать, что SystemJS — лучший выбор, но мы его любим, и он работает.
Специфика настройки SystemJS выходит за пределы данного туториала. Мы кратко опишем часть конфигурации в приложении ниже.
Когда Angular вызывает функцию bootstrap в boot.js, он читает метаданные AppComponent, находит селектор my-app, обнаруживает тэг с именем my-app и загружает наше приложение в этот тэг.
Откройте окно терминала и введите следующую команду
Эта команда запускает два параллельных процесса node.
В течение нескольких мгновений вкладка браузера должна открыться и отобразить:
Поздравляем! Вы в деле!
Попробуйте изменить сообщение на «My SECOND Angular 2 app».
Компилятор TypeScript и lite-server наблюдают за вашими действиями. Они должны засечь изменение, перекомпилировать TypeScript в JavaScript, обновить вкладку браузера и отобразить обновлённое сообщение.
Это изящный способ разработки приложений!
Мы закрываем окно терминала когда мы всё сделали для того, чтобы прервать одновременно компилятор и сервер.
Финальная структура нашей папки проекта должна выглядеть так:
И вот наши файлы:
Наше первое приложение делает не слишком много. Это, по сути, «Hello World» на Angular 2.
Для первого раза мы сделали всё максимально просто: написали небольшой компонент Angular, добавили немного библиотек JavaScript в index.html и запустили статический файловый сервер. В целом, это всё, что мы ожидали от «Hello World»-приложения.
Хорошая новость в том, что вся возня с установкой нас не касается. Возможно, мы чуть-чуть коснёмся package.json для обновления библиотек. Мы откроем index.html только если нам нужно будет добавить библиотеку или файл css-стилей.
Мы готовы к следующему шагу, и теперь наша задача — сконструировать приложение, которое демонстрирует, насколько великолепные вещи можно сделать с помощью Angular 2.
Присоединяйтесь к нам в туториале «Геройский тур»!
Остаток данной главы посвящён набору приложений, более подробно излагающих кое-какие моменты, которые мы кратко затронули выше.
Здесь нет никакого критически важного материала. Читайте дальше, если вам интересны детали.
Angular 2 полагается на некоторые возможности стандарта ES2015, большая часть из которых уже включена в современные браузеры. Однако некоторым браузерам (таким, как IE11), требуются полифиллы (shim) для поддержки этой функциональности. Попробуйте загрузить следующие полифиллы перед другими скриптами в index.html:
npm — это популярный менеджер пакетов для node, и многие Angular-разработчики используют его для загрузки и управления библиотеками, которые необходимы их приложениям.
Мы определили пакеты, которые нам необходимы в файле npm package.json.
Команда Angular предлагает использовать пакеты, указанные в секциях dependencies и devDependencies в этом файле:
В package.json может присутствовать необязательная секция scripts, в которой мы можем определить полезные команды для выполнения разработки и построения. Мы включаем несколько таких скриптов в предлагаемом нами package.json:
Мы уже видели, как можно запустить компилятор и сервер одновременно с помощью этой команды:
Мы исполняем скрипты npm в следующем формате: npm run + название-скрипта. Вот описание того, что делают скрипты:
Всё хорошо, когда любые консольные сообщения, начинающиеся с npm ERR! отсутствуют в конце работы npm install. Могут быть несколько сообщений npm WARN в течение работы команды — и это превосходный результат.
Мы часто наблюдаем сообщение npm WARN после серии gyp ERR! Игнорируйте его. Пакет может попытаться перекомпилировать себя с помощью node-gyp. Если эта попытка заканчивается неудачей, пакет восстанавливается (обычно на предыдущую версию), и всё работает.
Всё хорошо до тех пор, пока нет ни одного сообщения npm ERR! в самом конце npm install.
Мы добавили конфигурационный файл (tsconfig.json) в наш проект, чтобы объяснить компилятору, как нужно генерировать файлы JavaScript. Подробнее про tsconfig.json вы можете узнать из официальной TypeScript wiki.
Опции и флаги, которые мы добавили в файл, являются наиболее важными.
Хотелось бы немного подискутировать насчёт флага noImplicitAny. Разработчики TypeScript расходятся во мнении относительно того, должен он быть установлен как true или false. Здесь нет точного ответа, и мы всегда можем изменить флаг позже. Но наш выбор может серьёзно повлиять на крупные проекты, так что он достоин дискуссии.
Когда noImplicitAny установлен в позицию false, компилятор, если он не может вывести тип переменной в зависимости от того, как переменная используется, скрыто устанавливает тип переменной в any. Это и значит «скрытый (implicit) any».
Когда noImplicitAny установлен в позицию true, и компилятор TypeScript не может вывести тип, он всё ещё генерирует файл JavaScript, но также и отчитывается об ошибке.
В этом QuickStart и во многих других примерах этого Developer Guide мы устанавливаем noImplicitAny в позицию false.
Разработчики, которые предпочитают более строгую типизацию, должны устанавливать noImplicitAny в позицию true. Мы всё ещё можем установить тип переменной в позицию any, если это кажется наилучшим выбором, но мы должны сделать это явно после того, как немного поразмыслим над необходимостью этого шага.
Если мы устанавливаем noImplicitAny в true, мы можем также получить скрытые ошибки индексации. Если нам кажется, что это больше раздражает, чем помогает, мы можем выключить их с помощью следующего флага.
QuickStart использует SystemJS для загрузки приложения и библиотечных модулей. Однако не забывайте, что у него есть рабочие альтернативы, такие как высоко оцениваемый сообществом webpack. SystemJS — это неплохой выбор, но мы хотим дать ясное понимание, что это лишь выбор, а не предпочтение.
Все загрузчики модулей требуют конфигурирования, и любое конфигурирование загрузчика становится тем сложнее, чем более разнообразнее становится файловая структура — вплоть до того, что мы начинаем задумываться об объединении файлов для повышения производительности.
Мы рекомендуем вам хорошо разобраться в загрузчике, который вы выберете.
Приняв во внимание эти предостережения, что мы можем сделать?
Узел packages указывает SystemJS, что делать, если он видит запрос на модуль из папки app/.
Наш QuickStart создаёт такие запросы каждый раз, когда в любом TypeScript-файле приложения обнаруживается подобный оператор импорта:
Обратите внимание, что наименование модуля (после from) не содержит расширения файла. Конфигурация packages задаёт SystemJS расширение по-умолчанию как 'js', то есть файл JavaScript.
Это разумно, потому что мы компилируем TypeScript в JavaScript прежде, чем запускать приложение.
Вызов System.import заставляет SystemJS импортировать файл boot (boot.js… после компиляции boot.ts, помните?) boot — это файл, где мы просим Angular запустить приложение. Мы также отлавливаем и логируем ошибки запуска в консоль.
Все прочие модули загружаются с помощью запроса, который создаётся оператором импорта или самим Angular.
Мы импортируем функцию bootstrap из angular2/platform/browser, не из angular2/core. Этому есть причина.
Мы можем назвать «ядром» только те возможности, которые одинаковы для всех целевых платформ. Действительно, многие приложения Angular могут быть запущены только в браузере, и мы будем вызывать функцию bootstrap из этой библиотеки наибольшее количество времени. Вполне «core»-фича — если мы пишем исключительно для браузера.
Но ведь вполне возможно загружать компонент в ином окружении. Мы можем загрузить его на мобильном устройстве с помощью Apache Cordova. Мы можем захотеть отрендерить начальную страницу нашего приложения на сервере для увеличения производительности или содействия SEO-продвижению.
Эти платформы требуют других вариантов bootstrap-функции, которые мы будем загружать из другой библиотеки
Файл boot.ts небольшой. Это всего лишь QuickStart. Мы вполне можем вписать эти несколько строк в файл app.component и избавить себя от излишней сложности.
Но мы так не делаем по причинам, которые, как мы верим, являются весомыми:
Да, это дополнительный шаг и дополнительный файл. Насколько это сложно в целом?
Мы увидим, что отдельный файл boot.ts предпочтительней для большинства приложений — даже если это не столь важно для QuickStart. Давайте вырабатывать хорошие привычки сейчас, пока цена этому невысока.
Мы должны думать об удобстве тестирования с самого начала, даже если мы знаем, что никогда не будем тестировать QuickStart.
Это сложно — тестировать компонент, когда в этом же файле присутствует функция bootstrap. Каждый раз, когда мы загружаем файл компонента для тестирования, функция bootstrap пытается загрузить приложение в браузере. Это вызывает ошибку, потому что мы не ожидали запуска целого приложения — лишь компонента.
Перемещение bootstrap-функции в boot.ts убирает ложную ошибку и оставляет нас с чистым файлом компонента.
Мы рефакторим, переименовываем и перемещаем файлы в течение эволюции нашего приложения. Мы не сможем сделать ничего из этого, пока файл вызывает bootstrap. Мы не можем переместить его. Мы не можем переиспользовать компонент в другом приложении. Мы не можем отрендерить компонент на сервере для увеличения производительности.
Задача компонента — представлять отображение и управлять им.
Запуск приложения не имеет ничего общего с управлением отображением. Это совершенно другая обязанность. Проблемы, с которыми мы столкнулись при тестировании и переиспользовании, исходят именно из этого ненужного смешения обязанностей.
Когда мы писали отдельный файл boot.ts, то получили важный навык для работы с Angular: как экспортировать что-то из одного модуля и импортировать в другой. Мы будем делать много подобного, когда будем более тесно работать с Angular.
Давайте начнём с нуля и построим суперпростое приложение Angular2 на TypeScript.
Демо
Запуск работающего примера — это самый лучший способ увидеть, как оживает приложение на Angular 2.
Нажатие этой ссылки открывает новую вкладку, загружает пример в plunker и отображает простое сообщение:
My First Angular 2 App
Вот файловая структура:
angular2-quickstart
├── app
│ ├── app.component.ts
│ └── boot.ts
├── index.html
└── license.md
Функционально это index.html и два файла TypeScript в папке app/. Давайте сделаем это!
Конечно, мы не будем создавать приложения, которые будут запускаться только в plunker. Давайте сделаем всё как если бы мы делали это в реальной жизни:
- Обустроим нашу среду разработки.
- Напишем корневой компонент Angular для нашего приложения.
- Загрузим его для того, чтобы он принял контроль над главной веб-страницей.
- Напишем главную страницу (index.html).
Мы действительно можем собрать QuickStart за пять минут, если будем следовать инструкциям и проигнорируем все комментарии.
Но многие из нас заинтересуются, «почему» и «как» всё это происходит, и ответ на эти вопросы займёт значительно больше времени.
Среда разработки
Нам необходимо место (папка проекта), несколько библиотек, некоторое количество настроек TypeScript и редактор с поддержкой TypeScript на ваш выбор.
Создадим новую папку нашего проекта
mkdir angular2-quickstart
cd angular2-quickstart
Добавим нужные нам библиотеки
Мы рекомендуем использовать менеджер пакетов npm для загрузки и управления библиотеками.
У вас нет npm? Установите его прямо сейчас, потому что мы будем использовать несколько раз на протяжении этого туториала.
Добавьте файл package.json в папку проекта и скопируйте в него следующий код:
// package.json
{
"name": "angular2-quickstart",
"version": "1.0.0",
"scripts": {
"tsc": "tsc",
"tsc:w": "tsc -w",
"lite": "lite-server",
"start": "concurrent \"npm run tsc:w\" \"npm run lite\" "
},
"license": "ISC",
"dependencies": {
"angular2": "2.0.0-beta.0",
"systemjs": "0.19.6",
"es6-promise": "^3.0.2",
"es6-shim": "^0.33.3",
"reflect-metadata": "0.1.2",
"rxjs": "5.0.0-beta.0",
"zone.js": "0.5.10"
},
"devDependencies": {
"concurrently": "^1.0.0",
"lite-server": "^1.3.1",
"typescript": "^1.7.3"
}
}
Не терпится узнать детали? Мы изложили их в приложении ниже.
Установите пакеты. Откройте окно терминала (командную строку в Windows) и запустите эту команду npm:
npm install
Жуткие красные сообщения об ошибках могут появиться в течение установки. Игнорируйте их. Установка будет успешной. Смотрите приложение ниже, если хотите узнать больше.
Сконфигурируем TypeScript
Нам необходимо довольно специфично настроить компилятор TypeScript.
Добавьте файл tsconfig.json в папку проекта и скопируйте в него следующее:
// tsconfig.json
{
"compilerOptions": {
"target": "ES5",
"module": "system",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
},
"exclude": [
"node_modules"
]
}
Мы исследуем tsconfig.json в приложении ниже.
Итак, всё готово. Давайте напишем немного кода.
Наш первый компонент Angular
Компонент(Component) — это наиболее фундаментальная концепция Angular. Компонент настраивает отображение(view) — часть веб-страницы, где мы показываем информацию пользователю и реагируем на его действия.
Технически, компонент — это класс, который контролирует шаблон отображения. Мы будем писать много таких классов при создании приложений Angular. Это наша первая попытка, так что мы сделаем всё предельно просто.
Создадим подпапку, в которой будут находиться исходники
Мы предпочитаем хранить код нашего приложения в подпапке под названием app/. Выполните следующую команду в консоли:
mkdir app
cd app
Добавим файл компонента
Теперь добавьте файл с именем app.component.ts и скопируйте в него следующий код:
// app/app.component.ts
import {Component} from 'angular2/core';
@Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})
export class AppComponent { }
Давайте подробно рассмотрим этот файл, начиная с самого конца, где мы определяем класс.
Класс Компонента
В самом низу файла располагается пустой и ничем не занятый класс под названием AppComponent. Когда мы будем готовы создать независимое приложение, мы сможем дополнить этот класс свойствами и логикой приложения. Наш класс AppComponent пуст, потому что нам не нужно, чтобы он что-то делал в этом QuickStart.
Модули
Приложения на Angular модульны. Они включают в себя множество файлов, каждый из которых имеет определённую цель.
Большинство файлов приложения экспортируют что-нибудь вроде компонента. Наш файл app.component экспортирует класс AppComponent.
// app/app.component.ts (export)
export class AppComponent { }
Факт экспорта превращает обыкновенный файл в модуль. Имя файла (без его расширения), как правило, является именем модуля. Таким образом, 'app.component' — это имя нашего первого модуля.
Более сложные приложения могут иметь дочерние компоненты, которые наследуют AppComponent в визуальном дереве. Более сложные приложения будут иметь больше файлов и модулей, как минимум столько, сколько у них есть компонентов.
QuickStart не сложен: один компонент — это всё, что нам нужно. Однако модули играют фундаментальную организационную роль даже в таком маленьком приложении.
Модули полагаются на другие модули. В приложениях Angular, написанных на TypeScript, когда нам нужно что-то, предоставляемое иным модулем, мы импортируем его. Когда другому модулю требуется сослаться на AppComponent, он импортирует символ AppComponent следующим образом:
// app/boot.ts
import {AppComponent} from './app.component'
Angular также является коллекцией библиотечных модулей. Каждая библиотека представляет собой модуль, составленный из нескольких связанных общим направлением модулей.
Если нам требуется что-то из Angular, мы импортируем это из его библиотечного модуля. И прямо сейчас нам нужно кое-что, чтобы иметь возможность определить метаданные для нашего компонента.
Метаданные компонента
Класс становится компонентом Angular, когда мы привязываем к нему метаданные. Angular нуждается в метаданных для того, чтобы понять, как нужно конструировать отображение, и как компонент взаимодействует с другими частями приложения.
Мы определяем метаданные компонента с помощью функции Component из Angular. Мы получаем доступ к этой функции, импортируя её из основной библиотеки Angular — angular2/core.
// app/app.component.ts (import)
import {Component} from 'angular2/core';
В TypeScirpt мы можем связать функцию и класс через превращение функции в декоратор. Для этого перед её названием нужно добавить символ @, и поместить её прямо перед объявлением класса.
// app/app.component.ts (metadata)
@Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})
@Component сообщает Angular, что данный класс является компонентом Angular. Объект конфигурации, отправляемый в @Component, имеет два поля: selector и template.
Свойство selector определяет обычный CSS селектор для HTML-элемента my-app, выступающего в качестве хоста. Angular создаёт и отображает экземпляр нашего AppComponent каждый раз, когда он сталкивается с my-app в родительском HTML.
Запомните селектор my-app! Нам будет нужна эта информация, когда мы будем писать наш index.html.
Свойство template содержит шаблон компонента. Шаблон — это разновидность HTML, которая объясняет Angular, как рендерить отображение. Наш шаблон — это единственная строка HTML, объявляющая: «My First Angular App».
Теперь нам нужно как-то объяснить Angular, что этот компонент необходимо загрузить.
Запустим его
Добавьте новый файл, boot.ts, в папку app/, и скопируйте в него следующий код:
// app/boot.ts
import {bootstrap} from 'angular2/platform/browser'
import {AppComponent} from './app.component'
bootstrap(AppComponent);
Нам требуются две вещи для того, чтобы запустить приложение:
- Браузерная функция Angular bootstrap.
- Корневой компонент приложения, который мы только что написали.
Мы импортируем и то, и другое. Потом мы вызываем bootstrap и передаём в неё тип корневого компонента AppComponent.
Изучить, почему мы импортируем bootstrap из angular2/platform/browser и почему мы создаём отдельный файл boot.ts, можно в приложении ниже.
Мы попросили Angular запустить приложение в браузере с нашим компонентом в качестве корневого. Однако, где же Angular его запустит?
Добавим страницу index.html
Angular отображает наше приложение в специфическом месте на нашей странице index.html. Пришло время создать этот файл.
Мы не будем класть index.html в папку app/. Мы расположим его на один уровень выше, в корневой папке проекта.
cd ..
Теперь создайте файл index.html и скопируйте в него следующее:
<!-- index.html -->
<html>
<head>
<title>Angular 2 QuickStart</title>
<!-- 1. Загрузите библиотеки -->
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<!-- 2. Настройте SystemJS -->
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/boot')
.then(null, console.error.bind(console));
</script>
</head>
<!-- 3. Отобразите приложение -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
Здесь есть 3 секции HTML, которые необходимо отметить:
- Мы загружаем библиотеки JavaScript, которые нам требуются. angular2-polyfills.js и Rx.js требуются для работы Angular 2.
- Мы настраиваем нечто, что называется System, и просим его импортировать файл boot, который мы только что написали.
- Мы добавляем тэг <my-app> в <body>. Это то самое место, где обитает наше приложение!
Что-то должно найти и загрузить модули нашего приложения. Мы используем для этого SystemJS. Есть много вариантов, и нельзя сказать, что SystemJS — лучший выбор, но мы его любим, и он работает.
Специфика настройки SystemJS выходит за пределы данного туториала. Мы кратко опишем часть конфигурации в приложении ниже.
Когда Angular вызывает функцию bootstrap в boot.js, он читает метаданные AppComponent, находит селектор my-app, обнаруживает тэг с именем my-app и загружает наше приложение в этот тэг.
Скомпилируем и запустим!
Откройте окно терминала и введите следующую команду
npm start
Эта команда запускает два параллельных процесса node.
- Компилятор TypeScript в режиме наблюдения.
- Статический сервер lite-server, который загружает index.html в браузере и обновляет браузер каждый раз, когда код приложения изменяется.
В течение нескольких мгновений вкладка браузера должна открыться и отобразить:
My First Angular 2 App
Поздравляем! Вы в деле!
Если вы видите вместо этого Loading..., прочитайте приложение «Поддержка ES6 браузерами».
Внесём несколько изменений
Попробуйте изменить сообщение на «My SECOND Angular 2 app».
Компилятор TypeScript и lite-server наблюдают за вашими действиями. Они должны засечь изменение, перекомпилировать TypeScript в JavaScript, обновить вкладку браузера и отобразить обновлённое сообщение.
Это изящный способ разработки приложений!
Мы закрываем окно терминала когда мы всё сделали для того, чтобы прервать одновременно компилятор и сервер.
Финальная структура
Финальная структура нашей папки проекта должна выглядеть так:
angular2-quickstart
├── node_modules
├── app
│ ├── app.component.ts
│ └── boot.ts
├── index.html
├── package.json
└── tsconfig.json
И вот наши файлы:
app/app.component.ts
import {Component} from 'angular2/core';
@Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})
export class AppComponent { }
app/boot.ts
import {bootstrap} from 'angular2/platform/browser'
import {AppComponent} from './app.component'
bootstrap(AppComponent);
index.html
<html>
<head>
<title>Angular 2 QuickStart</title>
<!-- 1. Загрузите библиотеки -->
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<!-- 2. Настройте SystemJS -->
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/boot')
.then(null, console.error.bind(console));
</script>
</head>
<!-- 3. Отобразите приложение -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
package.json
{
"name": "angular2-quickstart",
"version": "1.0.0",
"scripts": {
"tsc": "tsc",
"tsc:w": "tsc -w",
"lite": "lite-server",
"start": "concurrent \"npm run tsc:w\" \"npm run lite\" "
},
"license": "ISC",
"dependencies": {
"angular2": "2.0.0-beta.0",
"systemjs": "0.19.6",
"es6-promise": "^3.0.2",
"es6-shim": "^0.33.3",
"reflect-metadata": "0.1.2",
"rxjs": "5.0.0-beta.0",
"zone.js": "0.5.10"
},
"devDependencies": {
"concurrently": "^1.0.0",
"lite-server": "^1.3.1",
"typescript": "^1.7.3"
}
}
tsconfig.json
{
"compileroptions": {
"target": "es5",
"module": "system",
"moduleresolution": "node",
"sourcemap": true,
"emitdecoratormetadata": true,
"experimentaldecorators": true,
"removecomments": false,
"noimplicitany": false
},
"exclude": [
"node_modules"
]
}
Заключение
Наше первое приложение делает не слишком много. Это, по сути, «Hello World» на Angular 2.
Для первого раза мы сделали всё максимально просто: написали небольшой компонент Angular, добавили немного библиотек JavaScript в index.html и запустили статический файловый сервер. В целом, это всё, что мы ожидали от «Hello World»-приложения.
У нас более серьёзные амбиции
Хорошая новость в том, что вся возня с установкой нас не касается. Возможно, мы чуть-чуть коснёмся package.json для обновления библиотек. Мы откроем index.html только если нам нужно будет добавить библиотеку или файл css-стилей.
Мы готовы к следующему шагу, и теперь наша задача — сконструировать приложение, которое демонстрирует, насколько великолепные вещи можно сделать с помощью Angular 2.
Присоединяйтесь к нам в туториале «Геройский тур»!
Приложения
Остаток данной главы посвящён набору приложений, более подробно излагающих кое-какие моменты, которые мы кратко затронули выше.
Здесь нет никакого критически важного материала. Читайте дальше, если вам интересны детали.
↑ Приложение: Поддержка ES6 браузерами
Angular 2 полагается на некоторые возможности стандарта ES2015, большая часть из которых уже включена в современные браузеры. Однако некоторым браузерам (таким, как IE11), требуются полифиллы (shim) для поддержки этой функциональности. Попробуйте загрузить следующие полифиллы перед другими скриптами в index.html:
<script src="node_modules/es6-shim/es6-shim.js"></script>
↑ Приложение: package.json
npm — это популярный менеджер пакетов для node, и многие Angular-разработчики используют его для загрузки и управления библиотеками, которые необходимы их приложениям.
Мы определили пакеты, которые нам необходимы в файле npm package.json.
Команда Angular предлагает использовать пакеты, указанные в секциях dependencies и devDependencies в этом файле:
// package.json (dependencies)
{
"dependencies": {
"angular2": "2.0.0-beta.0",
"systemjs": "0.19.6",
"es6-promise": "^3.0.2",
"es6-shim": "^0.33.3",
"reflect-metadata": "0.1.2",
"rxjs": "5.0.0-beta.0",
"zone.js": "0.5.10"
},
"devDependencies": {
"concurrently": "^1.0.0",
"lite-server": "^1.3.1",
"typescript": "^1.7.3"
}
}
Вы можете выбрать и другие пакеты. Мы рекомендуем именно этот набор потому, что знаем, что все его компоненты хорошо работают вместе. Подыграйте нам сейчас, а позже экспериментируйте в своё удовольствие, подбирая варианты, которые будут соответствовать вашим опыту и вкусу.
В package.json может присутствовать необязательная секция scripts, в которой мы можем определить полезные команды для выполнения разработки и построения. Мы включаем несколько таких скриптов в предлагаемом нами package.json:
// package.json (scripts)
{
"scripts": {
"tsc": "tsc",
"tsc:w": "tsc -w",
"lite": "lite-server",
"start": "concurrent \"npm run tsc:w\" \"npm run lite\" "
}
}
Мы уже видели, как можно запустить компилятор и сервер одновременно с помощью этой команды:
npm start
Мы исполняем скрипты npm в следующем формате: npm run + название-скрипта. Вот описание того, что делают скрипты:
- npm run tsc — запуск компилятора TypeScript на один проход
- npm run tsc:w — запуск компилятора TypeScript в режиме наблюдения; процесс продолжает работу, перекомпилируя проект в тот момент, когда засекает изменения, внесённые в файлы TypeScript.
- npm run lite — запускает lite-server, легковесный статический файловый сервер, написанный и поддерживаемый Джоном Папа с великолепной поддержкой приложений Angular, которые используют маршрутизацию.
↑ Приложение: Предупреждения и ошибки npm
Всё хорошо, когда любые консольные сообщения, начинающиеся с npm ERR! отсутствуют в конце работы npm install. Могут быть несколько сообщений npm WARN в течение работы команды — и это превосходный результат.
Мы часто наблюдаем сообщение npm WARN после серии gyp ERR! Игнорируйте его. Пакет может попытаться перекомпилировать себя с помощью node-gyp. Если эта попытка заканчивается неудачей, пакет восстанавливается (обычно на предыдущую версию), и всё работает.
Всё хорошо до тех пор, пока нет ни одного сообщения npm ERR! в самом конце npm install.
↑ Приложение: Конфигурация TypeScript
Мы добавили конфигурационный файл (tsconfig.json) в наш проект, чтобы объяснить компилятору, как нужно генерировать файлы JavaScript. Подробнее про tsconfig.json вы можете узнать из официальной TypeScript wiki.
Опции и флаги, которые мы добавили в файл, являются наиболее важными.
Хотелось бы немного подискутировать насчёт флага noImplicitAny. Разработчики TypeScript расходятся во мнении относительно того, должен он быть установлен как true или false. Здесь нет точного ответа, и мы всегда можем изменить флаг позже. Но наш выбор может серьёзно повлиять на крупные проекты, так что он достоин дискуссии.
Когда noImplicitAny установлен в позицию false, компилятор, если он не может вывести тип переменной в зависимости от того, как переменная используется, скрыто устанавливает тип переменной в any. Это и значит «скрытый (implicit) any».
Когда noImplicitAny установлен в позицию true, и компилятор TypeScript не может вывести тип, он всё ещё генерирует файл JavaScript, но также и отчитывается об ошибке.
В этом QuickStart и во многих других примерах этого Developer Guide мы устанавливаем noImplicitAny в позицию false.
Разработчики, которые предпочитают более строгую типизацию, должны устанавливать noImplicitAny в позицию true. Мы всё ещё можем установить тип переменной в позицию any, если это кажется наилучшим выбором, но мы должны сделать это явно после того, как немного поразмыслим над необходимостью этого шага.
Если мы устанавливаем noImplicitAny в true, мы можем также получить скрытые ошибки индексации. Если нам кажется, что это больше раздражает, чем помогает, мы можем выключить их с помощью следующего флага.
"suppressImplicitAnyIndexErrors":true
↑ Приложение: Конфигурация SystemJS
QuickStart использует SystemJS для загрузки приложения и библиотечных модулей. Однако не забывайте, что у него есть рабочие альтернативы, такие как высоко оцениваемый сообществом webpack. SystemJS — это неплохой выбор, но мы хотим дать ясное понимание, что это лишь выбор, а не предпочтение.
Все загрузчики модулей требуют конфигурирования, и любое конфигурирование загрузчика становится тем сложнее, чем более разнообразнее становится файловая структура — вплоть до того, что мы начинаем задумываться об объединении файлов для повышения производительности.
Мы рекомендуем вам хорошо разобраться в загрузчике, который вы выберете.
Узнать больше о конфигурации SystemJS можно здесь.
Приняв во внимание эти предостережения, что мы можем сделать?
<!-- index.html (Конфигурация SystemJS) -->
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/boot')
.then(null, console.error.bind(console));
</script>
Узел packages указывает SystemJS, что делать, если он видит запрос на модуль из папки app/.
Наш QuickStart создаёт такие запросы каждый раз, когда в любом TypeScript-файле приложения обнаруживается подобный оператор импорта:
// boot.ts (часть)
import {AppComponent} from './app.component'
Обратите внимание, что наименование модуля (после from) не содержит расширения файла. Конфигурация packages задаёт SystemJS расширение по-умолчанию как 'js', то есть файл JavaScript.
Это разумно, потому что мы компилируем TypeScript в JavaScript прежде, чем запускать приложение.
В демо-примере на plunker мы компилируем в JavaScript прямо в браузере на лету. Это неплохо для демо, но это не может стать нашим выбором для разработки или релиза.
Мы рекомендуем компилировать в JavaScript в течение фазы построения перед тем, как запускать приложение, по нескольким причинам:
- Мы можем видеть ошибки и предупреждения времени компиляции, которые скрыты от нас в браузере.
- Прекомпиляция упрощает процесс загрузки модулей, да и намного проще найти проблему, когда компиляция является отдельным внешним процессом.
- Прекомпиляция даёт большую производительность, потому что браузеру нет нужды тратить время на компиляцию.
- Наша разработка движется быстрее, потому что мы всего лишь перекомпилируем изменившиеся файлы. Разница станет заметна, когда наше приложение будет содержать множество файлов.
- Прекомпиляция подходит беспрерывному процессу разработки — построению, тестам, деплою.
Вызов System.import заставляет SystemJS импортировать файл boot (boot.js… после компиляции boot.ts, помните?) boot — это файл, где мы просим Angular запустить приложение. Мы также отлавливаем и логируем ошибки запуска в консоль.
Все прочие модули загружаются с помощью запроса, который создаётся оператором импорта или самим Angular.
↑ Приложение: boot.ts
Загрузка платформоспецифична
Мы импортируем функцию bootstrap из angular2/platform/browser, не из angular2/core. Этому есть причина.
Мы можем назвать «ядром» только те возможности, которые одинаковы для всех целевых платформ. Действительно, многие приложения Angular могут быть запущены только в браузере, и мы будем вызывать функцию bootstrap из этой библиотеки наибольшее количество времени. Вполне «core»-фича — если мы пишем исключительно для браузера.
Но ведь вполне возможно загружать компонент в ином окружении. Мы можем загрузить его на мобильном устройстве с помощью Apache Cordova. Мы можем захотеть отрендерить начальную страницу нашего приложения на сервере для увеличения производительности или содействия SEO-продвижению.
Эти платформы требуют других вариантов bootstrap-функции, которые мы будем загружать из другой библиотеки
Зачем создавать отдельный файл boot.ts?
Файл boot.ts небольшой. Это всего лишь QuickStart. Мы вполне можем вписать эти несколько строк в файл app.component и избавить себя от излишней сложности.
Но мы так не делаем по причинам, которые, как мы верим, являются весомыми:
- Сделать всё правильно — это просто.
- Удобство тестирования.
- Удобство переиспользования.
- Разделение обязанностей.
- Изучение импорта и экспорта.
Это просто
Да, это дополнительный шаг и дополнительный файл. Насколько это сложно в целом?
Мы увидим, что отдельный файл boot.ts предпочтительней для большинства приложений — даже если это не столь важно для QuickStart. Давайте вырабатывать хорошие привычки сейчас, пока цена этому невысока.
Удобство тестирования
Мы должны думать об удобстве тестирования с самого начала, даже если мы знаем, что никогда не будем тестировать QuickStart.
Это сложно — тестировать компонент, когда в этом же файле присутствует функция bootstrap. Каждый раз, когда мы загружаем файл компонента для тестирования, функция bootstrap пытается загрузить приложение в браузере. Это вызывает ошибку, потому что мы не ожидали запуска целого приложения — лишь компонента.
Перемещение bootstrap-функции в boot.ts убирает ложную ошибку и оставляет нас с чистым файлом компонента.
Переиспользование
Мы рефакторим, переименовываем и перемещаем файлы в течение эволюции нашего приложения. Мы не сможем сделать ничего из этого, пока файл вызывает bootstrap. Мы не можем переместить его. Мы не можем переиспользовать компонент в другом приложении. Мы не можем отрендерить компонент на сервере для увеличения производительности.
Разделение обязанностей
Задача компонента — представлять отображение и управлять им.
Запуск приложения не имеет ничего общего с управлением отображением. Это совершенно другая обязанность. Проблемы, с которыми мы столкнулись при тестировании и переиспользовании, исходят именно из этого ненужного смешения обязанностей.
Импорт/Экспорт
Когда мы писали отдельный файл boot.ts, то получили важный навык для работы с Angular: как экспортировать что-то из одного модуля и импортировать в другой. Мы будем делать много подобного, когда будем более тесно работать с Angular.