От автора: одностраничные приложения имеют много преимуществ — скорость, действительно хороший UX и, что касается разработки для Drupal, полный контроль над разметкой. Все большее число сайтов использует SPA; доступно все больше и больше инструментов, которые упрощают процесс разработки сложного SPA. Если вы читаете наши статьи (а если нет, то вам стоит), вы, вероятно, слышали о молодой реактивной среде под названием Vue.js и о том, как ее можно использовать в Drupal.
Мы создадим SPA Vue js — клиентское приложение простого блога. Это приложение будет отображать список всех постов и полный текст поста. Все изменения будут выполняться без перезагрузки страницы. На примере этого приложения вы узнаете, как извлекать данные в Vue, как создать маршрутизатор, и мы также рассмотрим очень интересную концепцию — Vue для отдельных файловых компонентов.
Серверная сторона
Поскольку мы имеем дело только с клиентом, мы будем использовать службу jsonplaceholder.typicode.com, которая предоставляет виртуальный онлайн-REST API. Если вы хотите использовать Drupal в качестве back-end (jeez, это, конечно же, сайт на Drupal), мы уже писали о том, как организовать RESTful-сервер с Drupal 8. Посмотрите статьи по RESTful в блоке «Полезные ссылки».
Клиентская сторона
Инструменты
Начать работу с 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
Это автоматически запустит наш проект на сервере разработки webpack. В браузере мы можем уже увидеть наше простое приложение Vue. Конечно, пока оно выглядит не так, как мы хотим. Чтобы продолжить работу, я предлагаю сначала ознакомиться со структурой нашего шаблона.
Шаблон Webpack-simple
Внутри шаблона Webpack-simple мы имеем следующую структуру:
Существует файл index.html с простой HTML-разметкой, включающей только элемент с идентификатором “app” в body. Он будет заменен сгенерированных Vue DOM. Вот почему вы не должны использовать тег body, как корневой элемент.
В каталоге src у нас есть файл main.js, который является точкой входа для webpack. Здесь импортируются компоненты Vue. Кроме того, у нас есть экземпляр root Vue, у которого есть два свойства. Свойство ‘el’ предоставляет экземпляр Vue существующего элемента DOM для монтирования. А второй — это функция рендеринга, которая генерирует DOM из App.vue.
В общем, это все, что нам нужно знать о структуре простого шаблона webpack. Не так уж плохо, не так ли? Основная часть нашего приложения будет закодирована в App.vue. Расширение .vue указывает, что этот файл является однофайловым компонентом vue. Это одна из функций Vue, давайте подробнее узнаем о ней.
Однофайловые компоненты
Каждый файл *.vue состоит из блоков трех типов: <template>, <script> и необязательного <style>. В результате этого мы можем разделить проект на слабосвязанные компоненты. Внутри компонента его шаблон, логика и стили неотъемлемо связаны, и их сопоставление фактически делает компонент более согласованным и поддерживаемым. Итак, теперь мы готовы создать наш Vue Blog.
Создание приложения
У нас в верхней части страницы есть заголовок с названием блога. С левой стороны у нас будет фиксированная боковая панель, в которой мы будем отображать заголовки наших статей — это будет что-то вроде оглавления. Остальная часть страницы будет занята динамическим блоком, в котором будет отображаться сама статья.
Шаг 1
Удалите все ненужные строки из App.vue и создайте шаблон в соответствии с нашими требованиями.
<template> <div id="app"> <header> <h1>Vue.js SPA</h1> </header> <main> <aside class="sidebar"> </aside> <div class="content"> </div> </main> </div> </template>
Затем мы создадим экземпляр Vue с свойством data, которое мы разместим в массиве с нашими постами. На данный момент он пуст, но вскоре мы поместим в массив данные, полученные с сервера.
После обозревателя вы больше не сможете добавлять реактивные свойства к корневому объекту данных. Поэтому, прежде чем создавать экземпляр Vue, рекомендуется объявить все реактивные свойства уровня root.
<script> export default { data () { return { posts: [] } } } </script>
Кроме того, я добавил несколько стилей, чтобы наше приложение выглядело лучше. Код приложения размещен на github.com. Просто клонируйте репозиторий и переключите ветвь на номер шага, чтобы шаг за шагом следовать за созданием приложения, например:
$ git checkout step-1
На данный момент нам абсолютно нечего отображать в панели навигации, поэтому давайте возьмем данные с нашего сервера. Для этого я выбрал axios, действительно простой в использовании HTTP-клиент. Вы также можете использовать любой удобный для вас способ, например, Vue-resource или оригинальное извлечение, или даже jQuery Ajax.
Шаг 2
Установите axios.
$ npm install — save-dev axios
Затем импортируйте его в компонент App и создайте метод getAllPosts(), который мы отправим на сервер Drupal и установим его для свойства поста. Вызовите метод в хуке 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); }) } } }
И теперь отобразите все заголовки статей в боковой панели.
<aside class="sidebar"> <div v-for="post in posts"> {{ post.title }} </div> </aside>
Сейчас мы отображаем названия постов, но мы не можем видеть полные посты. Теперь я собираюсь отобразить полный пост в разделе контента в соответствии с выбранным названием на боковой панели. В то же время я хочу, чтобы каждая статья была доступна по ее уникальному адресу.
Шаг 3
Я буду использовать официальную библиотеку Vue vue-router для реализации этого функционала. Как понятно из названия, эта библиотека позволяет настраивать маршрутизацию для нашего приложения. Установите пакет:
$ 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-адресов. Вместо этого мы можем использовать опцию propsand и установить для нее значение true. После этого $route.params устанавливается в качестве свойства компонента Post.
Теперь, когда мы создали маршрутизатор, мы можем вернуться к нашему приложению и добавить еще несколько строк в шаблон.
<main> <aside class="sidebar"> <router-link v-for="post in posts" active-class="is-active" class="link" :to="{ name: 'post', params: { id: post.id } }"> {{post.id}}. {{post.title}} </router-link> </aside> <div class="content"> <router-view></router-view> </div> </main>
Здесь у нас есть два компонента vue-router: <route> r-link и <router-view>. Первый — это компонент для включения навигации пользователя в приложении с поддержкой маршрутизатора. Второй компонент — это функциональный компонент, который отображает согласованный компонент для данного пути.
Шаг 4
Перейдем к файлу Post.vue, который мы создадим через простой шаблон:
<template lang="html"> <div class="post" v-if="post"> <h1 class="post__title">{{ post.title }}</h1> <p class="post__body">{{ post.body }}</p> <p class="post__id">{{ post.id }}</p> </div> </template>
Затем нам нужно установить параметры экземпляра Vue для этого компонента. Здесь все будет очень похоже на настройки отображения всех постов. Объявите опцию props с переменной id, которая получает номер нашего поста. Затем определите объект данных:
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 не перерисовывает его, пытаясь сохранить ресурсы. Это означает, что хуки жизненного цикла никогда не выполняются. Чтобы исправить это, нам просто нужно установить наблюдателя для объекта $route.
watch: { '$route'() { this.getPost(this.id); } }
Теперь все работает точно так, как нужно!
Заключение
Мы создали простое одностраничное приложение с Vue. Мы узнали, как просто начать проект с помощью vue-cli. Мы рассмотрели концепцию однофайловых компонентов Vue, которые делают ваш проект более гибким и масштабируемым. Мы узнали, как извлекать данные из внешнего API с помощью axios. И мы увидели, как настроить маршрутизацию с помощью vue-router. Чтобы получить производственную версию этого приложения, просто запустите в терминале npm run build. Если у вас есть вопросы, не стесняйтесь обращаться к нам.
Автор: Dmitry Chuchin
Источник: https://itnext.io/
Редакция: Команда webformyself.