Создадим бессерверное приложение Vue.js на базе ButterCMS, для создания продающих страниц, блога и базы знаний

Создадим бессерверное приложение Vue.js на базе ButterCMS, для создания продающих страниц, блога и базы знаний

Почему Vue.js?

Эта статья написана не для того, чтобы убедить вас в том, что Vue.js самое лучшее решение и вы должны использовать его. Vue.js на сегодняшний день всего лишь прогрессивная основа для создания пользовательских интерфейсов, которая набирает популярность. Он улучшает некоторые аспекты React и Angular и позволяет создавать подключаемые компоненты, которые вы можете добавить в любой проект.

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

В этом учебном пособии вы узнаете, как добавить API-интерфейсы в приложение Vue.js. Эти API-интерфейсы легко внедрять, что позволит вам наслаждаться простым управлением контентом без необходимости разворачивать и поддерживать собственную инфраструктуру CMS.

В рамках этой статьи мы рассмотрим три варианта использования:

  • продающие страницы
  • базы знаний
  • блоггинг

ButterCMS

В этом руководстве для создания безсерверного приложения Vue.js используется ButterCMS. Это система управления контентом и платформа для блоггинга, которая позволяет создавать приложения на базе CMS с использованием любого языка программирования. Справедливости ради стоит заметить, что JSON API легко интегрируется с приложениями, созданными на любом языке/фреймворке.

Вы можете использовать его для создания веб-сайтов, блогов и моделирования пользовательского контента.

Создайте учетную запись бесплатно, чтобы следовать этому руководству. Здесь вы можете найти свой собственный API-ключ.

Создадим бессерверное приложение Vue.js на базе ButterCMS, для создания продающих страниц, блога и базы знаний

Итак приступим. Для начала установите пакет ButterCMS JS SDK, его мы будем использовать для запроса контента API.

npm install buttercms --save

1. Создание продающих страниц

Используя Vue и ButterCMS, вы можете создать среду, в которой любой человек в вашей команде сможет добавить контент на ваши продающие страницы, не имея абсолютно никаких технических знаний в области Web. Для этого в качестве примера мы создадим страницу тематического исследования. Используя панель управления на ButterCMS, вы можете создать «page type» под названием «Customer Case Study» и определить поля.

Как только вы это сделаете, вы можете создать свою первую страницу. Укажите имя и URL-адрес страницы с помощью панели инструментов Butter и заполните содержимое страницы. Как только вы это сделаете, API ButterCMS вернет страницу в формате JSON, которая должна выглядеть примерно так:

{
    "data": {
        "slug": "acme-co",
        "fields": {
            "facebook_open_graph_title": "Acme Co loves ButterCMS",
            "seo_title": "Acme Co Customer Case Study",
            "headline": "Acme Co saved 200% on Anvil costs with ButterCMS",
            "testimonial": "<p>We've been able to make anvils faster than ever before! - <em>Chief Anvil Maker</em></p>\r\n<p><img src=\"https://cdn.buttercms.com/NiA3IIP3Ssurz5eNJ15a\" alt=\"\" caption=\"false\" width=\"249\" height=\"249\" /></p>",
            "customer_logo": "https://cdn.buttercms.com/c8oSTGcwQDC5I58km5WV",
        }
    }
}

Затем откройте редактор кода и создайте файл buttercms.js в каталоге src.
Если у вас ещё нет существующего проекта создайте его следующими командами:

vue init webpack buttercms-project
cd buttercms-project
npm i
npm i -S buttercms
npm run dev

Теперь обновите маршруты в своем приложении

import Vue from 'vue'
import Router from 'vue-router'
import CustomersHome from '@/components/CustomersHome'
import CustomerPage from '@/components/CustomerPage'

Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/customers/',
      name: 'customers-home',
      component: CustomersHome
    },
    {
      path: '/customers/:slug',
      name: 'customer-page',
      component: CustomerPage
    }
  ]
})

Далее для настройки страницы клиентов, для отображения списка клиентов, мы определяем метод getpages () для получения всех страниц клиента. В components/CustomersHome.vue мы делаем:

<script>
  // import ButterCMS from 
  import { butter } from '@/buttercms'
  export default {
    name: 'customers-home',
    data() {
      return {
        page_title: 'Customers',
        // Create array to hold the pages from ButterCMS API
        pages: []
      }
    },
    methods: {
      // Get List of Customer Pages
      getPages() {
        butter.page.list('customer_case_study')
          .then((res) => {
            // console.log(res.data.data) // Check the results in the console
            this.pages = res.data.data
          })
      }
    },
    created() {
      // Fire on page creation
      this.getPages()
    }
  }
</script>

Отображение результата:

<template>
  <div id="customers-home">
    <h1>{{ page_title }}</h1>
    <div v-for="(page,index) in pages" :key="page.slug + '_' + index">
      <router-link :to="'/customers/' + page.slug">
        <div>
          <img :src="page.fields.customer_logo" alt="">
          <h2>{{ page.fields.headline }}</h2>
        </div>
      </router-link>
    </div>
  </div>
</template>

Теперь мы собираемся настроить страницу для просмотра одного клиента. Для этого в components/CustomerPage.vue мы определяем метод getPage () для получения определенной страницы клиента:
Когда вы отобразите результат, вы должны увидеть что-то вроде:

<script>
  import { butter } from '@/buttercms'
  export default {
    name: 'customer-page',
    data() {
      return {
        slug: this.$route.params.slug,
        page: {
          slug: '',
          fields: {}
        }
      }
    },
    methods: {
      getPage() {
        butter.page.retrieve('customer_case_study', this.slug)
          .then((res) => {
            console.log(res.data.data)
            this.page = res.data.data
          }).catch((res) => {
            console.log(res)
          })
      }
    },
    created() {
      this.getPage()
    }
  }
</script>

В результате получаем:

<template>
  <div id="customer-page">
    <figure>
      <img :src="page.fields.customer_logo">
    </figure>
    <h1>{{ page.fields.headline }}</h1>
    <h3>Testimonials</h3>
    <div v-html="page.fields.testimonial"></div>
    <div v-html="page.fields.body"></div>
  </div>
</template>

Теперь вы можете перейти на страницу клиента, созданную на панели управления Butter с помощью списка всех клиентских страниц или напрямую по URL-адресу.

2. Создание базы знаний

Сейчас вы познакомитесь с созданием базы знаний для своего приложения Vue.js. Для этого мы будем использовать «Content Fields» ButterCMS. Content Fields (поля контента) — это просто глобальные участки в контенте, которыми может управлять ваша команда. С помощью Content Field можно вывести практически любую информацию. Каждый Content Field имеет уникальный идентификатор для запроса.

Создание Content Field

Используя панель инструментов ButterCMS, вам нужно создать workspace (рабочее пространство) для управления Content Field. Зададим ему имя FAQ. Рабочие пространства позволят редактировать контент, не затрагивая разработку или API.

После создания Workspace, вы будете перенесены в этот workspace (рабочее пространство), кликните кнопку создать новый Content Field (поле содержимого). Выберите тип «Object» и назовите поле (Content Label) «FAQ Headline». API slug автоматически получит имя «faq_headline». Как видите не скрине, тип контента (Content Type) может быть абсолютно разный

После сохранения добавьте новый Content Field, но на этот раз выберите тип «Collection» и назовите поле «FAQ Items». Автоматически создастся API slug «faq_items». На следующем экране добавьте любые два параметра (properties) для элементов в коллекции.

Теперь вернитесь в workspace «FAQ» и добавьте нужные элементы.

Интеграция вашего приложения

Теперь, когда вы создали динамический контент, пришло время отобразить его в вашем приложении. Для этого вы получите поля с вызовом API и ссылаетесь на них в своем отображении. Вначалеа настройте маршрут на страницу часто задаваемых вопросов, для этого в редакторе кода:

router/index.js

import Vue from 'vue'
import Router from 'vue-router'

import FAQ from '@/components/FAQ'

Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/faq',
      name: 'faq',
      component: FAQ
    }
  ]
})

Далее создайте components/FAQ.vue с запросом получения FAQ из API:

<script>
  import { butter } from '@/buttercms'
  export default {
    name: 'faq',
    data() {
      return {
        page_title: 'FAQ',
        faq_items: []
      }
    },
    methods: {
      getFaqs() {
        butter.content.retrieve(['faq_headline', 'faq_items'])
          .then((res) => {
            console.log(res.data.data)
            this.page_title = res.data.data.faq_headline
            this.faq_items = res.data.data.faq_items
          })
      }
    },
    created() {
      this.getFaqs()
    }
  }
</script>

Результат должен быть примерно таким:

<template>
  <div id="faq">
    <h1>{{ page_title }}</h1>
    <div v-for="(faq, index) in faq_items" :key="index">
      <p>{{ faq.question }}</p>
      <p>{{ faq.answer }}</p>
    </div>
  </div>
</template>

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

2. Создание блога

Теперь мы займемся добавлением движка блога. Вы можете быстро создать блог с поддержкой CMS с помощью Vue.js.

Отображение статей

Мы начнем с создания маршрута блога с помощью vue-router. Для отображения статей, мы создаем простой маршрут /blog в нашем приложении и получаем сообщения из блога, а также создадим маршрут /blog/:slug для обработки отдельных сообщений.

router/index.js:

import Vue from 'vue'
import Router from 'vue-router'
import BlogHome from '@/components/BlogHome'
import BlogPost from '@/components/BlogPost'

Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/blog/',
      name: 'blog-home',
      component: BlogHome
    },
    {
      path: '/blog/:slug',
      name: 'blog-post',
      component: BlogPost
    }
  ]
})

Для создания главной страницы Home вашего блога создайте components/BlogHome.vue, на ней будут отображаться последние статьи

<script>
  import { butter } from '@/buttercms'
  export default {
    name: 'blog-home',
    data() {
      return {
        page_title: 'Blog',
        posts: []
      }
    },
    methods: {
      getPosts() {
        butter.post.list({
          page: 1,
          page_size: 10
        }).then((res) => {
          // console.log(res.data)
          this.posts = res.data.data
        })
      }
    },
    created() {
      this.getPosts()
    }
  }
</script>

На выходе должно получиться следующее:

<template>
  <div id="blog-home">
      <h1>{{ page_title }}</h1>
      <!-- Create v-for and apply a key for Vue. Example is using a combination of the slug and index -->
      <div v-for="(post,index) in posts" :key="post.slug + '_' + index">
        <router-link :to="'/blog/' + post.slug">
          <article class="media">
            <figure>
              <!-- Bind results using a ':' -->
              <!-- Use a v-if/else if their is a featured_image -->
              <img v-if="post.featured_image" :src="post.featured_image" alt="">
              <img v-else src="http://via.placeholder.com/250x250" alt="">
            </figure>
            <h2>{{ post.title }}</h2>
            <p>{{ post.summary }}</p>
          </article>
        </router-link>
      </div>
  </div>
</template>

Далее создайте components/BlogPost.vue, это будет вашей отдельной страницей блога.

<script>
  import { butter } from '@/buttercms'
  export default {
    name: 'blog-post',
    data() {
      return {
        post: {}
      }
    },
    methods: {
      getPost() {
        butter.post.retrieve(this.$route.params.slug)
          .then((res) => {
            // console.log(res.data)
            this.post = res.data
          }).catch((res) => {
            console.log(res)
          })
      }
    },
    created() {
      this.getPost()
    }
  }
</script>

Получаем:

<template>
  <div id="blog-post">
    <h1>{{ post.data.title }}</h1>
    <h4>{{ post.data.author.first_name }} {{ post.data.author.last_name }}</h4>
    <div v-html="post.data.body"></div>

    <router-link v-if="post.meta.previous_post" :to="/blog/ + post.meta.previous_post.slug" class="button">
      {{ post.meta.previous_post.title }}
    </router-link>
    <router-link v-if="post.meta.next_post" :to="/blog/ + post.meta.next_post.slug" class="button">
      {{ post.meta.next_post.title }}
    </router-link>
  </div>
</template>

На этом этапе ваше приложение вытаскивает все статьи из блога, позволяя вам перемещаться между ними. Если вы протестировали, то уже заметили, что кнопки навигации next/previous не работают. Почему? При использовании маршрутов с параметрами, когда пользователь переходит от /blog/foo к /blog/bar, один и тот же экземпляр компонента используется повторно.

Поскольку оба маршрута отображают один и тот же компонент, это более эффективно, чем уничтожение старого экземпляра и создание нового. Но это также означает, что хуки жизненного цикла компонента не будут вызваны.

Для этого есть исправление. Нам нужно посмотреть объект $route и вызвать getPost () при изменении маршрута. Для этого обновим раздел сценария в components/BlogPost.vue :

<script>
  import { butter } from '@/buttercms'
  export default {
    name: 'blog-post',
    data() {
      return {
        post: {}
      }
    },
    methods: {
      getPost() {
        butter.post.retrieve(this.$route.params.slug)
          .then((res) => {
            // console.log(res.data)
            this.post = res.data
          }).catch((res) => {
            console.log(res)
          })
      }
    },
    watch: {
      $route(to, from) {
        this.getPost()
      }
    },
    created() {
      this.getPost()
    }
  }
</script>


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

Также для фильтрации и размещения контента в своем блоге с категориями, тегами и авторами вы можете использовать API. Фактически, с точки зрения управления различными параметрами вашего блога, с помощью API вы можете сделать ещё очень много, например добавить разметку RSS, Atom, создать Sitemap, а также добавить стили CSS контента.

Послесловие

В этой статье наглядно показано, как легко создать бессерверное приложение Vue.js с поддержкой CMS. Прогрессивная инфраструктура для создания пользовательских интерфейсов Vue, довольно быстро реализуется, что экономит ваше время разработки.

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

Используйте ButterCMS, если вы хотите сосредоточиться на фронтоне и не увязнуть с такими бэкэнд операциями, как хостинг, масштабирование, безопасность и т. д.

Перевод статьи автора Jake Lumetta: Why and How You Should Build a Serverless CMS-powered Vue.js App

Создадим бессерверное приложение Vue.js на базе ButterCMS, для создания продающих страниц, блога и базы знаний

Гик, хакинтошник, линуксоид, считаю что немного умею в вебдизайн и сайтостроение

Создаём одностраничник с Vue.js

Сегодня мы создадим одностраничное веб-приложение с Vue.js, показывающее пользователю случайную цитату. Мы начнём с нуля, используя шаблон webpack-simple от создателей vue.

Этот пост — продолжение серии о том, как мы делали photographerexcuses.com.

Перед началом работы вам необходимо установить node.js (включая npm).

Создание проекта Vue.js

Мы используем шаблон webpack-simple:

npm install -g vue-cli
vue init webpack-simple vue-example-spa
cd vue-example-spa
npm install
npm run dev

В результате у нас будет создан шаблонный проект с webpack и Vue.js, а после выполнения команды npm run dev откроется вкладка браузера на localhost:8080 и вы должны увидеть такую картину:

Создаём одностраничник с Vue.js


Посмотрим на структуру проекта:

tree -I node_modules
.
├── index.html
├── package.json
├── README.md
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ └── main.js
└── webpack.config.js

Самое важное для нас сейчас — src/App.vue и src/main.js. В package.json перечислены все необходимые зависимости, а в webpack.config.js — задачи для сборки проекта.

Итак, начнём разработку и вначале изменим часть файла App.vue. Вот как он есть сейчас:

<template>
  <div id="app">
  </div>
</template>
Построение логики фронтенда

Если вы посмотрите на photographerexcuses.com, то увидите, что цитаты меняются по клику на текст цитаты или по кнопке refresh. Добавим несколько цитат в App.vue для имитации этого функционала:


<template>
  <div id="app">
    <blockquote>
      <p>{{currentQuote.content}}</p>
      <footer>
      — <cite>{{currentQuote.author}}</cite>
      </footer>
    </blockquote>
  </div>
</template>
<script>
export default {
  name: 'app',
  data () {
    return {
      currentQuote: {},
      quotes: [
        {
          content: "Overcoming fear and conceiving this 'art of more' should be a fundamental practice in what it is that you do and make.",
          author: "Chase Jarvis"
        },
        {
          content: "As everything in this world is but a sham, Death is the only sincerity",
          author: "Yamamoto Tsunetomo"
        },
        {
          content: "The world needs actual excitement and emotion more than it needs cool people.",
          author: "Amanda Palmer"
        }
      ]
    }
  },
  created: function() {
    this.displayQuote();
  },
  methods: {
    displayQuote: function(){
      var randomInt = Math.floor(Math.random() * (this.quotes.length - 0));
      this.currentQuote = this.quotes[randomInt];
    }
  }
}
</script>

Начнём с тега <sсript>. Здесь мы изменили возврат у функции data и теперь она возвращает пустой currentQuote и несколько цитат известных людей. Функция created выполняется при выводе компонента. Внутри methods у нас есть функция displayQuote, выбирающая случайную цитату и присваивающая её переменной currentQuote.</sсript>

Попробуйте это у себя, нажав несколько раз CTRL+R или CMD+R и вы должны увидеть смену цитат. Вот скрин того, как это должно выглядеть:

Создаём одностраничник с Vue.js

Но это ещё не все, мы хотим сделать смену цитат не только при перегрузке всей страницы.

Обработка событий в Vue.js

Обработать событие нажатия на кнопку очень просто в Vue, вы просто привязываете событие к функции в том же компоненте.

Было так:

<blockquote>

А стало так:

<blockquote v-on:click="displayQuote()">

Теперь при клике на цитату или её автора, произойдёт смена цитаты. Только помните, что имя функции должно быть точно таким, как задано в объекте methods
вашего компонента!

Работа с запросами XHR / AJAX с Vue и Axios

Для начала установим axios с помощью npm:

npm install axios --save

Для работы с axios необходимо его импортировать. Webpack позаботится о том, чтобы были правильно обработаны все импорты.

<script>
import axios from 'axios';

Для примера запросов и получения случайных данных при обновлении цитат, мы воспользуемся API от httpbin. Добавим немного данных, которые мы хотим получить, ключами в объекте data, в данном случае это userAgent.


data () {
    return {
      userAgent: '',
      currentQuote: {},

Затем перепишем метод displayQuote для выполнения HTTP-запроса. Для защиты контекста vue мы присвоим this переменной self в начале функции, таким образом, мы сможем ссылаться на неё внутри запроса axios:


displayQuote: function(){
    var self = this;
    var randomInt = Math.floor(Math.random() * (this.quotes.length - 0));
    this.currentQuote = this.quotes[randomInt];

    axios.get('http://httpbin.org/user-agent')
    .then(response => {
      self.userAgent = response.data['user-agent'];
    })
    .catch(error => {
      console.log(error);
    })
}

Первый блок .then запустится в случае успешного запроса, второй — при любой ошибке.

Теперь изменим шаблон для вывода пользовательского агента, добавив:

<p>Your user agent: {{userAgent}}</p>

в

<div id="app">

Перезапустим программу:

Создаём одностраничник с Vue.js
Деплой одностраничника

Деплой делается очень просто: выполните команду npm run build и скопируйте файл index.html и папку dist в любое место на сервере.

Дальнейшие шаги

Я реализовал довольно большой функционал и могу получать оправдания (фотографов) по уникальному идентификатору, например, такой запрос photographerexcuses.com/id/67 возвращает оправдание с id 67.

Для этого создан такой роут в vue-router:

{ path: '/id/:id', component: App }

а получаем id так:

this.$route.params.id;

Также я отслеживаю изменения в params.id для поддержки кнопки «Назад». И использую marked для разметки markdown, как в этом оправдании photographerexcuses.com/id/1 (AV выделено жирным).

По материалам: «Building a Singe Page App with Vue.js Tutorial»


Создаём одностраничник с Vue.js

Разработчик: java, kotlin, c#, javascript, dart, 1C, python, php.

Пишите: @ighar. Buy me a coffee, please :).

Создаём крутые диаграммы с Vue.js и Chart.js

Создаём крутые диаграммы с Vue.js и Chart.js

Интерактивные графики прекрасно визуализируют ваши данные, отчеты и т.п. Сегодня мы попробуем создать приложение с действительно классными графиками на Vue.js с Chart.js и покажем как настраивать эту библиотеку для достижения требуемого результата.

Быстрый старт

Мы будем использовать в проекте следующие инструменты:

С помощью утилиты vue-cli мы создадим базовую структуру проекта. Поэтому убедитесь, что она у вас установлена.

vue init webpack awesome-charts

Затем перейдём в папку проекта и установим зависимости:

cd awesome-charts && yarn install

Добавим vue-chartjs, через него мы будем работать с Chart.js:

yarn add vue-chartjs -S
Первый график

Создадим первый линейный график:

touch src/components/LineChart.js && subl .

Импортируем Line BaseChart из vue-chartjs и создадим наш компонент. В функции mount() нам необходимо вызвать метод renderChart() с нашими данными и параметрами.

Файл LineChart.js:

import {Line} from 'vue-chartjs'

export default Line.extend({
  mounted () {

    this.renderChart({
      labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
      datasets: [
        {
          label: 'Data One',
          backgroundColor: '#FC2525',
          data: [40, 39, 10, 40, 39, 80, 40]
        },{
          label: 'Data Two',
          backgroundColor: '#05CBE1',
          data: [60, 55, 32, 10, 2, 12, 53]
        }
      ]
    }, {responsive: true, maintainAspectRatio: false})

  }
})

Здесь мы передали простой объект данных chart.js с небольшим количеством данных и в параметрах указали responsive: true. Таким образом, размер графика будет расти пропорционально размеру внешнего контейнера.

Теперь мы можем удалить компонент Hello.vue из файла App.vue и импортировать наш график.

<template>
  <div id="app">
    <div class="container">
      <div class="Chart__list">
        <div class="Chart">
          <h2>Linechart</h2>
          <line-example></line-example>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import LineExample from './components/LineChart.js'
export default {
  name: 'app',
  components: {
    LineExample
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
.container {
  max-width: 800px;
  margin:  0 auto;
}
</style>

Запустим наше приложение и сможем увидеть результат в браузере:

yarn run dev 
Украшательства

Перейдём к настройке chart.js. Мы можем передать шестнадцатеричное значение цвета в backgroundColor; Но можем передать и значение rgba(). Таким образом, можно сделать цвет прозрачным. А так как chart.js использует канву (html canvas) для отрисовки, мы сможем использовать createLinearGradient().

Но для него нужен объект канвы, и vue-chartjs как раз содержит ссылку на него. Мы можем взять её как this.$refs.canvas.

В файле LineChart.js создадим две переменные для градиентов. Каждая на свой набор данных.

this.gradient = this.$refs.canvas
 .getContext(‘2d’)
 .createLinearGradient(0, 0, 0, 450)
this.gradient2 = this.$refs.canvas
 .getContext(‘2d’)
 .createLinearGradient(0, 0, 0, 450)

И есть ещё одна крутая функция: addColorStop()

Создадим три colorStop-а для каждого градиента, для 0%, 50% и 100%.

this.gradient.addColorStop(0, ‘rgba(255, 0,0, 0.5)’)
this.gradient.addColorStop(0.5, ‘rgba(255, 0, 0, 0.25)’);
this.gradient.addColorStop(1, ‘rgba(255, 0, 0, 0)’);
 
this.gradient2.addColorStop(0, ‘rgba(0, 231, 255, 0.9)’)
this.gradient2.addColorStop(0.5, ‘rgba(0, 231, 255, 0.25)’);
this.gradient2.addColorStop(1, ‘rgba(0, 231, 255, 0)’);

Теперь мы можем передать this.gradient в backgroundColor. У нас получился симпатичный градиент, а чтобы сделать его ещё лучше, мы настроим borderColor в индивидуальный цвет с alpha, равной 1, установим borderWidth в 1 и изменим pointColor:

borderColor: ‘#FC2525’, 
pointBackgroundColor: ‘white’, 
borderWidth: 1, 
pointBorderColor: ‘white’,
import {Line} from 'vue-chartjs'

export default Line.extend({
  data () {
    return {
      gradient: null,
      gradient2: null
    }
  },
  mounted () {
    this.gradient = this.$refs.canvas.getContext('2d').createLinearGradient(0, 0, 0, 450)
    this.gradient2 = this.$refs.canvas.getContext('2d').createLinearGradient(0, 0, 0, 450)

    this.gradient.addColorStop(0, 'rgba(255, 0,0, 0.5)')
    this.gradient.addColorStop(0.5, 'rgba(255, 0, 0, 0.25)');
    this.gradient.addColorStop(1, 'rgba(255, 0, 0, 0)');
    
    this.gradient2.addColorStop(0, 'rgba(0, 231, 255, 0.9)')
    this.gradient2.addColorStop(0.5, 'rgba(0, 231, 255, 0.25)');
    this.gradient2.addColorStop(1, 'rgba(0, 231, 255, 0)');


    this.renderChart({
      labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
      datasets: [
        {
          label: 'Data One',
          borderColor: '#FC2525',
          pointBackgroundColor: 'white',
          borderWidth: 1,
          pointBorderColor: 'white',
          backgroundColor: this.gradient,
          data: [40, 39, 10, 40, 39, 80, 40]
        },{
          label: 'Data Two',
          borderColor: '#05CBE1',
          pointBackgroundColor: 'white',
          pointBorderColor: 'white',
          borderWidth: 1,
          backgroundColor: this.gradient2,
          data: [60, 55, 32, 10, 2, 12, 53]
        }
      ]
    }, {responsive: true, maintainAspectRatio: false})

  }
})

Добавим стили для контейнера нашего App.vue в файле Styling.css:

.Chart {
  background: #212733;
  border-radius: 15px;
  box-shadow: 0px 2px 15px rgba(25, 25, 25, 0.27);
  margin:  25px 0;
}

.Chart h2 {
  margin-top: 0;
  padding: 15px 0;
  color:  rgba(255, 0,0, 0.5);
  border-bottom: 1px solid #323d54;
}
Результат
Создаём крутые диаграммы с Vue.js и Chart.js

По материалам «Creating stunning charts with Vue.js and Chart.js» by Jakub Juszczak

UPD: увидеть вживую этот пример и другие виды графиков с vue-chartjs можно посмотреть здесь.

Создаём крутые диаграммы с Vue.js и Chart.js

Разработчик: java, kotlin, c#, javascript, dart, 1C, python, php.

Пишите: @ighar. Buy me a coffee, please :).

Создание мобильных приложений с Cordova и Vue.js

Cordova это фреймворк, с которым вы можете создавать мобильные приложения с использование веб-технологий, таких как HTML, Javascript и CSS. Это позволяет создавать приложения сразу для нескольких платформ (к примеру, на Android и iOS) с общей кодовой базой. Несмотря на то, что вам всё ещё нужны платформо-зависимые технологии, типа Android SDK и XCode для сборки, вы можете создавать приложения без использования нативного Android- или iOS-кода.

А так как наш код это HTML и Javascript, мы легко можем использовать любимые Javascript библиотеки вроде Vue.js с Cordova.

И сегодня мы рассмотрим пример создания мобильного приложения с Cordova и Vue.js.

Исходный код проекта можно взять здесь.

Подготовка

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

  • Скачайте Node.js
  • Установите Cordova: npm install -g cordova
  • Ознакомьтесь с основами Vue.js
Создание проекта Cordova

Создадим проект и назовём его RandomWord:


cordova create RandomWord
cd RandomWord

Это создаст иерархию директорий:

Создание мобильных приложений с Cordova и Vue.js


  • config.xml — содержит информацию о приложении, используемых плагинах и платформах
  • platforms — содержит библиотеки Cordova для целевых платформ, на которых приложение будет запущено
  • plugins — содержит библиотеки плагинов Cordova, используемые в приложении. Они предоставляют приложению доступ к устройству: к камере, состоянию батареи и т.п.
  • www — содержит исходный код приложения на HTML, Javascript и CSS
  • hooks — содержит скрипты для настройки системы сборки приложения

Добавим платформу Android:

cordova platform add android --save

Эта команда добавит библиотеку платформы Android в папку platforms (platforms/android).


...
    <engine name="android" spec="~5.2.1" />
</widget>

Проверим установленные зависимости для сборки/запуска приложений Android с Cordova:

cordova requirements

Собираем наше приложение под Android:

cordova build android

Подключите ваш телефон к компьютеру и запустите приложение:

cordova run android

Если не подключать устройство, Cordova запустит приложение в эмуляторе. Наше приложение пока лишь показывает картинку и надпись:

Создание мобильных приложений с Cordova и Vue.js

Для построения приложения под iOS нужно выполнить те же шаги, заменив в командах android на ios. Также есть вариант использовать ваш браузер вместо мобильного устройства, установив платформу browser.

Изменим немного файл config.xml:


<?xml version='1.0' encoding='utf-8'?>
<widget id="io.coligo.randomword" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
    <name>RandomWord</name>
    <description>
        A mobile app for generating a random word.
    </description>
    <author email="michaelviveros@gmail.com" href="http://www.michaelviveros.com/">
        Michael Viveros
    </author>
    ...
Добавляем Vue.js

Добавим Vue.js CDN в конец файла www/index.html:


...
        <script type="text/javascript" src="cordova.js"></script>
        <script src="http://cdn.jsdelivr.net/vue/1.0.16/vue.js"></script>
        <script type="text/javascript" src="js/index.js"></script>
    </body>
</html>

Для того, чтобы приложение могло вызывать библиотеку Vue.js, добавим строчку в конец мета-тега Content Security Policy (CSP) в www/index.html:

; script-src 'self' http://cdn.jsdelivr.net/vue/1.0.16/vue.js 'unsafe-eval'

CSP позволяет создать список разрешённых источников и разрешает браузеру обрабатывать контент только из этих источников.

Часть мета-тега CSP script-src отвечает за то, откуда будут браться скрипты, исполняемые нашим приложением.

  • ’self’ — разрешены скрипты только из этого же источника, например, www/js/index.js
  • http://cdn.jsdelivr.net/vue/1.0.16/vue.js — разрешена библиотека Vue.js
  • ’unsafe-eval’ — разрешено выполнение небезопасного кода, так как некоторый код из Vue.js использует строки для генерации функций

Мета-тег CSP должен выглядеть так:

После замены кода в теле www/index.html некоторым кодом Vue.js, этот файл должен стать таким:


<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; script-src 'self' http://cdn.jsdelivr.net/vue/1.0.16/vue.js 'unsafe-eval'">
        <meta name="format-detection" content="telephone=no">
        <meta name="msapplication-tap-highlight" content="no">
        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
        <link rel="stylesheet" type="text/css" href="css/index.css">
        <title>Random Word</title>
    </head>
    <body>
        <div id="vue-instance" class="app">
            <h1>Random Word</h1>
            <button id="btn-get-random-word" @click="getRandomWord">Get Random Word</button>
            <p>{{ randomWord }}</p>
        </div>
        <script type="text/javascript" src="cordova.js"></script>
        <script src="http://cdn.jsdelivr.net/vue/1.0.16/vue.js"></script>
        <script type="text/javascript" src="js/index.js"></script>
    </body>
</html>

Теперь добавим немного Javascript для генерации и вывода случайного слова.

В www/js/index.js уже есть код, он может сменить цвет фона или заголовок, при получении приложением события deviceready. И нам не нужно пока лезть в эти методы. Однако, попробуйте сами разобраться с методом bindEvents для выполнения различных действий на различных этапах жизненного цикла приложения.

Мы добавим новый метод в файл www/js/index.js, назовём его setupVue, он будет создавать новый объект Vue и привязывать его к блоку random word. Новый объект Vue будет содержать метод getRandomWord, извлекающий случайное слово из списка слов при нажатии кнопки Get Random Word.


var app = {
    initialize: function() {
        this.bindEvents();
        this.setupVue();
    },
    ...
    setupVue: function() {
        var vm = new Vue({
            el: "#vue-instance",
            data: {
                randomWord: '',
                words: [
                    'formidable',
                    'gracious',
                    'daft',
                    'mundane',
                    'onomatopoeia'
                ]
            },
            methods: {
                getRandomWord: function() {
                    var randomIndex = Math.floor(Math.random() * this.words.length);
                    this.randomWord = this.words[randomIndex];
                }
            }
        });
    }
};

app.initialize();

После удаления ненужного кода, файл www/js/index.js будет таким:


var app = {
    initialize: function() {
        this.bindEvents();
        this.setupVue();
    },
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    onDeviceReady: function() {
        app.receivedEvent('deviceready');
    },
    receivedEvent: function(id) {
        console.log('Received Event: ' + id);
    },
    setupVue: function() {
        var vm = new Vue({
            el: "#vue-instance",
            data: {
                randomWord: '',
                words: [
                    'formidable',
                    'gracious',
                    'daft',
                    'mundane',
                    'onomatopoeia'
                ]
            },
            methods: {
                getRandomWord: function() {
                    var randomIndex = Math.floor(Math.random() * this.words.length);
                    this.randomWord = this.words[randomIndex];
                }
            }
        });
    }
};

app.initialize();

Соберём приложение, подключим телефон и запустим:

cordova build android
cordova run android

Приложение теперь выглядит примерно так:

Создание мобильных приложений с Cordova и Vue.js
HTTP запросы с vue-resource

Вместо того, чтобы использовать заданный список слов в нашем приложении, используем для этого специальный веб-сервис, например, этот: Wordnik Random Word API

В конец мета-тега CSP добавьте следующий код для разрешения работать с этим ресурсом:

; connect-src http://api.wordnik.com:80/v4/words.json/randomWord

В connect-src мы указали приложению, куда разрешено делать http-запросы.

Добавим библиотеку vue-resource для создание таких запросов.

index.html немного изменится:


<!DOCTYPE html>
...
        <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; script-src 'self' http://cdn.jsdelivr.net/vue/1.0.16/vue.js https://cdn.jsdelivr.net/vue.resource/0.7.0/vue-resource.min.js 'unsafe-eval'; connect-src http://api.wordnik.com:80/v4/words.json/randomWord">
...
        <script src="http://cdn.jsdelivr.net/vue/1.0.16/vue.js"></script>
        <script src="https://cdn.jsdelivr.net/vue.resource/0.7.0/vue-resource.min.js"></script>
        <script type="text/javascript" src="js/index.js"></script>
    </body>
</html>

Отредактируем файл www/js/index.js для использования веб-сервиса:


...
    setupVue: function() {
        var vm = new Vue({
            el: "#vue-instance",
            data: {
                randomWord: ''
            },
            methods: {
                getRandomWord: function() {
                    this.randomWord = '...';
                    this.$http.get(
                        'http://api.wordnik.com:80/v4/words.json/randomWord?api_key=a2a73e7b926c924fad7001ca3111acd55af2ffabf50eb4ae5'
                    ).then(function (response) {
                        this.randomWord = response.data.word;
                    }, function (error) {
                        alert(error.data);
                    });
                }
            }
        });
    }
};

app.initialize();

Соберём и запустим приложение:

cordova build android
cordova run android

Приложение выглядит как раньше, однако слова берутся уже с помощью Random word API.

Компоненты Vue

Vueify это библиотека для Vue.js, помогающая разделить интерфейс на небольшие компоненты, каждый со своим HTML, JavaScript и CSS. Приложение становится более модульным и вы можете вызывать отдельные его компоненты в иерархическом порядке.

Но для сборки модульного приложения нужно добавить хуки в скрипты сборки.

Так должна выглядеть структура папок после добавления Vue Component:

Создание мобильных приложений с Cordova и Vue.js



Создадим компонент, вызывающий наш метод получения слова в www/js/random-word.vue:


<template>
  <div class="app">      
    <h1>Random Word</h1>
    <button id="btn-get-random-word" @click="getRandomWord">Get Random Word</button>
    <p>{{randomWord}}</p>
  </div>
</template>

<script>
export default {
  data () {
    return {
      randomWord: ''
    }
  },
  methods: {
    getRandomWord: function() {
        this.randomWord = '...';
        this.$http.get(
            'http://api.wordnik.com:80/v4/words.json/randomWord?api_key=a2a73e7b926c924fad7001ca3111acd55af2ffabf50eb4ae5'
        ).then(function (response) {
            this.randomWord = response.data.word;
        }, function (error) {
            alert(error.data);
        });
    }
  }
}
</script>


Разметка HTML из файла www/index.html теперь находится в теге template, а Javascript код из www/js/index.js находится в теге script файла random-word.vue.

Создадим новый объект Vue, содержащий этот компонент в новом файле www/js/main.js:


var Vue = require('vue');
var VueResource = require('vue-resource');
var RandomWord = require('./random-word.vue');

Vue.use(VueResource);

var vm = new Vue({
  el: 'body',
  components: {
    'random-word': RandomWord
  }
});

Для склеивания компонентов мы используем browserify и vueify для создания файла bundle.js. Создадим новую папку scripts и в ней файл vueify-build.js.

scripts/vueify-build.js будет таким:


var fs = require('fs');
var browserify = require('browserify');
var vueify = require('vueify');

browserify('www/js/main.js')
  .transform(vueify)
  .bundle()
  .pipe(fs.createWriteStream('www/js/bundle.js'))

Раньше мы вызывали библиотеки Vue.js с CDN в www/index.html, но сейчас www/js/main.js использует javascript для этого. Создадим файл package.json для синхронизации всех зависимостей:


{
  "name": "random-word",
  "version": "1.0.0",
  "description": "A mobile app for generating a random word",
  "main": "index.js",
  "dependencies": {
    "browserify": "~13.0.1",
    "vue": "~1.0.24",
    "vue-resource": "~0.7.4",
    "vueify": "~8.5.4",
    "babel-core": "6.9.1",
    "babel-preset-es2015": "6.9.0",
    "babel-runtime": "6.9.2",
    "babel-plugin-transform-runtime": "6.9.0",
    "vue-hot-reload-api": "2.0.1"
  },
  "author": "Michael Viveros",
  "license": "Apache version 2.0"
}

Установить все зависимости проекта теперь можно одной командой:

npm install
Добавим хук в конец файла config.xml, чтобы Cordova знала о необходимости склеивания компонента перед сборкой приложения:


...
    <hook type="before_compile" src="scripts/vueify-build.js" />
</widget>


Мы помним, что scripts/vueify-build.js создаст собранный компонент и положит его в www/js/bundle.js

Добавим вызов компонента в тело www/index.html, создав тег random-word и тег script, вызывающий наш склеенный ранее компонент.


...
        <link rel="stylesheet" type="text/css" href="css/index.css">
        <title>Random Word</title>
    </head>
    <body>
        <random-word></random-word>
        <script src="js/bundle.js"></script>

        <script type="text/javascript" src="js/index.js"></script>
        <script type="text/javascript" src="cordova.js"></script>
    </body>
</html>

Помните, что тег link в www/index.html задаёт CSS приложения, а тег div в www/js/random-word.vue использует класс «app» из CSS.

Поскольку мы перенесли логику в другое место, теперь мы можем удалить метод setupVue из www/js/index.js:


var app = {
    initialize: function() {
        this.bindEvents();
    },
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    onDeviceReady: function() {
        app.receivedEvent('deviceready');
    },
    receivedEvent: function(id) {
        console.log('Received Event: ' + id);
    }
};

app.initialize();

Соберём и запустим приложение:

cordova build android
cordova run android
Заключение

Разрабатывать мобильные приложения с Cordova очень просто. А с Vue.js вы можете использовать огромное количество её функций в мобильных приложениях.

Написано по материалам Building a Mobile App with Cordova and Vue.js by Michael Viveros

Создание мобильных приложений с Cordova и Vue.js

Разработчик: java, kotlin, c#, javascript, dart, 1C, python, php.

Пишите: @ighar. Buy me a coffee, please :).