Статьи

Создание JavaScript-приложения следующего поколения с Aurelia

В 2015 году была завершена работа над спецификацией ECMAScript 6, а вместе с тем и уверенность в создании современных превосходных приложений на JavaScript.

В современной среде JavaScript-фреймворков преобладают узнаваемые гиганты AngularJS и React, которые в той или иной мере стремятся каким-либо образом придать форму новым формам ES6 в свои парадигмы.

Однако есть еще один игрок, который, будучи новым и относительно скрытным, выглядит элегантно в использовании современных функций JavaScript. Я бы хотел немного познакомить вас с Аурелией .

Aureli-кто?

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

Aurelia — это естественное развитие Durandal , конкурента AngularJS, созданного Робом Айзенбергом . История Aurelia включает в себя ряд встреч с командой AngularJS на протяжении многих лет. Именно по этой причине многие аспекты фреймворка могут показаться знакомыми разработчикам AngularJS среди вас.

Новые технологии

Как я уже сказал, Aurelia — это фреймворк «следующего поколения», и, как следствие, используемые им инструменты могут быть новыми для некоторых из вас. Он работает на Node.js и использует npm, но использует несколько новых интересных технологий, которые мы кратко рассмотрим ниже:

Глоток

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

ES6 Module Loader Polyfill

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

Загрузчик позволяет нам динамически загружать модули, определенные в синтаксисе модуля ES6, используя метод System.import :

 System.import('mymodule').then(function(m) { ... }); 

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

SystemJS

С легким запутанным названием SystemJS представляет собой набор хуков загрузчика для загрузчика модулей ES6, который позволяет загружать модули из модулей npm, jspm, ES6 и других . Вы можете думать о нем как о многофункциональном загрузчике модулей, построенном на перспективной основе ESF Module Loader Polyfill.

JSPM

jspm — это менеджер пакетов, такой как npm, разработанный для использования с SystemJS. Это позволяет нам устанавливать пакеты из различных источников и предоставляет их нашему приложению, чтобы мы могли легко импортировать их с помощью SystemJS.

Давайте настроим

Я предполагаю, что вы уже установили Node.js, npm и Git, и что вы знакомы с их использованием.

Начнем с клонирования репозитория примеров приложений Aurelia из GitHub.

 git clone https://github.com/aurelia/skeleton-navigation.git 

В этот момент вы можете спросить: «Почему мы клонируем их пример приложения, а не начинаем наше собственное с нуля?»
Причина в том, что Aurelia все еще находится на ранней стадии, поэтому пока не существует простой команды aurelia init которую вы можете запустить, чтобы получить файл package.json и все настройки.

Репозиторий, который мы клонировали, служит хорошей основой для нашего приложения. Это дает нам структуру каталогов, манифест пакета, некоторую конфигурацию тестирования и многое другое. Надеюсь, однажды будет своего рода установщик, или мы отложим генераторы, такие как Yeoman, установку. Поскольку мы используем репозиторий для его конфигурации, а не для самого примера приложения, вы можете удалить каталог src/ , а также файлы styles/styles.css и index.html . Мы создадим нашу собственную в ближайшее время.

Нам нужно установить несколько других вещей, чтобы установить наши зависимости и запустить наше приложение:

Установите gulp глобально, чтобы у нас был доступ к CLI gulp:

 npm install -g gulp 

Затем установите jspm глобально по той же причине.

 npm install -g jspm 

Теперь откройте CLI и перейдите в корневой каталог вашего приложения. После этого запустите команду:

 npm install 

Он установит наши зависимости (из файла package.json ), которые включают, среди прочего:

  • Инструменты Aurelia
  • Плагины Gulp
  • Карма пакеты для тестирования

Как только процесс будет завершен, мы также установим наши пакеты jspm с помощью команды:

 jspm install -y 

Это бит, который фактически устанавливает модули, которые включают Aurelia.

И последнее, но не менее важное: давайте установим Bootstrap с помощью jspm:

 jspm install bootstrap 

Стоит отметить, что библиотека Aurelia (содержащаяся в этих модулях) сама по себе имеет ряд зависимостей, включая SystemJS. Все они будут установлены через управление зависимостями в результате установки самой Aurelia. Я хотел бы подчеркнуть этот момент на тот случай, если вам интересно, каким образом у нас будет доступ к таким вещам, как SystemJS, несмотря на то, что мы не перечислили это явно здесь в наших зависимостях.

Время построить приложение

Теперь у нас есть множество инструментов, которые помогут нам создать наше приложение. Далее нам нужна страница index.html :

 <!doctype html> <html> <head> <link rel="stylesheet" href="jspm_packages/github/twbs/[email protected]/css/bootstrap.min.css"> <link rel="stylesheet" href="styles/styles.css"> </head> <body aurelia-app> <script src="jspm_packages/system.js"></script> <script src="config.js"></script> <script> System.config({ "paths": { "*": "dist/*.js" } }); System.import('aurelia-bootstrapper'); </script> </body> </html> 

Давайте пройдемся по содержимому <body> .

Как я упоминал ранее, SystemJS позволяет нам использовать метод System.import . В этом коде мы используем его для импорта модуля aurelia-bootsrapper который aurelia-bootsrapper наше приложение Aurelia. Мы можем ссылаться на aurelia-bootstrapper по имени благодаря файлу config.js, который jspm создал для нас, когда мы запустили jspm install -y . Он сопоставляет имя модуля с его версионным источником. Довольно изящные вещи.

Бит System.config устанавливает пути для наших модулей, то есть, с чего начать поиск файлов.

Теперь создайте файл styles/style.css и добавьте в него этот код:

 body { padding-top: 74px; } 

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

Что делает aurelia-bootstrapper?

Модуль aurelia-bootstrapper проверит файл index.html на наличие атрибута aurelia-app . Если такой атрибут указывает значение, то загрузчик загрузит представление / модуль с этим именем; в противном случае он загрузит представление и модуль с app.html и app.js (по умолчанию). Представление будет загружено в элемент, имеющий атрибут aurelia-app (в данном случае <body> ). Он будет подключен к файлу app.js

Давайте создадим файл app.js и app.html в каталоге src чтобы увидеть это в действии:

 export class App { constructor() { this.name = "Brad"; } } 
 <template> Hello, my name is <strong>${name}</strong> </template> 

Первое, что вы заметите, — это использование нового синтаксиса модуля ES6 и ключевого слова export . Вы также заметите использование нового синтаксиса класса ES6 и сокращенных сигнатур функций. Aurelia, благодаря SystemJS, поставляется с поддержкой многих интересных функций ES6 прямо из коробки.

Здесь мы видим, что app.js определяет класс, свойства которого предоставляются в качестве переменных для использования в файле app.html . Этот класс известен как модель представления, поскольку это структура данных, которая поддерживает наше представление. Мы печатаем переменные в нашем шаблоне с использованием синтаксиса интерполяции строк ES6.

В качестве последнего замечания я хочу подчеркнуть, что все шаблоны в Aurelia заключены в <template> .

Просмотр нашего приложения в браузере

Чтобы запустить приложение в браузере, все, что нам нужно сделать, это выполнить команду:

 gulp watch 

Это сделает всю магию компиляции ES6, живой перезагрузки и так далее. Вы должны увидеть свое приложение по адресу http://localhost:9000/ . Как мы и ожидали, мы видим содержимое нашего шаблона, отображаемое внутри <bodygt; тег, и мы видим свойство, интерполированное в шаблон.

Наш gulpfile уже настроил BrowserSync для нас, поэтому страница перезагрузится, если вы gulpfile какие-либо изменения.

Время строить наше приложение

В этом разделе мы создадим наивный клиент Reddit, который имеет две страницы: «Funny» и «Gifs». Мы будем получать данные для каждой страницы из API Reddit и отображать список на каждой странице.

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

 import {Router} from "aurelia-router"; export class App { static inject() { return [Router]; } constructor(router) { this.router = router; this.router.configure(config => { config.title = "Reddit"; config.map([ {route: ["", "funny"], moduleId: "funny", nav: true, title: "Funny Subreddit"}, {route: "gifs", moduleId: "gifs", nav: true, title: "Gifs Subreddit"} ]); }); } } 

Итак, что мы здесь сделали?

Первая строка ( import {Router} from "aurelia_router" ) импортирует сам маршрутизатор с использованием синтаксиса импорта модуля ES6.

Затем в классе App у нас есть статическая функция inject . Те из вас, кто знаком с AngularJS и не только уже знают о внедрении зависимостей . Функция inject определит, посредством внедрения зависимостей, какие параметры будут доступны в нашей функции конструктора. В этом случае будет предоставлен один параметр, и это наш маршрутизатор. Вы можете видеть, что мы изменили функцию конструктора, чтобы принять этот новый параметр.

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

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

Прежде всего, мы устанавливаем маршрутизатор как свойство самого класса с помощью this.router = router; , Это соглашение Aurelia и необходимо для работы. Обратите внимание, что наименование важно в этом случае.

Во-вторых, мы настраиваем наши маршруты с помощью объекта config предоставленного нам в функции обратного вызова this.router.configure . Мы устанавливаем свойство title которое будет использоваться для задания заголовка наших страниц. Мы также передаем список определений маршрутов в функцию config.map .

Каждое определение маршрута имеет следующий шаблон:

 { route: ["", "foo"], // Activate this route by default or when on /foo moduleId: "foo", // When active, load foo.js and foo.html (module) nav: true, // Add this route to the list of navigable routes (used for building UI) title: "Foo" // Used in the creation of a pages title } 

Итак, в нашем случае у нас есть две страницы, которые мы можем посетить на /#/funny и /#/gifs , с /#/funny выступающей в качестве нашей страницы по умолчанию благодаря списку ["", "funny"] из двух схемы маршрутов.

Нам также потребуется обновить app.html чтобы он работал как файл макета нашего приложения.

 <template> <a href="/#/funny">Funny</a> <a href="/#/gifs">Gifs</a> <router-view> </router-view> </template> 

Вы видите пользовательский элемент <router-view></router-view> ? Это еще одна встроенная часть функций Аурелии. Вы можете думать об этом как о директиве AngularJS или просто как о веб-компоненте. Представление, связанное с текущим маршрутом, будет автоматически загружено в этот элемент.

Далее нам нужно определить два модуля: funny и gifs .

Написание наших страниц модулей

Модуль «Забавный»

Мы начнем с funny а затем скопируем его в качестве основы для gifs .

Создайте файл /src/funny.js со следующим содержимым:

 import {HttpClient} from 'aurelia-http-client'; export class Funny { // Dependency inject the HttpClient static inject() { return [HttpClient]; } constructor(http) { this.http = http; // Assign the http client for use later this.posts = []; this.subreddit_url = "http://reddit.com/r/funny.json"; } loadPosts() { // Aurelia's http client provides us with a jsonp method for // getting around CORS issues. The second param is the callback // name which reddit requires to be "jsonp" return this.http.jsonp(this.subreddit_url, "jsonp").then(r => { // Assign the list of posts from the json response from reddit this.posts = r.response.data.children; }); } // This is called once when the route activates activate() { return this.loadPosts(); } } 

Также создайте /src/funny.html следующим образом:

 <template> <ul class="list-group"> <li class="list-group-item" repeat.for="p of posts"> <img src.bind="p.data.thumbnail" /> <a href="http://reddit.com${p.data.permalink}"> ${p.data.title} </a> </li> </ul> </template> 

Модуль «Gifs»

Давайте просто скопируем наши funny.js и funny.html в src/gifs.js и src/gifs.html соответственно. Нам нужно немного подправить содержимое gifs.js

 import {HttpClient} from 'aurelia-http-client'; export class Gifs { static inject() { return [HttpClient]; } constructor(http) { this.http = http; this.posts = []; this.subreddit_url = "http://reddit.com/r/gifs.json"; } loadPosts() { return this.http.jsonp(this.subreddit_url, "jsonp").then(r => { this.posts = r.response.data.children; }); } activate() { return this.loadPosts(); } } 

Теперь вы сможете посетить localhost:9000/#/gifs чтобы увидеть список gif-постов и их ссылок.

Улучшения нашего макета

Мы можем внести несколько улучшений в наш шаблон макета, используя маршрутизатор Aurelia.

Помните свойство nav:true мы установили ранее в нашей конфигурации маршрута? Что он делает, так это добавляет маршрут в список, который мы можем перебрать в нашем представлении для построения динамической навигации. Давайте сделаем это сейчас.

Обновите содержимое app.html следующим образом:

 <template> <div class="container"> <ul class="nav navbar-nav navbar-fixed-top navbar-inverse"> <li repeat.for="navItem of router.navigation" class="${navItem.isActive ? 'active' : ''}"> <a href.bind="navItem.href"> ${navItem.title} </a> </li> </ul> <router-view></router-view> </div> </template> 

Вывод

Ну, вот и все! Ваше первое приложение Aurelia. Я очень взволнован будущим Аурелии, так как думаю, что он чистый и понятный. Более того, с помощью ES6 он сохраняет все в многократно расширяемых модулях. В будущих уроках я расскажу, как мы можем абстрагировать дублирование между модулями Gifs и Funny, а также некоторые другие улучшения и дополнения к нашему клиенту Reddit. Я хотел бы знать, как проходит ваша первая попытка разработки приложений с Aurelia!

Полное приложение, которое мы создали в этой статье, можно найти здесь.