Облачные веб приложения набирают популярность. Множество ИТ компаний видят эту тенденцию и все больше программных продуктов разрабатывают на основе удаленного доступа. Существует много аналогов desktop программ, которые предлагают онлайн версию за небольшую ежемесячную плату.
Они дают больше гибкости и мобильности. Например, вы легко можете вносить данные в облачные CRM или ERP системы через свой мобильный телефон и это может происходить в удобном для Вас месте. Как видно с графика Statista , рынок облачных решений растет и к 2026 году должен достигнуть почти 522 миллиардам долларов.
Чтобы обеспечить стабильную работу сложных веб приложений, желательно использовать технологии которые дадут наилучшую производительность и скорость. Существует два способа разработки веб приложений: одностраничные приложения (SPA) и многостраничные приложения(MPA). Давайте рассмотрим какая между ними разница и какие преимущества имеет каждый тип веб приложений.
Одностраничные приложения позволяют имитировать работу десктоп приложений. Архитектура устроена таким образом, что при переходе на новую страницу, обновляется только часть контента. Таким образом, нет необходимости повторно загружать одни и те же элементы. Это очень удобно для разработчиков и пользователей. Для разработки SPA используется один из самых популярных языков программирования - javascript . Небольшое веб приложение можно сделать с библиотекой jQuery.
Но, сразу стоит отметить, что jQuery очень плохо подходит для разработки крупных проектов. Наша компания, Merehead, рекомендует использовать более мощные технологии для разработки SPA. Для этих целей хорошо подойдет React.js, Angular.js, Vue.js и другие фреймворки/библиотеки. Их архитектура позволяет разрабатывать гибкие веб приложения. Более того на базе фреймоврков можно строить мобильные приложения с повторным использованием когда. Такие возможности дает Rreact Native и Ionic. Основные преимущества Single Page Application:
При всех своих достоинствах, Single Page Application имеет некоторые недостатки, которые сдерживают рост популярности:
JavaScript имеет низкий уровень безопасности, но если использовать современные фреймворки, они могу сделать ваше веб приложение безопасным. Но стоит обратить внимание, что использование jQuery может существенно понизить безопасность вашего проекта.
Одностраничные веб приложения хорошо подходят для разработки динамических платформ, с небольшим объемом данных. Кроме того, если Вам потребуется в будущем построить мобильное приложение , SPA отлично подойдет как основа. Основным недостатком, который сдерживает стремительный рост популярности SPA это плохая SEO оптимизация. Проекты, где SEO имеет важнейший приоритет, стоит использовать MPA.
Многостраничные приложения имеют более классическую архитектуру. Каждая страница отправляет запрос на сервер и полностью обновляет все данные. Даже если эти данные небольшие. Таким образом тратится производительность на отображение одних и тех же элементов. Соответственно это влияет на скорость и производительность. Многие разработчики, для того чтобы повысить скорость и уменьшить нагрузку, используют JavaScript/jQuery. Хороший пример, обновление товаров без перезагрузки страницы, при использования фильтров в интернет магазине. Это намного удобней и главное быстрее. Главные преимущества Multi Page Application (MPA):
Используя MPA вы можете найти подходящее коробочное решение. Например использовать Magento, OpenCart для разработки e-commerce веб приложения или Dolphin, Elgg для разработки социальных сетей . Недостатки MPA:
Основным преимуществом МПА является хорошая SEO оптимизация и огромные количество коробочных решений.
Термин «одностраничное приложение» (или SPA) обычно используется для описания приложений, которые были созданы для Интернета. Эти приложения доступны через веб-браузер, как и другие веб-сайты, но предлагают более динамичные взаимодействия, напоминающие родные мобильные и настольные приложения.
Самая заметная разница между обычным сайтом и SPA – это уменьшение количества обновлений страницы. У СПА более тяжелое использование AJAX – способ общения с серверными серверами без полного обновления страницы – для загрузки данных в наше приложение. В результате процесс рендеринга (построения) страниц происходит в основном на стороне клиента с помощью JavaScript.
SPA, Single Page Application, Одностраничное приложениеВ то время как строительство SPA-приложений является модным и считается современной практикой развития, важно знать о его недостатках, в том числе:
- Браузер делает большую часть тяжелого подъема, что означает, что производительность может быть проблемой – особенно на менее способных мобильных устройствах.
- Сложность поисковой оптимизации (SEO), сложность предоставления вашего контента поисковым системам и сайтам социальных сетей, которые предоставляют предварительный просмотр ссылок.
Большинство современных фреймворков JavaScript работают над тем, как обрабатывать серверный рендеринг SPA, т.е. пользователь получит полностью заполненную страницу, когда SPA будет загружен впервые, вместо того, чтобы, например, увидеть индикатор загрузки.
Рендеринг на стороне сервера может облегчить некоторые из блогов, которые браузеры должны выполнять при рендеринге страниц, а также поможет решить проблему SEO и доступности контента.
Популярные JavaScript-фреймворки и библиотеки для создания SPAЧем больше интерактивности происходит на стороне клиента, тем больше кода JavaScript требуется для того, чтобы эти интерактивные элементы функционировали хорошо. И чем больше написано кода, тем важнее иметь чистую и хорошо продуманную кодовую базу. И это именно та проблема, которую решают фреймворки JavaScript, – каждый с собственным подходом.
Существует множество открытых исходных JavaScript-фреймворков, которые помогают создавать SPA-приложения, такие как.
- Tutorial
Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js . Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.
Мы напишем клиент-серверное приложение простейшего блога. Приложение будет отображать список записей а также полный текст каждой отдельной записи. И само собой, всё это будет происходить без перезагрузки страницы.
Ознакомившись с примером этого приложения, вы научитесь извлекать данные в Vue, создавать роуты и разберётесь с интересной особенностью Vue - однофайловыми компонентами.
Бэкенд В этом руководстве мы в основном сосредоточимся на фронтенде на Vue . Размышлять о написании REST бэкенда мы не будем. Для примера будет использоваться сервис jsonplaceholder.typicode.com предостовляющий заглушку в виде REST API.ФронтендИнструменты Начать работу с Vue очень просто. С использованием правильных инструментов это ещё проще. Рекомендую взглянуть на проект vue-awesome , который содержит список инструментов, компонентов, библиотек и плагинов на все случаи жизни.Vue-cli При создании нового проекта рекомендуется воспользоваться Vue-cli. Так можно создавать проекты с использованием официальных шаблонных проектов Vue, или одного из множества шаблонных проектов с открытым исходным кодом, и, конечно же, вы можете создать свой собственный и использовать его в любом месте.Итак, для начала установим vue-cli в качестве глобального пакета:
$ npm install -g vue-cli
Затем проинициализируем проект с выбранным шаблоном; для нашего примера более чем достаточно использовать webpack-simple.
$ vue init webpack-simple vue-spa
Далее перейдём в папку vue-spa и запустим npm install
в терминале. После установки всех пакетов, мы можем запустить наше приложение в режиме разработки.
$ npm run dev
Эта команда автоматически запустит наш проект на локальном dev-сервере webpack. В браузере появится наше простейшее приложение Vue. Конечно оно выглядит совсем не так, как бы нам хотелось, и годится лишь в качестве отправной точки для начала чего-то большего. Чтобы продолжить работу, предлагаю сначала ознакомиться со структурой нашего шаблона.
Внутри шаблон webpack-simple имеет следующую структуру:
Файл index.html содержит простую разметку HTML с единственным элементом “app” в body. Он будет заменён на DOM, сгенерированный vue. По этой причине тэг body не рекомендуется использовать в качестве корневого элемента.
В папке src лежит файл main.js, который содержит точку входа webpack. Компоненты Vue импортируются там же. Ещё там описан корневой экземпляр Vue, который пока что имеет два свойства. Свойство ‘el’ обеспечивает экземпляру Vue связь с указанным DOM элементом. Ещё одно - функция отрисовки, генерирующая DOM из App.vue . В общем, это все, что нам нужно знать о структуре шаблона webpack-simple, не так много, не так ли? Основная часть нашего приложения будет запрограммирована в App.vue. Расширение.vue определяет файл как однофайловый компонент vue. Это одна из особенностей Vue с которой мы сейчас познакомимся поближе.
Каждый файл *.vue состоит из блоков трёх типов: , и опционально . В результате, мы можем разделить проект на связанные компоненты. Внутри компонента его шаблон, логика и стили неотъемлемо связаны, и их совмещение фактически делает компонент более целостным и легко поддерживаемым. Теперь мы готовы приступить к созданию блога на Vue.
Давайте посмотрим, что собственно мы собираемся реализовать. У нас будет заголовок с названием нашего блога в верхней части страницы. С левой стороны у нас будет фиксированная боковая панель, в которой мы будем отображать заголовки наших записей, это будет что-то вроде оглавления. Остальная часть страницы будет занята динамическим блоком, в котором будет отображаться сам текст записи.
Шаг 1 Прежде всего, удалим все лишние строки из App.vue. И перепишем шаблон в соответствии с нашими требованиями.
Vue.js SPA
Во-вторых, создадим экземпляр Vue со свойством data, которое мы разместим в массиве с нашими сообщениями. На данный момент он пуст, но вскоре мы поместим в него данные, полученные с сервера внутрь массива.
После первого обращения, вы больше не сможете добавлять реактивные свойства к корневому объекту данных. Поэтому, прежде чем создавать экземпляр Vue, рекомендуется объявить все реактивные свойства на уровне корня.
export default {
data () {
return {
posts:
}
}
}
Кроме того, можно добавить немного стилей, чтобы приложение выглядело лучше.
Код приложения хостится на github.com . Достаточно клонировать репозиторий и переключать ветку по номеру шага чтобы проследить разработку приложения шаг за шагом например:
$ git checkout step-1
В настоящий момент нам абсолютно нечего отображать в нашей навигационной панели, поэтому давайте получим данные с сервера. Для этого я выбрал Axios - простой в использовании HTTP-клиент. Вы также можете использовать любой удобный для вас способ, например, Vue-ресурс или собственную выборку или даже jQuery Ajax.
$ npm install --save-dev axios
Затем импортируем его в компонент App и определим метод getAllPosts() который будет делать запрос к серверу и присваивать его свойству posts. Вызываем метод в хуке created(), который будет вызываться после создания экземпляра Vue и после установки настроек обращения к данным.
Import axios from "axios"
export default {
data () {
return {
posts: null,
endpoint: "https://jsonplaceholder.typicode.com/posts/",
}
},
created() {
this.getAllPosts();
},
methods: {
getAllPosts() {
axios.get(this.endpoint)
.then(response => {
this.posts = response.data;
})
.catch(error => {
console.log("-----error-------");
console.log(error);
})
}
}
}
А теперь отобразим все заголовки записей в боковой панели.
{{ post.title }}
До сих пор мы отображали только заголовки записей, но пока мы ещё не можем видеть сами записи. Теперь необходимо отобразить полный пост в разделе контента в соответствии с выбранным названием на боковой панели. В то же время хотелось бы, чтобы каждая запись была доступна по своему уникальному адресу.
Установим библиотеку:
$ npm install --save-dev vue-router
Для настройки роутинга вернёмся к файлу main.js. Здесь мы определим настройки роутинга и добавим их в наш экземпляр Vue.
Import Vue from "vue"
import Router from "vue-router"
import App from "./App.vue"
import Post from "./components/Post.vue"
import Hello from "./components/Hello.vue"
Vue.use(Router)
const router = new Router({
routes: [
{
path: "/",
name:"home",
component: Hello,
},
{
path: "/post/:id",
name:"post",
component: Post,
props: true,
},
]
})
new Vue({
el: "#app",
render: h => h(App),
router
})
В настройках роутинга мы указали, какой компонент вызывает отрисовку по соответствующему пути. Поскольку только компонент Post.vue будет отвечать за отрисовку каждого поста, нам не потребуется определять путь к каждому посту, достаточно определить динамический путь.
Path: "/post/:id"
Этот путь содержит динамический сегмент:id который указывает на конкретный пост. При этом у нас есть доступ к этому сегменту в компоненте Post через this.$route.params.id
. Однако использование $route в нашем компоненте закрепит жесткую связь с роутом, что в свою очередь ограничивает гибкость компонента, поскольку он может использоваться только на определенных URL-адресах. Вместо этого мы можем использовать опцию props
и установить её в true
. После этого $route.params станет связан с опцией props компонента Post.
Теперь, когда мы создали роутер, мы можем вернуться к нашему приложению и добавить еще несколько строк в шаблон.
{{post.id}}. {{post.title}}
Здесь мы имеем два компонента vue-router
: и . Первый - это компонент для включения навигации пользователя в приложении с поддержкой роутинга. Второй компонент - это функциональный компонент, который отрисовывает согласованный компонент для данного пути.
Остался заключительный шаг. Нам нужно отобразить содержимое записи поста.
Шаг 4 Перейдём к файлу Post.vue, в котором добавим простой шаблон:{{ post.title }}
{{ post.body }}
{{ post.id }}
Затем нам нужно установить параметры экземпляра Vue для этого компонента. Здесь все также как в настройках отображения всех постов. Объявим опцию props с меняющимся id , которая будет получать номер нашего поста. Далее, объявим объект данных, как уже делали в App.vue:
Import axios from "axios";
export default {
props: ["id"],
data() {
return {
post: null,
endpoint: "https://jsonplaceholder.typicode.com/posts/",
}
}
}
Затем опишем метод getPost()
, который будет получать только одну запись поста по идентификатору и вызовем его в хуке created()
.
Methods: {
getPost(id) {
axios(this.endpoint + id)
.then(response => {
this.post = response.data
})
.catch(error => {
console.log(error)
})
}
},
created() {
this.getPost(this.id);
},
Почти готово. Если мы запустим приложение сейчас, мы увидим, что, хотя URL-адрес меняется, мы видим единственный пост, который был отрисован первым. Дело в том, что для отрисовки разных постов у нас есть один и тот же компонент, и Vue не нужно его пересоздавать из-за лишней траты ресурсов, а это также означает, что хуки жизненного цикла компонента не будут вызваны.
Чтобы это исправить, нам просто нужно установить watcher для объекта $route
.
Watch: {
"$route"() {
this.getPost(this.id);
}
}
Теперь всё работает так, как и должно. Чтобы получить версию для продакшина достаточно выполнить команду npm run build
в консоли.
Продукты и технологии:
Single-Page Applications (SPA), ASP.NET Web API, Knockout.js, Ember.js, AJAX и HTML5
В статье рассматриваются:
- создание уровня сервисов и веб-клиента AJAX для приложения-примера;
- шаблоны MVC и MVVM;
- связывание с данными;
- создание веб-клиента с применением Knockout.js;
- создание веб-клиента с применением Ember.js.
Одностраничные приложения (Single-Page Applications, SPA) - это веб-приложения, которые загружают одну HTML-страницу и динамически обновляют ее при взаимодействии с пользователем.
SPA используют AJAX и HTML5 для создания гибких и адаптивных веб-приложений без постоянных перезагрузок страницы. Однако это означает, что большая часть работы возлагается на клиентскую сторону, а именно на JavaScript-код. Разработчику для традиционной ASP.NET может быть трудно совершить такой кульбит. К счастью, существует множество JavaScript-инфраструктур с открытым исходным кодом, которые облегчают создание SPA.
В этой статье я пошагово пройду процесс создания простого SPA-приложения. Попутно вы ознакомитесь с некоторыми фундаментальными концепциями создания SPA, в том числе с шаблонами Model-View-Controller (MVC) и Model-View-ViewModel (MVVM), связыванием с данными и маршрутизацией (routing).
О приложении-примереЯ создал приложение-пример для операций с простой базой данных по фильмам (рис. 1 ). В крайнем слева столбце страницы отображается список жанров. Выбор жанра приводит к появлению списка соответствующих фильмов. Кнопка Edit рядом с записью позволяет изменять эту запись. После редактирования можно щелкнуть кнопку Save для передачи обновления на сервер или кнопку Cancel для отмены изменений.
Рис. 1. SPA-приложение для базы данных по фильмам
Я создал две версии этого приложения: одна из них использует библиотеку Knockout.js, а другая - библиотеку Ember.js. Эти две библиотеки основаны на разных подходах, поэтому будет весьма поучительно сравнить их. В обоих случаях клиентское приложение не требовало более 150 строк JavaScript-кода. На серверной стороне я задействовал ASP.NET Web API, чтобы обслуживать JSON для клиента. Исходный код обеих версий вы найдете на github.com/MikeWasson/MoviesSPA .
(Примечание Я создавал приложение, используя RC-версию Visual Studio 2013. В RTM-версии некоторые вещи могли измениться, но они не должны повлиять на код.)
ОбзорВ традиционном веб-приложении при каждом вызове сервера тот осуществляет рендеринг новой HTML-страницы. Это вызывает обновление страницы в браузере. Если вы когда-нибудь писали приложение Web Forms или PHP, этот жизненный цикл страниц должен быть знаком вам.
В SPA после загрузки первой страницы все взаимодействие с сервером происходит через AJAX-вызовы. Эти AJAX-вызовы возвращают данные (не разметку) - обычно в формате JSON. Приложение использует JSON-данные для динамического обновления страницы без ее перезагрузки. Рис. 2 иллюстрирует разницу между этими двумя подходами.
Рис. 2. Сравнение традиционного жизненного цикла страницы с жизненным циклом в SPA
Одно из преимуществ SPA очевидно: приложения более гибкие и адаптивные, свободные от рваного эффекта перезагрузки страницы и ее рендеринга заново. Другое преимущество может оказаться менее очевидным и касается того, как вы проектируете архитектуру веб-приложения. Отправка данных приложения как JSON обеспечивает разделение между презентационной частью (HTML-разметкой) и прикладной логикой (AJAX-запросы плюс JSON-ответы).
Это разделение упрощает проектирование и развитие каждого уровня. В SPA-приложении с тщательно продуманной архитектурой можно изменять HTML-разметку, не касаясь кода, который реализует прикладную логику (по крайней мере, в идеале). Вы увидите это на практике, когда мы будем обсуждать связывание с данными.
В чистом SPA все UI-взаимодействие происходит на клиентской стороне через JavaScript и CSS. После начальной загрузки страницы сервер действует исключительно как уровень сервисов. Клиенту нужно просто знать, какие HTTP-запросы он должен посылать. Ему не важно, как сервер реализует свою часть.
При такой архитектуре клиент и сервис независимы. Вы могли бы полностью заменить серверную часть, которая выполняет сервис, и, пока вы не изменяете API, вы никак не нарушите работу клиента. Верно и обратное: вы можете заменить все клиентское приложение, не изменяя уровень сервисов. Например, вы могли бы написать родной мобильный клиент, который использует этот сервис.
Создание проекта в Visual StudioВ Visual Studio 2013 есть один тип проекта ASP.NET Web Application. Мастер этого проекта позволяет выбрать ASP.NET-компоненты, которые будут включены в проект. Я начал с шаблона Empty, а затем добавил в проект ASP.NET Web API, установив флажок Web API в разделе Add folders and core references for, как показано на рис. 3 .
Рис. 3. Создание нового ASP.NET-проекта в Visual Studio 2013
В новом проекте есть все библиотеки, необходимые для Web API, а также кое-какой конфигурационный код Web API. Я не вводил никаких зависимостей от Web Forms или ASP.NET MVC.
Обратите внимание на рис. 3 , что Visual Studio 2013 включает шаблон Single Page Application. Этот шаблон устанавливает скелет SPA-приложения, основанный на Knockout.js. Он поддерживает вход с применением базы данных с информацией о членстве в группах или с помощью внешнего провайдера аутентификации. Я не стал использовать этот шаблон в своем приложении, потому что хотел показать более простой пример с нуля. Шаблон SPA - отличный ресурс, особенно если вам нужно добавить аутентификацию в приложение.
Создание уровня сервисовЯ использовал ASP.NET Web API, чтобы создать простой REST API для приложения. Не буду здесь вдаваться в детали Web API - подробности вы можете прочитать по ссылке asp.net/web-api.
Сначала я создал класс Movie, представляющий фильм. Этот класс делает две вещи:
- сообщает Entity Framework (EF), как создавать таблицы базы данных для хранения информации о фильмах;
- сообщает Web API, как форматировать полезные данные JSON.
Вы не обязаны использовать одну модель для обеих целей. Например, вам может понадобиться, чтобы схема базы данных отличалась от полезных данных JSON. В этом приложении я ничего не усложняю:
Затем я воспользовался технологией scaffolding в Visual Studio для создания контроллера Web API, который задействует EF в качестве уровня данных. Чтобы применить эту технологию, щелкните правой кнопкой мыши папку Controllers в Solution Explorer и выберите Add / New Scaffolded Item. В мастере Add Scaffold укажите Web API 2 Controller with actions, using Entity Framework, как показано на рис. 4 .
Рис. 4. Добавление контроллера Web API
На рис. 5 приведен мастер Add Controller. Я присвоил контроллеру имя MoviesController. Имя имеет значение, так как URI для REST API основываются на имени контроллера. Я также установил флажок Use async controller actions, чтобы задействовать преимущества новой функциональности async в EF 6. Я выбрал класс Movie в качестве модели и указал New data context, чтобы создать новый контекст данных EF.
Рис. 5. Мастер Add Controller
Мастер добавляет два файла:
- MoviesController.cs - определяет контроллер Web API, который реализует REST API для приложения;
- MovieSPAContext.cs - это в основном склеивающий слой EF, который предоставляет методы для запроса нижележащей базы данных.
В табл. 1 показан REST API по умолчанию, создаваемый технологией scaffolding.
Табл. 1. REST API по умолчанию, созданный технологией scaffolding из Web API
Значения в фигурных скобках являются заменителями для подстановки. Например, чтобы получить фильм с идентификатором, равным 5, URI должен выглядеть так: /api/movies/5.
Я расширил этот API, добавив метод, который находит все фильмы указанного жанра:
Клиент указывает жанр в строке запроса URI. Например, чтобы получить все фильмы жанра Drama, клиент посылает GET-запрос на /api/movies?genre=drama. Web API автоматически связывает параметр запроса с параметром genre в методе GetMoviesByGenre.
Создание веб-клиентаДо сих пор я просто создавал REST API. Если вы отправите GET-запрос на /api/movies?genre=drama, исходный HTTP-ответ будет выглядеть так:
HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Type: application/json; charset=utf-8 Date: Tue, 10 Sep 2013 15:20:59 GMT Content-Length: 240 [{"ID":5,"Title":"Forgotten Doors","Year":2009,"Genre":"Drama","Rating":"R"}, {"ID":6,"Title":"Blue Moon June","Year":1998,"Genre":"Drama","Rating":"PG-13"},{"ID":7,"Title":"The Edge of the Sun","Year":1977,"Genre":"Drama","Rating":"PG-13"}]
Теперь мне нужно написать клиентское приложение, которое делает с этим что-то осмысленное. Базовый рабочий процесс такой:
- UI инициирует AJAX-запрос;
- обновляем HTML для отображения полезных данных ответа;
- обрабатываем AJAX-ошибки.
Вы могли закодировать все это вручную. Например, вот некоторый jQuery-код, который создает список названий фильмов:
В этом коде есть кое-какие проблемы. Он смешивает прикладную логику с презентационной и тесно связан с вашим HTML. Кроме того, писать его весьма утомительно. Вместо того чтобы сосредоточиться на приложении, вы тратите время на написание обработчиков событий и кода, манипулирующего DOM.
Решение заключается в том, чтобы использовать JavaScript-инфраструктуру. К счастью, их выбор довольно велик, и эти инфраструктуры имеют открытый исходный код. К некоторым из более популярных инфраструктур относятся Backbone, Angular, Ember, Knockout, Dojo и JavaScriptMVC. Большинство использует вариации шаблонов MVC или MVVM, поэтому будет полезно вкратце рассмотреть эти шаблоны.
Шаблоны MVC и MVVMКорни шаблона MVC уходят в 80-е годы прошлого века и связаны с ранними графическими UI. Цель MVC - разбиение кода на три уровня со своими обязанностями (рис. 6 ). Вот что они делают:
- модель представляет данные и бизнес-логику предметной области;
- представление отображает модель;
- контроллер принимает пользовательский ввод и обновляет модель.
Рис. 6. Шаблон MVC
Более современная вариация MVC - шаблон MVVM (рис. 7 ). В шаблоне MVVM:
- модель по-прежнему представляет данные предметной области;
- модель представления - это абстрактное отражение представления;
- представление отображает модель представления и посылает пользовательский ввод модели представления.
Рис. 7. Шаблон MVVM
View Model | View Model |
В JavaScript-инфраструктуре MVVM представлением является разметка, а моделью представления - код.
MVC имеет много вариаций, а литература по MVC зачастую противоречива и запутана. По-видимому, это и не удивительно для проектировочного шаблона, который начал свою жизнь со Smalltalk-76 и все еще применяется в современных веб-приложениях. Поэтому, хоть и хорошо знать теорию, главное - понимать конкретную инфраструктуру MVC, используемую вами.
Создание веб-клиента с применением Knockout.jsДля первой версии своего приложения я использовал библиотеку Knockout.js. Knockout следует шаблону MVVM, соединяя представление и модель представления через связывание с данными.
Чтобы создать привязки данных, вы добавляете к HTML-элементам специальный атрибут data-binding. Например, следующая разметка связывает элемент span со свойством genre в модели представления. Всякий раз, когда изменяется значение genre, Knockout автоматически обновляет HTML:
Привязки также могут работать в другом направлении, скажем, если пользователь вводит текст в поле, Knockout обновляет соответствующее свойство в модели представления.
Удобно, что связывание с данными осуществляется декларативно. Вам не требуется подключать модель представления к элементам HTML-страницы. Просто добавьте атрибут data-binding, и Knockout сделает остальное.
Я начал с создания HTML-страницы с базовой разметкой без связывания с данными, как показано на рис. 8 .
Рис. 8. Начальная HTML-разметка
No records found.
(Примечание Я использовал библиотеку Bootstrap для оформления внешнего вида приложения, поэтому в настоящем приложении уйма дополнительных элементов и CSS-классов, управляющих форматированием. Для ясности я убрал все это из кода.)
Создание модели представленияНаблюдаемые объекты (observables) занимают центральное место в системе связывания с данными в Knockout. Наблюдаемым является объект, который хранит какое-то значение и может уведомлять подписчиков об изменении этого значения. Следующий код преобразует JSON-представление фильма в эквивалентный объект с наблюдаемыми полями:
На рис. 9 показана начальная реализация модели представления. Эта версия поддерживает только получение списка фильмов. Средства редактирования я добавлю позже. Модель представления содержит наблюдаемые свойства для списка фильмов, строку ошибки и текущий жанр.
Рис. 9. Модель представления
Заметьте, что фильмы находятся в observableArray. Как и подразумевает его имя, observableArray действует как массив, уведомляющий подписчиков об изменении своего содержимого.
Функция getByGenre выдает AJAX-запрос серверу на получение списка фильмов, а затем заполняет результатами массив self.movies.
При использовании REST API одна из самых хитрых частей - обработка асинхронной природы HTTP. jQuery-функция ajax возвращает объект, реализующий Promises API. Вы можете задействовать метод then объекта Promise, чтобы установить обратный вызов, инициируемый, когда AJAX-вызов завершается успешно, и еще один обратный вызов, запускаемый при неудачном AJAX-вызове:
Теперь, когда у меня есть модель представления, я могу связать ее с HTML через привязки данных. Для полного списка жанров, который появляется на левой стороне экрана, я использую следующие привязки данных:
Атрибут data-bind содержит одно или более объявлений привязок, где каждая привязка имеет форму "привязка: выражение". В этом примере привязка foreach сообщает Knockout перебирать в цикле содержимое массива genres в модели представления. Для каждого элемента в массиве Knockout создает новый элемент
На данный момент щелчок названия жанра ни к чему не приводит, поэтому я добавляю привязку click для обработки событий щелчка:
Это связывает событие щелчка с функцией getByGenre в модели представления. Здесь нужно было использовать $parent, так как эта привязка осуществляется в контексте foreach. По умолчанию привязки в foreach ссылаются на текущий элемент в цикле.
Рис. 10. Добавление привязок в таблицу для отображения списка фильмов
На рис. 10 привязка foreach перебирает в цикле массив объектов movie. Внутри foreach привязки text ссылаются на свойства текущего объекта.
Привязка visible в элементе
Edit |