2015 год принес завершение спецификации ECMAScript 6 и вместе с тем уверенность в создании современных, улучшенных приложений в JavaScript.
JavaScript фреймворки подавлены такими гигантами, как AngularJS и React, оба из которых стремятся каким-то образом, или иной формой, включить новые характеристики ES6 есть в свои парадигмы.
Существует, однако, еще один игрок, новый и относительно скрытный, и выглядит элегантно в своем использовании современных возможностей JavaScript. Я хотел бы представить вам Aurelia.
Aureli-кто?
Aurelia является фреймворком следующего поколения, который использует современные концепты, как ES6, веб-компоненты, и модульность, чтобы помочь вам развить мощные, перспективные приложения.
Aurelia является – естественная прогрессия Durandal, конкурента AngularJS, созданная Робом Айзенбергом. История Aurelia включает в себя ряд схваток с командой AngularJS на протяжении многих лет. Именно по этой причине многие аспекты фреймворка могут быть знакомы знакомы разработчикам AngularJS среди вас.
Новые Технологии
Как я уже сказал, Aurelia – фреймворк “нового поколения” и, как следствие, инструменты, которые он использует, могут быть новыми для некоторых из вас. Он работает на Node.js, и использует npm, но он опирается на новые интересные технологии, которые мы кратко рассмотрим:
Gulp
Это не новинка, но это является основной частью установки Aurelia. Мы используем Gulp для проведения всех наших файлов через различные задачи, чтобы обеспечить приложению полную готовность к работе.
ES6 Module Loader Polyfill
ES6 Module Loader – это полизаполнение для загрузчика динамического модуля System, который был частью оригинальной спецификации ES6. Загрузчик System погрузчик в процессе записи в спецификации браузера, но в то же время это полизаполнение обеспечивает перспективное решение, которое мы можем использовать сегодня.
Загрузчик позволяет динамически загружать модули, определенные в модуле синтаксиса ES6 с использованием метода System.import:
System.import('mymodule').then(function(m) { ... });
В дополнение к загрузке модулей ES6, загрузчик позволяет загружать другие синтаксисы модуля за счет использования хуков.
SystemJS
SystemJS по существу является коллекцией хуков загрузчика для загрузчика модуля ES6, которые позволяют нам загружать модули из npm, jspm, ES6 модулей и многое другое. Вы можете считать его многофункциональным загрузчиком модуля, построенном на перспективном ES6 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 глобально, чтобы иметь доступ к gulp CLI:
npm install -g gulp
Затем установите jspm глобально по той же причине.
npm install -g jspm
Теперь откройте CLI и перейдите к основному каталогу вашего приложения. После этого, выполните команду:
npm install
Это установит наши зависимости (из файла package.json), которые среди прочего содержат:
- инструменты Aurelia
- плагины Gulp
- пакеты Karma для тестирования
После того, как процесс будет завершен, мы установим наши 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/bootstrap@3.3.4/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. Мы можем найти 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
Вы увидите приложение по адресу: //localhost:9000/. Как мы и ожидали, мы видим содержимое нашего шаблона внутри тега <bodygt;, и мы видим свойство, интерполированное в шаблон.
Наш gulpfile уже установил BrowserSync для нас, поэтому страница перезагрузиться, если вы сделаете какие-либо изменения.
Создаем наше приложение
В этом разделе, мы создадим клиент 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>? Это еще один встроенная особенность Aurelia. Вы можете воспринимать его как директиву AngularJS или просто веб-компонент. Просмотр, связанный с текущим рутом автоматически загрузится в этот элемент.
Далее, мы должны определить два модуля: funny и gifs.
Написание модулей страницы
Модуль “Funny”
Мы начнем с 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 = "//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="//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 = "//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. Я представляю будущее Aurelia прямым и ясным. Кроме того, с помощью ES6 модули будут расширяемыми и многоразовыми.
Высоких конверсий!