От автора: сегодня поговорим о том, как создать с помощью Vue PWA. PWA (прогрессивные веб-приложения) уже были определены как будущее веб-приложений, и причина совершенно очевидна. PWA позволяют создавать веб-приложения, способные предоставлять пользователям опыт, не отличимый от нативных приложений.
От отправки push-уведомлений, кэширование данных для автономного извлечения, синхронизации фона. Прогрессивные веб-приложения полностью вас прикроют. PWA также могут гарантировать, что пользователи будут вовлечены и будут получать свежие данные динамические данные даже при очень плохих сетевых подключениях.
«Прогрессивное веб-приложение (PWA) — это термин, используемый для обозначения веб-приложений, использующих новейшие веб-технологии. Прогрессивные веб-приложения, также известные как «Устанавливаемые веб-приложения» или «Гибридные веб-приложения», являются обычными веб-страницами или веб-сайтами, но могут отображаться как обычные приложения или собственные мобильные приложения. Тип приложения пытается объединить функции, предлагаемые большинством современных браузеров, с преимуществами мобильного использования. — Википедия»
В этой статье показано, как создать простое PWA реального времени с Vue.js и Pusher. Vue.js — это прогрессивная веб-платформа для JavaScript, она проста в использовании и требует относительно небольшого кода для создания потрясающих результатов.
Для части реального времени этого приложения мы будем подключать библиотеку JavaScript Pusher. Pusher — это механизм реального времени, который позволяет легко добавлять функциональные возможности в реальном времени в приложения.
Что мы хотим создать
В этой статье мы будем создавать криптовалютное приложение под названием «KryptoWatcher». Его функция заключается в отображении обновлений цен трех криптовалют (Bitcoin, Ethereum и Litecoin) в реальном времени. Обновления цен будут получены из API Cryptocompare.
KryptoWatcher также сможет заглянуть на пять дней в прошлое и получить данные за те дни. Вот визуальное отображение того, как будет выглядеть окончательное приложение:
Большая часть всего этого заключается в том, что после того, как это приложение запускается один раз, оно может снова запускаться и отображать данные монет даже без подключения к Интернету. Это возможно, потому что мы построим KryptoWatcher для кэширования данных монет при первом запуске.
Давайте начнем собирать кусочки.
Требования
Чтобы следовать этому руководству, вам нужно будет иметь следующее:
Знать Vue.js.
Vue CLI, установленный на вашем компьютере.
Node и NPM установлены на вашем компьютере.
Знание инфраструктуры Node.js и Express.
Знание JavaScript.
Pusher Application. Создайте его здесь.
Как только у вас появятся требования, мы можем перейти к настройке нашего приложения.
Настройка Pusher Application
Создайте учетную запись Pusher, если вы еще этого не сделали, а затем настройте приложение, как показано на скриншоте ниже.
Когда закончите настройку, обратите внимание на свои ключи приложения Pusher. Позже они нам понадобятся.
Настройка нашего приложения PWA Vue.js
Вы можете думать, что инструмент Vue CLI – это облегченный инструмент для создания проектов Vue.js. Чтобы начать строить наше приложение, мы будем использовать инструмент Vue CLI, чтобы вытащить шаблон Vue PWA, с которым мы будем работать.
Чтобы создать наше приложение, выполните следующую команду на своем терминале:
$ vue init pwa krypto-watcher
Вам будут предложены подсказки и несколько вопросов «Да» или «Нет». Тем не менее, вы можете отвечать на все, как вам удобно, для подсказок «Y» или «N», так как мы не требуем дополнительных функций и функциональности, давайте ответим «N» на все запросы.
Шаблон дает нам потрясающие возможности PWA по умолчанию. Одной из таких функций является service worker. Service worker позволяет нашему приложению работать в автономном режиме.
Service worker — это скрипт, который ваш браузер запускает в фоновом режиме, отдельно от веб-страницы, открывая дверь для функций, которым не нужна веб-страница или взаимодействие с пользователем.
Чтобы установить зависимости, перейдите в окно терминала и выполните следующую команду:
$ cd krypto-watcher && npm install
Если вы посмотрите на свою директорию проекта, вы обнаружите, что она содержит несколько подпапок: build, config, src, static, test. Давайте откроем файл build/webpack.prod.conf.js и быстро заглянем в объект SWPrecacheWebpackPlugin:
new SWPrecacheWebpackPlugin({ cacheId: 'krypto-watcher', filename: 'service-worker.js', staticFileGlobs: ['dist/**/*.{js,html,css}'], minify: true, stripPrefix: 'dist/' })
Это означает, что при создании приложения создается новый service worker (с командой npm run build).
Service worker будет кэшировать все файлы, соответствующие выражению glob, для автономного доступа, в staticFileGlobs который в настоящее время указывает на несуществующую папку dist. Каталог dist будет создан при создании нашего приложения.
Давайте начнем строить наше приложение по компонентам.
Компоненты Vue.js
Подобно другим современным JavaScript-библиотекам и фреймворкам, таким как React, Vue позволяет нам создавать компоненты при создании приложений. Компоненты помогают нам сохранять модульное приложение и гарантировать, что приложения могут быть разделены на модули многократного использования.
Давайте построим KryptoWatcher, создав три компонента многократного использования:
Компонент Intro который будет содержать вводную разметку и стили для приложения.
Current компонент, который будет отображать цены монет в реальном времени.
Previous компонент, который будет отображать цены монет от «x дней назад».
Давайте начнем создавать компоненты. Мы будем делать их вручную, однако вы всегда можете использовать пакет NPM, как здесь, чтобы упростить создание компонентов. Создайте каталог src/components и создайте следующие файлы в каталоге: Intro.vue , Current.vue и Previous.vue.
Компонент intro
Этот компонент не имеет особых функций, поскольку он просто содержит встроенную разметку и стили, которые сделают приложение презентабельным. HTML идет между тегами template, а стили идут в теге styles.
В файле Intro.vue вставьте следующее:
<template> <header class="hero"> <div class="bar logo"> <h3>KryptoWatcher</h3> <span class="monitor"><span class="monitorText">receive updates</span></span> </div> <h1>Realtime PWA that displays updates on cryptocurrencies</h1> <h2>Bitcoin, Ethereum, Litecoin?</h2> </header> </template> <script>export default { name: 'app' }</script> <style scoped> header { background: linear-gradient(to bottom right, rgb(0, 193, 131),rgb(50, 72, 95)); padding: 1em; margin-bottom: 1em; text-align: center; height: 300px; color: #fff; } header h3 { color: white; font-weight: bold; text-transform: uppercase; float: left; } bar { padding: 20px; height: 48px; } .monitor{ text-transform: uppercase; float:right; background-color: rgba(255, 255, 255, 0.2); line-height: 23px; border-radius: 25px; width: 175px; height: 48px; margin: auto; } .monitor:hover, monitorText:hover { cursor:pointer; } .monitorText{ width: 104px; height: 23px; font-weight: bold; line-height: 50px; font-size: 14px; } header h1 { padding-top: 80px; width: 80%; margin: auto; } header h2{ padding-top:20px; } </style>
С компонентом intro закончили.
Компонент current
В компоненте Current.vue мы напишем некоторый HTML, который отображает цены в реальном времени по мере их обновления. Откройте файл и вставьте следующее внутри файла:
<template> <div> <h2>Current prices of coins</h2> <div id="btc" class="currency"> <label>1 BTC</label> <p>${{currentCurrency.BTC}}</p> </div> <div id="eth"class="currency"> <label>1 ETH</label> <p>${{currentCurrency.ETH}}</p> </div> <div id="ltc"class="currency"> <label>1 LTC</label> <p>${{currentCurrency.LTC}}</p> </div> </div> </template>
Ниже тегов template у нас будет тег script. Это будет то, где мы будем обрабатывать скрипты компонента. Ниже тега template в том же файле вставьте следующий код:
<script> export default { name: 'app', props: { currentCurrency: { type: Object } }, data () { return {} } } </script>
Скрипт выше указывает props, которые должен ожидать компонент Current. Он будет получать его, currentCurrency, из родительского компонента App.vue.
Наконец, под тегом script, давайте укажем style для компонента. Вставьте следующий код после тега script:
<style scoped> .currency { border: 1px solid #F5CE00; border-radius: 15px; padding: 2em 0em; display: inline-block; width: 30%; } div p { font-size: 2rem; } h2 { font-size: 1.5em; } </style>
С компонентом Current закончили.
Компонент Previous
Этот компонент должен показывать цены на монеты в прошлом, максимум пять дней. Мы также покажем даты каждого из дней. В файле Previous.vue вставьте следующий код:
<template> <div> <h2>Previous prices of coins</h2> <div id="first"> <h2>Date: {{previousCurrency.yesterday.DATE}}</h2> <p><label>1 BTC:</label> {{previousCurrency.yesterday.BTC}}</p> <p><label>1 ETH:</label> {{previousCurrency.yesterday.ETH}}</p> <p><label>1 LTC:</label> {{previousCurrency.yesterday.LTC}}</p> </div> <div id="second"> <h2>Date: {{previousCurrency.twoDays.DATE}}</h2> <p><label>1 BTC:</label> {{previousCurrency.twoDays.BTC}}</p> <p><label>1 ETH:</label> {{previousCurrency.twoDays.ETH}}</p> <p><label>1 LTC:</label> {{previousCurrency.twoDays.LTC}}</p> </div> <div id="third"> <h2>Date: {{previousCurrency.threeDays.DATE}}</h2> <p><label>1 BTC:</label> {{previousCurrency.threeDays.BTC}}</p> <p><label>1 ETH:</label> {{previousCurrency.threeDays.ETH}}</p> <p><label>1 LTC:</label> {{previousCurrency.threeDays.LTC}}</p> </div> <div id="fourth"> <h2>Date: {{previousCurrency.fourDays.DATE}}</h2> <p><label>1 BTC:</label> {{previousCurrency.fourDays.BTC}}</p> <p><label>1 ETH:</label> {{previousCurrency.fourDays.ETH}}</p> <p><label>1 LTC:</label> {{previousCurrency.fourDays.LTC}}</p> </div> <div id="fifth"> <h2>Date: {{previousCurrency.fiveDays.DATE}}</h2> <p><label>1 BTC:</label> {{previousCurrency.fiveDays.BTC}}</p> <p><label>1 ETH:</label> {{previousCurrency.fiveDays.ETH}}</p> <p><label>1 LTC:</label> {{previousCurrency.fiveDays.LTC}}</p> </div> </div> </template>
В разделе script мы будем получать объект previousCurrency из родительского компонента App.vue. В том же файле вставьте следующий код после тега template:
<script> export default { name: 'app', props: { previousCurrency: { type: Object } }, data () { return {} } } </script>
Наконец, немного декоративных стилей:
<style scoped> #first, #second, #third, #fourth, #fifth { border: 1px solid #F5CE00; padding: 2em 0em; max-width: 90%; margin: 3px auto; } #first p, #second p, #third p, #fourth p, #fifth p { display: inline-block; padding: 0em 1.5em; font-size: 1.5rem; } h2 { font-size: 1.5em; } </style>
Это почти все, что у нас есть с тремя компонентами, они довольно просты. Большая часть сложности и логики приложений App.vue в корневом компоненте App.vue. Давайте исследуем его дальше.
Настройка корневого компонента
Корневой компонент включен по умолчанию в каждой новой установке Vue в файле src/App.vue, поэтому нам не нужно его создавать. В отличие от других компонентов, которые мы создали ранее, корневой компонент содержит логику и сложнее, чем они.
Мы сохраним тег template корневого компонента простым. Мы включаем более ранние компоненты, Intro.vue , Current.vue и Previous.vue, в качестве пользовательских тегов и передаем соответствующие props. В файле App.vue замените содержимое следующим:
<template> <div> <intro></intro> <div id="body"> <div id="current"> <current v-bind:currentCurrency="currentCurrency"></current> </div> <div id="previous"> <previous v-bind:previousCurrency="previousCurrency"></previous> </div> </div> </div> </template>
Затем добавим script и добавим логику в script . Вставьте следующее ниже тега template:
<script> import Intro from './components/Intro.vue'; import Current from './components/Current.vue'; import Previous from './components/Previous.vue'; export default { name: 'app', components: {Intro, Current, Previous}, data() { return { currentCurrency: {BTC: '', ETH: '', LTC: ''}, previousCurrency: { yesterday: {}, twoDays: {}, threeDays: {}, fourDays: {}, fiveDays: {} } } }, methods: { // Stub }, created() { // Stub } } </script>
Сценарий выше не делает многого, но он создает основу для нашей логики. Мы установили все значения по умолчанию для data, которые будем использовать в приложении, и мы определили created метод, который вызывается автоматически во время жизненного цикла компонента Vue. Мы также импортировали компоненты, которые будем использовать в приложении.
Прежде чем мы начнем добавлять скриптовую логику, добавим стили для корневого компонента. Ниже тега script вставьте следующий код:
<style> @import url('https://fonts.googleapis.com/css?family=Lato'); * { margin : 0px; padding : 0px; font-family: 'Lato', sans-serif; } body { height: 100vh; width: 100%; } .row { display: flex; flex-wrap: wrap; } h1 { font-size: 48px; } a { color: #FFFFFF; text-decoration: none; } a:hover { color: #FFFFFF; } a:visited { color: #000000; } .button { margin: auto; width: 200px; height: 60px; border: 2px solid #E36F55; box-sizing: border-box; border-radius: 30px; } #body { max-width: 90%; margin: 0 auto; padding: 1.5em; text-align: center; color:rgb(0, 193, 131); } #current { padding: 2em 0em; } #previous { padding: 2em 0em; } </style>
Добавление методов в наш корневой компонент
Нам нужно заполнить объект method с помощью реальных методов. Начнем с определения методов, которые будут получать цены на монеты за предыдущие дни.
Подтягивание зависимостей
Поскольку мы получаем данные из удаленного API, нам нужен HTTP-клиент, чтобы вытащить данные для нас. В этой статье мы будем использовать ориентированные на promise HTTP-клиент axios для отправки HTTP-запросов. Нам также нужен moment чтобы легко работать с датами.
Чтобы добавить Axios и Moment.js в наш проект, выполните следующую команду в своем терминале:
npm install --save vue-axios axios vue-momentjs moment
vue-axios и vue-momentjs — обертки Vue вокруг пакетов Axios и Moment.js.
Когда установка будет завершена, мы будем глобально импортировать пакеты в наше приложение. Откройте файл src/main.js и замените:
import App from './App'
на:
import App from './App' import moment from 'moment'; import VueMomentJS from 'vue-momentjs'; import axios from 'axios' import VueAxios from 'vue-axios' Vue.use(VueAxios, axios) Vue.use(VueMomentJS, moment);
Построение логики методов
Затем мы хотим вернуться к нашему корневому компоненту и построить объект methods. В объекте methods давайте создадим первый метод. Вставьте следующий код внутри объекта methods в файл App.vue:
_fetchDataFor: (key, daysAgo) => { var date = this.$moment().subtract(daysAgo, 'days').unix() let fetch = (curr, date) => this.axios.get(`https://min-api.cryptocompare.com/data/pricehistorical?fsym=${curr}&tsyms=USD&ts=${date}`) this.axios .all([fetch('BTC', date), fetch('ETH', date), fetch('LTC', date)]) .then(this.axios.spread((BTC, ETH, LTC) => { this.previousCurrency[key] = { BTC: BTC.data.BTC.USD, LTC: LTC.data.LTC.USD, ETH: ETH.data.ETH.USD, DATE: this.$moment.unix(date).format("MMMM Do YYYY"), } localStorage.setItem(`${key}Prices`, JSON.stringify(this.previousCurrency[key])); })) },
Вышеуказанный метод является вспомогательным методом для сбора обменного курса монеты в течение определенного периода времени и сохранения ответа в localStorage и this.previousCurrency. Мы будем использовать это позже в коде.
Затем вставьте следующую функцию внутри объекта methods рядом с тем, который мы добавили выше:
_fetchDataForToday: () => { let url = 'https://min-api.cryptocompare.com/data/pricemulti?fsyms=BTC,ETH,LTC&tsyms=USD' this.axios.get(url).then(res => { localStorage.setItem('BTC', this.currentCurrency.BTC = res.data.BTC.USD), localStorage.setItem('ETH', this.currentCurrency.ETH = res.data.ETH.USD), localStorage.setItem('LTC', this.currentCurrency.LTC = res.data.LTC.USD) }) },
Вышеуказанный метод просто извлекает данные монеты за текущую дату и сохраняет ответ на localStorage и объект this.currentCurrency. Далее, внутри метода created() корневого компонента вставьте следующий код:
if ( ! navigator.onLine) { this.currentCurrency = { BTC: localStorage.getItem('BTC'), ETH: localStorage.getItem('ETH'), LTC: localStorage.getItem('LTC'), } this.previousCurrency = { yesterday: JSON.parse(localStorage.getItem('yesterdayPrices')), twoDays: JSON.parse(localStorage.getItem('twoDaysPrices')), threeDays: JSON.parse(localStorage.getItem('threeDaysPrices')), fourDays: JSON.parse(localStorage.getItem('fourDaysPrices')), fiveDays: JSON.parse(localStorage.getItem('fiveDaysPrices')) } } else { this._fetchDataFor('yesterday', 1) this._fetchDataFor('twoDays', 2) this._fetchDataFor('threeDays', 3) this._fetchDataFor('fourDays', 4) this._fetchDataFor('fiveDays', 5) this._fetchDataForToday() }
В приведенном выше коде мы определили код для извлечения текущей валюты из localStorage если клиент находится в автономном режиме. Если клиент находится в сети, он извлекает данные из API.
Теперь все должно работать, кроме функциональности в реальном времени.
Интеграция функций реального времени с помощью Pusher
Теперь, когда у нас есть функциональное приложение, мы хотели бы добавить некоторые функции реального времени, чтобы мы видели обновления по мере их возникновения.
Мы будем использовать Pusher для предоставления этой функции, если вы этого не сделали, создайте приложение Pusher с панели инструментов Pusher, так как вам понадобятся: app_id, key, secret и cluster.
Создание базы данных Node.js для нашего приложения
Нам нужен сервер базы данных для запуска событий в Pusher, мы будем использовать Node.js для создания бэкэнд для этой статьи.
Чтобы начать работу, создайте новый файл в корневом каталоге нашего приложения и назовите его server.js. В этом файле server.js мы будем использовать Express в качестве веб-фреймворка, поэтому нам нужно потянуть его. Мы также будем использовать axios, Pusher и body-parser так как мы будем ссылаться на них в нашем коде.
В вашем терминале введите следующую команду:
$ npm install --save express axios body-parser pusher
По завершении установки откройте файл server.js и вставьте в него следующий код:
const express = require('express'); const path = require('path'); const bodyParser = require('body-parser'); const app = express(); const Pusher = require('pusher'); const axios = require('axios'); // Initialise Pusher var pusher = new Pusher({ appId: 'PUSHER_APP_ID', key: 'PUSHER_APP_KEY', secret: 'PUSHER_APP_SECRET', cluster: 'PUSHER_APP_CLUSTER', encrypted: true }); // Body parser middleware app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); // CORS middleware app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', '*') res.setHeader('Access-Control-Allow-Credentials', true) res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type') next() }); // Routes app.get('/', _ => res.send('Welcome')); // Simulated Cron setInterval(_ => { let url = 'https://min-api.cryptocompare.com/data/pricemulti?fsyms=BTC,ETH,LTC&tsyms=USD'; axios.get(url).then(res => { pusher.trigger('price-updates', 'coin-updates', {coin: res.data}) }) }, 5000) // Start app app.listen(8000, () => console.log('App running on port 8000!'));
Вам нужно заменить PUSHER_APP_ID, PUSHER_APP_KEY, PUSHER_APP_SECRET и PUSHER_APP_CLUSTER на детали с панели инструментов приложения Pusher.
В приложении Express выше мы импортируем наши зависимости, а затем создаем экземпляр Pusher. Затем мы регистрируем некоторое промежуточное программное обеспечение, включая промежуточное CORS, поэтому мы не получаем ошибок запроса на перекрестное происхождение.
Затем у нас есть «Simulated Cron», который работает через 5 секунд. Задача — получить обновления с сервера и отправить обновления в Pusher. Затем наше приложение Vue подписывается на Pusher, вытягивает изменения и отображает их.
Наконец, мы сообщаем приложению Node прослушивать порт 8000. Чтобы запустить сервер Node, выполните следующую команду:
$ node server.js
Это запустит сервер Node, и Simulated Cron начнет работу и отправит события в Pusher.
Создание прокси-сервера API
Чтобы получить доступ к нашему серверу API из приложения Vue, мы можем создать прокси в config/index.js и запустить сервер-разработчик и бэкэнд API бок о бок. Все запросы /api в нашем интерфейсном коде будут проксированы на back end сервер.
Откройте config/index.js и внесите следующие изменения:
// config/index.js module.exports = { // ... dev: { // ... proxyTable: { '/api': { target: 'http://localhost:8000', changeOrigin: true, pathRewrite: { '^/api': '' } } }, // ... } }
В proxyTable мы пытаемся пропустить через прокси запросы от /api до localhost:8000.
Использование Pusher в нашем приложении Vue.js
Чтобы использовать Pusher на клиентской стороне нашего приложения, нам нужно подтянуть pusher-js . Выполните следующую команду в терминале:
$ npm install --save pusher-js
Когда установка будет завершена, мы импортируем pusher-js в корневой компонент. В теге script добавьте следующее вверху:
import Pusher from 'pusher-js'
Затем мы инициализируем Pusher с учетными данными приложения с панели инструментов Pusher и подписываемся на канал в created() жизненном цикле. Откройте App.vue и добавьте это в нижнюю часть метода created() в блоке else:
let pusher = new Pusher('PUSHER_APP_KEY', { cluster: 'PUSHER_APP_CLUSTER', encrypted: true }); let channel = pusher.subscribe('price-updates'); channel.bind('coin-updates', data => { this.currentCurrency = { BTC: data.coin.BTC.USD, ETH: data.coin.ETH.USD, LTC: data.coin.LTC.USD } });
В приведенном выше коде мы подписываемся на получение обновлений на канале price-updates. Затем мы привязываемся к событию coin-updates на канале. Когда событие запускается, мы получаем данные и обновляем currentCurrency.
Вот и все. Вы можете создать приложение, выполнив следующую команду:
$ npm run dev
Команда должна открыть Vue PWA в вашем браузере. Чтобы убедиться, что вы получаете обновления, убедитесь, что ваш Node-сервер запущен.
Использование service workers и автономных возможностей
Как бы то ни было, приложение уже функционирует, но не является PWA в истинном смысле этого слова. Поэтому давайте работать над тем, чтобы приложение PWA поддерживало автономное хранилище. Процесс сборки уже автоматически генерирует service worker при создании приложения, поэтому давайте создадим приложение. Для создания приложения выполните следующую команду:
$ npm run build
Эта команда создает папку dist в нашем рабочем каталоге, а также регистрирует новый service worker. Давайте откроем service worker в веб-браузере Chrome.
Мы будем обслуживать это приложение, используя пакет NPM под названием Serve. Для его установки выполните следующую команду:
$ npm i serve -g
Когда установка будет завершена, мы будем использовать пакет для обслуживания приложения. Для обслуживания приложения выполните следующую команду:
$ serve dist
Мы должны получить вывод, который выглядит так:
Если мы перейдем к этому адресу http://localhost:5000 в нашем веб-браузере, мы увидим наше приложение, поскольку это был последний раз, никаких очевидных изменений, кроме того факта, что приложение теперь является PWA.
Мы можем проверить эту функцию, открыв инструменты разработчика браузера и щелкнув вкладку «Приложение». Вот что мы должны увидеть:
Наше приложение зарегистрировало service worker, который кэширует оболочку приложения при первом запуске, благодаря шаблону Vue PWA.
Оболочка приложения (или app shell) относится к локальным ресурсам, которые вашему веб-приложению необходимо загрузить как скелет вашего пользовательского интерфейса (UI). Представьте оболочку вашего приложения, как комплект кода, который вы опубликуете в собственном магазине приложений при создании собственного приложения.
Заключение
В этом уроке мы увидели, как написать простое PWA реального времени с Vue.js, Pusher и Service Workers. Мы также увидели, как кэшировать динамические значения из удаленного API с помощью объекта хранения данных Web Storage API. Существует намного больше возможностей для PWA и Vue, но это хорошее введение до сих пор.
Автор: Neo Ighodaro
Источник: https://medium.com/
Редакция: Команда webformyself.