Получение данных из стороннего API с помощью Vue.js и Axios

Чтобы следовать этому руководству, вам потребуются установленные на вашем компьютере Программирование и разработка

Чаще всего при создании приложения JavaScript вы хотите получать данные из удалённого источника или использовать API. Есть много интересных вещей, которые можно сделать с данными из ряда общедоступных API.

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

Я продемонстрирую, как создать простое новостное приложение, которое будет показывать самые популярные новостные статьи дня и позволит пользователям фильтровать их по интересующей их категории, получая данные из New York Times API.

Вот как будет выглядеть окончательное приложение:

Чтобы следовать этому руководству, вам потребуются установленные на вашем компьютере

Чтобы следовать этому руководству, вам потребуются установленные на вашем компьютере Node.js и (необязательно) Yarn. Чтобы установить Node, вы можете либо перейти на официальную страницу загрузки и загрузить двоичные файлы Node для своей системы, либо вместо этого использовать диспетчер версий.

После установки Node, чтобы получить Yarn, запустите:

npm i -g yarn

Вам также потребуются базовые знания Vue.js. Здесь вы можете найти отличное руководство по началу работы.

Получить ключ API

Чтобы использовать API NYTimes, вам потребуется ключ API. Так что, если у вас его ещё нет, перейдите на их страницу регистрации и зарегистрируйтесь, чтобы получить ключ API для Top Stories API.

Чтобы использовать API NYTimes, вам потребуется ключ API

Мы будем использовать конечную точку API главных историй для извлечения данных из. Обратите внимание, что этот API содержит несколько разделов, таких как «дом», «путешествия», «искусство» и «наука». Нам нужно будет создать фильтр, который позволит пользователям выбирать раздел и загружать в него истории.

Ниже приведены примеры вызовов:

https://api.nytimes.com/svc/topstories/v2/arts.json?api-key=yourkey
https://api.nytimes.com/svc/topstories/v2/home.json?api-key=yourkey
https://api.nytimes.com/svc/topstories/v2/science.json?api-key=yourkey
https://api.nytimes.com/svc/topstories/v2/us.json?api-key=yourkey
https://api.nytimes.com/svc/topstories/v2/world.json?api-key=yourkey

Не стесняйтесь использовать свой любимый клиент REST (например, Hoppscotch или Insomnia ) для тестирования вызовов API.

Не стесняйтесь использовать свой любимый клиент REST

Структура проекта

Давайте быстро развернём проект Vue 3 с помощью Vite, сервера разработки, который работает быстрее, чем Vue CLI :

yarn create @vitejs/app vue-news-app --template vue

# Install package dependencies
cd vue-news-app
yarn install

# Confirm app can run
yarn dev

Откройте localhost:3000в своём браузере. У вас должно получиться следующее представление:

Затем давайте установим фреймворк TailwindCSS

Затем давайте установим фреймворк TailwindCSS, чтобы обеспечить базовый стиль. Вам нужно будет остановить сервер, чтобы выполнить это действие:

yarn add -D tailwindcss@latest postcss@latest autoprefixer@latest

# Generate tailwind.config.js and postcss.config.js files
npx tailwindcss init -p

Нам понадобятся дополнительные служебные программы пакета, которые помогут нам отформатировать даты и ограничить количество строк для abstractполя:

yarn add @tailwindcss/line-clamp date-fns

@tailwindcss/line-clampэто плагин, который необходимо включить в tailwind.config.js. Ниже представлена ​​полная конфигурация:

module.exports = {
  purge: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [require("@tailwindcss/line-clamp")],
}

Затем создайте index.css файл в src папке и добавьте этот код:

@tailwind base;
@tailwind components;
@tailwind utilities;

body {
  @apply antialiased text-green-900 bg-green-50;
  font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS", sans-serif;
}

#app {
  @apply flex flex-col min-h-screen overflow-x-hidden;
}

Помимо импорта необходимых классов CSS Tailwind, мы включили несколько настроек CSS, которые помогут нам определить тему по умолчанию для нашего приложения. Мы также внедрили систему гибкого макета, чтобы помочь нам создать липкий верхний и нижний колонтитулы для нашего приложения.

Нам нужно будет импортировать index.css в src/main.js:

import { createApp } from "vue"
import App from "./App.vue"
import "./index.css"

createApp(App).mount("#app")

Теперь давайте продолжим и определим макет нашего приложения. Сначала удалите все существующие компоненты в src/components. Затем в той же папке создайте эти три файла:

  • Layout.vue
  • Header.vue
  • Footer.vue

Скопируйте следующий код для каждого файла:

src / components / Footer.vue :

<template>
  <footer
    class="px-4 py-8 text-sm font-bold text-center text-green-100 bg-green-900">
    <p class="text-sm tracking-wide">Copyright (c) 2021 SitePoint</p>
  </footer>
</template>

src / components / Header.vue :

<template>
  <header class="flex justify-center py-6 bg-green-900 place-items-center">
    <img alt="Vue logo" src="../assets/logo.png" width="32" />
    <span class="ml-4 text-lg font-bold text-green-100 md:text-xl">
      Vue News | NYTimes Edition
    </span>
  </header>
</template>

src / components / Layout.vue :

<template>
  <Header />
  <main class="container flex-grow px-4 mx-auto my-12">
    <slot />
  </main>
  <Footer />
</template>

<script>
import Header from "./Header.vue"
import Footer from "./Footer.vue"

export default {
  components: {
    Header,
    Footer,
  },
}
</script>

Наконец, обновите src/App.vue:

<template>
  <Layout>
    <p>Main content goes here</p>
  </Layout>
</template>

<script>
import Layout from "./components/Layout.vue"

export default {
  components: {
    Layout,
  },
}
</script>

Выполнить yarn dev. Браузер должен обновиться автоматически.

Выполнить yarn dev

После завершения макета приложения мы можем приступить к построению основной логики нашего новостного приложения.

Создание компонентов новостного приложения

Структура нашего приложения будет состоять из трёх компонентов News и одного контейнера src/App.vue. Контейнер будет отвечать за выборку данных публикации и заполнение компонентов.

Во-первых, нам нужно разработать макет и найти эти компоненты. Следовательно, для начала нам нужны фиктивные данные. Создайте файл src/posts.json и заполните его следующими данными:

{
  "posts": [
    {
      "title": "Stay Healthy When Exercising Outdoors",
      "abstract": "Cold weather workouts do bring unique risks, but a little planning and preparation can help whether you’re going for a winter walk, trekking in snowshoes or sledding with the kids.",
      "url": "https://www.nytimes.com/2021/02/06/at-home/exercise-outdoors-cold-weather.html",
      "byline": "By Kelly DiNardo",
      "published_date": "2021-02-06T23:40:05-05:00",
      "thumbnail": "https://static01.nyt.com/images/2021/02/07/multimedia/07ah-OUTDOOREXERCISE/07ah-OUTDOOREXERCISE-mediumThreeByTwo210.jpg",
      "caption": ""
    },
    {
      "title": "4 Skiers Killed in Avalanche in Utah, Officials Say",
      "abstract": "It was the third such deadly episode in days and the deadliest avalanche in the United States since May 2014, according to the authorities.",
      "url": "https://www.nytimes.com/2021/02/06/us/avalanche-salt-lake-city.html",
      "byline": "By Michael Levenson",
      "published_date": "2021-02-06T20:22:39-05:00",
      "thumbnail": "https://static01.nyt.com/images/2021/02/06/lens/06xp-avalanche-photo2/06xp-avalanche-photo2-mediumThreeByTwo210.jpg",
      "caption": "A helicopter returning to Millcreek Canyon after rescuing one of the four avalanche survivors on Saturday."
    }
  ]
}

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

Теперь приступим к созданию наших компонентов новостей. В src/components папке создайте следующие файлы:

  • NewsCard.vue
  • NewsList.vue
  • NewsFilter.vue

Просто чтобы визуализировать, как все эти компоненты объединяются, импортируйте их src/App.vue и расположите следующим образом:

<template>
  <Layout>
    <h2 class="mb-8 text-4xl font-bold text-center capitalize">
      News Section : <span class="text-green-700">{{ section }}</span>
    </h2>
    <NewsFilter v-model="section" />
    <NewsList :posts="posts" />
  </Layout>
</template>

<script>
import Layout from "./components/Layout.vue"
import NewsFilter from "./components/NewsFilter.vue"
import NewsList from "./components/NewsList.vue"

import data from "./posts.json"

export default {
  components: {
    Layout,
    NewsFilter,
    NewsList,
  },
  data() {
    return {
      section: "home",
      posts: data.posts,
    }
  },
}
</script>

Разберём приведённый выше код:

  • headerТег где отображается текущее значение состояния section.
  • NewsFilter Компонент будет содержать выпадающий вход для пользователей, чтобы выбрать другой раздел. Там будет кнопка, которую им нужно будет нажать, чтобы выполнить выборку. Мы привязали компонент к состоянию, section чтобы разрешить синхронизацию состояния.
  • NewsListКомпонент будет отображать сообщения с помощью NewsCard компонента через отзывчивую сетку.

Теперь приступим к работе с каждым отдельным компонентом новостей. NewsCard.vue Компонент будет представлять данные для одного поста. Требуется одна опора post:

<template>
  <section class="p-4 rounded-lg shadow-lg bg-gray-50 w-80">
    <div class="h-96">
      <a
        class="text-xl font-bold text-center text-green-800 hover:text-green-600 hover:underline"
        :href="post.url"
        target="_blank"
        rel="noreferrer"
      >
        {{ post.title }}
      </a>
      <img
        class="w-full mt-2 rounded"
        :src="post.thumbnail"
        :alt="post.caption"
        height="140"
        width="210"
      />
      <p class="mt-2 text-justify text-gray-700 line-clamp-4">
        {{ post.abstract }}
      </p>
    </div>
    <div>
      <p class="mt-4 font-bold text-gray-600">{{ post.byline }}</p>
      <p class="font-light text-gray-600">
        {{ formatDate(post.published_date) }}
      </p>
    </div>
  </section>
</template>

<script>
import { format } from "date-fns"

export default {
  props: {
    post: {
      type: Object,
      required: true,
    },
  },
  methods: {
    formatDate(strDate) {
      return format(new Date(strDate), "MMMM do, yyyy")
    },
  },
}
</script>

Он NewsList.vue будет проходить через массив сообщений и заполнять NewsCards адаптивную сетку:

<template>
  <div
    class="grid grid-cols-1 gap-6 mt-4 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 justify-items-center"
  >
    <NewsCard v-for="(post, index) in posts" :key="index" :post="post" />
  </div>
</template>

<script>
import NewsCard from "./NewsCard.vue"
export default {
  props: {
    posts: {
      type: Array,
      required: true,
    },
  },
  components: {
    NewsCard,
  },
}
</script>

Далее у нас есть NewsFilter компонент, который позволит пользователям загружать сообщения из разных разделов. Во-первых, нам нужен файл содержимого для хранения всех разделов, поддерживаемых конечной точкой Top Stories API. Создайте файл src/components/sections.js:

const sections = [
  "home",
  "arts",
  "automobiles",
  "books",
  "business",
  "fashion",
  "food",
  "health",
  "insider",
  "magazine",
  "movies",
  "nyregion",
  "obituaries",
  "opinion",
  "politics",
  "realestate",
  "science",
  "sports",
  "sundayreview",
  "technology",
  "theater",
  "magazine",
  "travel",
  "upshot",
  "us",
  "world",
]

export default sections

Давайте теперь создадим наш NewsFilter.vue, который содержит раскрывающийся список ввода и кнопку. Нам нужно будет использовать v-model для привязки состояния section таким образом, чтобы оно синхронизировалось с состоянием в App.vue:

<template>
  <div class="flex justify-center p-4 rounded">
    <!-- Start of select dropdown -->
    <div class="relative inline-flex">
      <svg
        class="absolute top-0 right-0 w-2 h-2 m-4 pointer-events-none"
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 412 232"
      >
        <path
          d="M206 171.144L42.678 7.822c-9.763-9.763-25.592-9.763-35.355 0-9.763 9.764-9.763 25.592 0 35.355l181 181c4.88 4.882 11.279 7.323 17.677 7.323s12.796-2.441 17.678-7.322l181-181c9.763-9.764 9.763-25.592 0-35.355-9.763-9.763-25.592-9.763-35.355 0L206 171.144z"
          fill="#648299"
          fill-rule="nonzero"
        />
      </svg>
      <select
        class="h-10 pl-5 pr-10 text-gray-600 bg-white border border-gray-300 rounded-lg appearance-none hover:border-gray-400 focus:outline-none"
        v-model="section"
      >
        <option
          v-for="(section, index) in sections"
          :key="index"
          :value="section"
        >
          {{ capitalize(section) }}
        </option>
      </select>
    </div>
    <!-- End of select dropdown -->
    <div class="self-center ml-8">
      <button
        class="px-6 py-2 text-white bg-green-700 rounded hover:bg-green-900"
      >
        Retrieve
      </button>
    </div>
  </div>
</template>

<script>
import { computed } from "vue"
import sectionsData from "./sections"

export default {
  props: {
    modelValue: String,
  },
  setup(props, { emit }) {
    const section = computed({
      get: () => props.modelValue,
      set: value => emit("update:modelValue", value),
    })

    return {
      section,
    }
  },
  data() {
    return {
      sections: sectionsData,
    }
  },
  methods: {
    capitalize(value) {
      if (!value) return ""
      value = value.toString()
      return value.charAt(0).toUpperCase() + value.slice(1)
    },
  },
}
</script>

Статья » Vue 3: Data down, Events up » лучше всего объясняет стратегию, которую мы используем для привязки section состояния к NewsFilter компоненту. По сути, это позволяет дочерним компонентам обновлять реквизиты и синхронизироваться с родительским компонентом.

Ниже приведён снимок экрана текущего состояния приложения:

Ниже приведён снимок экрана текущего состояния приложения

Использование Axios для получения удалённых данных

Axios — это HTTP-клиент, основанный на обещаниях, для выполнения запросов Ajax, который отлично подходит для наших целей. Он предоставляет простой и богатый API. Он очень похож на fetch API, но без необходимости добавления полифилла для старых браузеров и некоторых других тонкостей.

Чтобы установить axios, запустите:

yarn add axios

Разработка пользовательского интерфейса нашего приложения завершена. Теперь нам нужно только реализовать логику удалённой выборки. Ниже приведён пример полного формата URL, ожидаемого службой NYTimes API:

https://api.nytimes.com/svc/topstories/v2/home.json?api-key=your_api_key

Во-первых, давайте сохраним наш ключ API в.env файле в корне нашего проекта. Сохраните в следующем формате:

VITE_NYT_API_KEY=####

Замените хэши своим фактическим ключом API.

Поскольку мы используем Vite, нам необходимо соблюдать руководство Vite по загрузке переменных среды. Vue / CLI имеет свои собственные инструкции для того же.

Давайте теперь реализуем логику, которая будет получать фактические сообщения из конечной точки NYTimes REST API. Просто обновите src/App.vue соответственно:

<template>
  <Layout>
    <h2 class="mb-8 text-4xl font-bold text-center capitalize">
      News Section : <span class="text-green-700">{{ section }}</span>
    </h2>
    <NewsFilter v-model="section" :fetch="fetchNews" />
    <NewsList :posts="posts" />
  </Layout>
</template>

<script>
import Layout from "./components/Layout.vue"
import NewsFilter from "./components/NewsFilter.vue"
import NewsList from "./components/NewsList.vue"

import axios from "axios"
const api = import.meta.env.VITE_NYT_API_KEY

export default {
  components: {
    Layout,
    NewsFilter,
    NewsList,
  },
  data() {
    return {
      section: "home",
      posts: [],
    }
  },
  methods: {
    // Helper function for extracting a nested image object
    extractImage(post) {
      const defaultImg = {
        url: "http://placehold.it/210x140?text=N/A",
        caption: post.title,
      }
      if (!post.multimedia) {
        return defaultImg
      }
      let imgObj = post.multimedia.find(
        media => media.format === "mediumThreeByTwo210"
      )
      return imgObj ? imgObj : defaultImg
    },
    async fetchNews() {
      try {
        const url = `https://api.nytimes.com/svc/topstories/v2/${this.section}.json?api-key=${api}`
        const response = await axios.get(url)
        const results = response.data.results
        this.posts = results.map(post => ({
          title: post.title,
          abstract: post.abstract,
          url: post.url,
          thumbnail: this.extractImage(post).url,
          caption: this.extractImage(post).caption,
          byline: post.byline,
          published_date: post.published_date,
        }))
      } catch (err) {
        if (err.response) {
          // client received an error response (5xx, 4xx)
          console.log("Server Error:", err)
        } else if (err.request) {
          // client never received a response, or request never left
          console.log("Network Error:", err)
        } else {
          console.log("Client Error:", err)
        }
      }
    },
  },
  mounted() {
    this.fetchNews()
  },
}
</script>

Здесь мы создали функцию с именем, fetchNews которая содержит логику для выполнения логики выборки. Эта функция будет вызываться из двух мест:

  • mounted()Событие жизненного цикла
  • NewsFilter компонент

Давайте разберём функцию, чтобы убедиться, что мы понимаем, что происходит:

  • Мы используем синтаксис async, поскольку он чище, чем обычный Promiseсинтаксис обратного вызова.
  • Поскольку мы собираемся выполнить сетевой вызов, многое может пойти не так. Мы заключили код функции в try…catchблок. В противном случае пользователи столкнутся с неописательной ошибкой Promise, если таковая произойдёт.
  • Используя литералы шаблонов ES6, мы можем создать строку URL-адреса, которая автоматически обновляется всякий раз, когда пользователь изменяет новости section через
  • NewsFilter компонент. Обратите внимание, что ключ API также включён в строку URL.
  • После получения результатов с помощью axios.get()функции нам необходимо проанализировать результаты и отформатировать их таким образом, чтобы это было совместимо с нашим пользовательским интерфейсом, в частности с NewsCardкомпонентом. Мы делаем это с помощью Array.mapфункции JavaScript, чтобы создать новый массив с нашими отформатированными данными.
  • Извлечь данные изображения немного сложно. В некоторых сообщениях отсутствует multimediaполе, и даже если оно есть, нет гарантии, что нужный нам медиаформат присутствует. В таком случае мы возвращаем URL-адрес изображения по умолчанию — http://placehold.it/210×140?text=N/Aи используем заголовок сообщения в качестве подписи.
  • В блоке ошибок мы проверяем наличие определенных свойств ошибки, чтобы определить, какой тип ошибки произошёл. Вы можете использовать эту информацию, чтобы составить полезное сообщение об ошибке.

Теперь взгляните на раздел шаблонов и обратите внимание, что мы включили новую опору с именем fetch, которая ссылается на fetchNews функцию. Нам нужно обновить, src/components/NewsFilter.vue чтобы принять эту опору. Ниже я выделил только те разделы кода, которые вам следует изменить:

<template>
  ...
  <button
    class="px-6 py-2 text-white bg-green-700 rounded hover:bg-green-900"
    @click="fetch"
  >
    Retrieve
  </button>
  ...
</template>

<script>
export default {
  props: {
    modelValue: String,
    fetch: Function,
  },
}
</script>

Возможно, вам потребуется перезапустить сервер разработки, чтобы правильно загрузить библиотеку axios и ключ API. Как только вы это сделаете, у вас должны появиться актуальные сообщения. Ниже показано текущее состояние приложения.

У вас должна быть возможность переключать и загружать разные разделы новостей

У вас должна быть возможность переключать и загружать разные разделы новостей.

Вывод

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

Теперь у нас есть работающее приложение Vue.js 3.0, построенное на базе службы API. Есть множество улучшений, которые можно сделать, подключив некоторые другие API. Например, мы могли бы:

Автоматически ставить в очередь публикации в социальных сетях из категории с помощью Buffer API.
Отметьте сообщения для последующего чтения с помощью Pocket API.API.

Читайте также:  5 лучших IDE для программирования на С и С++
Оцените статью
bestprogrammer.ru
Добавить комментарий