В этом уроке мы узнаем, как получать и отображать данные по частям с помощью бесконечной прокрутки, а также как интегрировать и использовать плагин meilisearch для поиска книг.
Автор: @tammibriggs
Одновременная выборка больших наборов данных может привести к некоторым негативным последствиям, например, к медленной отрисовке компонентов, что создает плохой пользовательский опыт для посетителей сайта. Чтобы справиться с этим, обычно используются два паттерна, среди которых бесконечная прокрутка, о которой мы расскажем в этом уроке.
- Цель
- Предварительные условия
- Введение в Strapi
- Настройка проекта Strapi
- Создание типов коллекций
- Создание книжного приложения
- Добавление данных в Strapi
- Интеграция Meilisearch
- Получение данных о книгах из Meilisearch
- Реализация функциональности поиска
- Реализация бесконечной прокрутки с помощью API Intersection Observer
- Получение новых данных с помощью Intersection Observer
- Заключение
- Ссылки
Цель
В этом уроке мы создадим книжное приложение с помощью Strapi. Приложение будет сфокусировано на том, как получать и отображать данные кусками с бесконечной прокруткой, а также как интегрировать и использовать плагин meilisearch для поиска книг.
Предварительные условия
Чтобы следовать этому руководству, вы должны быть знакомы с React и в вашей системе должен быть установлен Node.
Введение в Strapi
Strapi — это безголовая система управления контентом (CMS) с открытым исходным кодом, разработанная с использованием Javascript-фреймворка Nodejs, который позволяет быстро проектировать API и может быть доступен из любого клиента (React, Vue и т.д.), предоставляя разработчикам свободу использования их родных инструментов.
Strapi включает в себя удобную страницу администратора, которая предоставляет возможность легкого управления и мониторинга контента. Страница администратора, как и созданный API, может быть настроена в соответствии с нашими потребностями на основе системы плагинов, что является одной из привлекательных особенностей Strapi.
Настройка проекта Strapi
Создание проекта Strapi довольно простое. Как и create-react-app
, Strapi имеет [create-strapi-app](https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/installation/cli.html#creating-a-strapi-project)
.
Выполните следующие команды:
$ mkdir book-app
$ cd book-app
$ npx create-strapi-app@latest book-app-backend --quickstart
Команда выше устанавливает Strapi для нашего приложения со всеми необходимыми зависимостями и создает новую папку book-app-backend
.
После завершения установки будет запущен сервер, который мы можем просмотреть в браузере по указанной ссылке. В нашем браузере мы видим следующую страницу:
Здесь заполните необходимые данные и создайте учетную запись пользователя для доступа к панели управления.
Создание типов коллекций
В типе коллекции мы определим контент, который хотим хранить на Strapi. Для этого сначала нажмите на кнопку Создать свой первый тип коллекции.
На этой странице нажмите на кнопку Создать новый тип коллекции, и мы увидим запрос на ввод отображаемого имени и другой информации:
Введите Books в качестве отображаемого имени и нажмите кнопку Continue. В следующем окне мы увидим различные типы полей, которые мы можем создать для нашей коллекции.
Здесь мы создадим поля для книг. У каждой книги будут авторы, описание, изображение, previewLink, publishDate, издатель, подзаголовок и название. Это поля, которые будет включать наш тип коллекции Books. Все поля будут текстовыми, за исключением авторов, которые будут иметь тип JSON.
Выше перечислены все поля для данных книги. После создания нажмите на кнопку Save в правом верхнем углу страницы.
Создание книжного приложения
Книжное приложение будет иметь главную страницу, на которой будут отображаться все книги, доступные в нашей коллекции Strapi, которые мы будем получать частями, используя бесконечную прокрутку. У нас будет страница подробностей для отображения информации о конкретной книге и страница поиска, на которой будут отображаться результаты поиска, полученные от Meilisearch.
Я уже создал stater repo для книжного приложения с шаблоном, который мы будем использовать, и данными о книгах для добавления Strapi, которые я получал из Google book API.
Далее нам нужно клонировать стартовое репо на GitHub. В терминале перейдите в каталог book-app, который мы создали ранее, и введите следующие строки кода:
$ git clone -b starter https://github.com/Tammibriggs/strapi-book-app.git
$ cd strapi-book-app
$ npm install
Теперь, когда мы запустим наше приложение командой $ npm start
, мы увидим эту страницу:
Если мы нажмем на книгу, мы попадем на страницу подробностей, которая выглядит следующим образом:
Сейчас мы получаем данные о книгах из файла data.js в каталоге src клонированного приложения. В ближайшее время мы перенесем данные о книгах в Strapi и получим их оттуда с помощью meilisearch, а также реализуем бесконечную прокрутку с помощью Intersection Observer API.
Добавление данных в Strapi
В файле data.js в каталоге src у нас есть более пятидесяти четырех (54) данных о книгах; давайте перенесем их в Strapi. Для этого нам нужно сначала разрешить доступ к коллекции Strapi. Перейдите на приборную панель в Настройки на боковой панели. Выберите Роли в разделе Пользователи и разрешения. Нажмите на Public, выберите Book и установите все флажки.
Затем нажмите на кнопку Сохранить в правом верхнем углу, чтобы сохранить эти изменения.
Далее в src/pages/Home.js добавьте следующий импорт:
import axios from 'axios'
Мы можем импортировать axios здесь, потому что он был включен в стартовое приложение. Далее добавьте следующие строки кода после состояния books
в компоненте Home
:
// src/pages/Home.js
const URL = "http://localhost:1337/api/books"
useEffect(() => {
sendData()
}, [])
const sendData = async () => {
let fetchedData;
const fetchCol = await axios.get(URL)
fetchedData = fetchCol.data.data
if (!fetchedData.length) {
console.log('done')
try {
books.forEach((book) => {
axios.post(URL,{
data: {
authors: book.authors,
description: book.description,
image: book.image,
previewLink: book.previewLink,
publishDate: book.publishDate,
publisher: book.publisher,
subtitle: book.subtitle,
title: book.title,
}})
})
console.log('done')
} catch (error) {
console.log(error);
}
} else {
console.log("data already uploadedd")
}
}
Приведенный выше код проверяет, есть ли данные в нашей коллекции Strapi, и если их нет, он заполняет нашу коллекцию всеми данными из файла data.js.
Теперь, когда мы переходим на нашу приборную панель Strapi и нажимаем на Content Manager в боковой панели, мы видим пятьдесят четыре (54) записи в нашей коллекции Books.
Далее мы интегрируем и используем meilisearch для получения данных о книгах из коллекции Strapi и их отображения, а также реализуем функцию поиска. Для поиска данных meilisearch использует переданный ему запрос. Если запрос пуст, он вернет все книги из нашей коллекции, которые мы отобразим на главной странице, а если запрос не пуст, он вернет соответствующий результат.
Интеграция Meilisearch
Чтобы использовать Meilisearch локально, мы загрузим и запустим его экземпляр. Загрузить его можно здесь. Открыв загруженное приложение, мы увидим терминал с экземпляром Meilisearch, размещенным на локальном хосте:
Если мы перейдем в браузере по указанному URL, то увидим интерфейс Meilisearch.
Далее, в другом терминале заходим в директорию book-app-backend и устанавливаем плагин Strapi-meilisearch
следующей командой:
$ npm install strapi-plugin-meilisearch
После этого мы повторно запускаем npm run develop
, чтобы пересобрать наше приложение Strapi с новым плагином meilisearch
. Когда мы откроем URL-адрес localhost в нашем браузере и войдем в систему, мы будем направлены на приборную панель Strapi:
Далее нажмем на опцию meilisearch
на боковой панели и во вкладке Settings введем URL для экземпляра meilisearch.
Нажмите на кнопку сохранить. Теперь добавим коллекцию книг в meilisearch в разделе Collections, установив флажок:
Таким образом, при обновлении экземпляра meilisearch мы увидим записи в нашей коллекции Strapi.
Получение данных о книгах из Meilisearch
Чтобы получить данные о книгах в нашем фронтенде, мы можем либо воспользоваться предоставленными нам маршрутами поиска (например, это позволит получить данные о 30 книгах: http://127.0.0.1:7700/indexes/book/search?limit=30), либо использовать пакет meilisearch. В этом руководстве мы будем использовать этот пакет, поэтому сначала нам нужно будет установить его.
- В терминале перейдите в папку strapi-book-app и введите следующую команду:
$ npm install meilisearch
- Затем добавьте следующий импорт в файл Home.js в src/pages:
import MeiliSearch from "meilisearch";
- Далее измените состояние книги, заменив Allbooks пустым массивом. Это должно выглядеть следующим образом:
const [books, setBooks] = useState([])
- Теперь добавьте следующие строки кода после состояния
books
:
// src/pages/Home.js
const fetchData = async () => {
const client = new MeiliSearch({
host: 'http://127.0.0.1:7700',
})
const index = await client.getIndex('book')
const booksData = await index.search('*')
setBooks(booksData.hits)
}
Вышеприведенная функция при вызове возвращает наши данные из коллекции книг Strapi, полученные через экземпляр meilisearch
. Обратите внимание, что в методе search
мы передаем ***** в качестве запроса. Это позволит получить все наши данные с ограничением в двадцать (20), которое установлено по умолчанию. Это можно настроить.
Для поиска определенных данных нам просто нужно передать их в метод search
. Мы будем использовать его для реализации нашей функции поиска.
Мы хотим, чтобы вышеуказанная функция вызывалась при рендеринге нашего приложения, поэтому мы вызовем ее в хуке useEffect
. В компоненте Home
измените хук useEffect
с функцией sendData()
, чтобы он выглядел следующим образом:
// src/pages/Home.js
useEffect(() => {
fetchData()
sendData()
}, [])
Таким образом, данные из нашей коллекции книг Strapi теперь должны отображаться в нашем приложении. Далее давайте убедимся, что при нажатии на карточку книги мы получаем подробную информацию об этой конкретной книге.
Для этого,
- Перейдите в src/pages/BookDetail.js и сначала добавьте следующий импорт:
import MeiliSearch from 'meilisearch'
- Затем измените хук
useEffect
, чтобы он выглядел следующим образом:
// src/pages/BookDetail.js
useEffect(() => {
const fetchData = async () => {
const client = new MeiliSearch({
host: 'http://127.0.0.1:7700',
})
const index = await client.getIndex('book')
const bookData = await index.getDocument(params.id)
setBook(bookData)
}
fetchData()
}, [])
Таким образом, при нажатии на книгу мы увидим подробную информацию о ней.
Реализация функциональности поиска
Для функциональности поиска, когда мы вводим запрос в строке поиска, он переводит нас на страницу поиска, прикрепляя запрос к URL. Мы получим этот запрос и передадим его в метод поиска meilisearch, который затем вернет соответствующие результаты:
Чтобы сделать это,
- Перейдите в src/pages/Search.js и сначала добавьте следующие импорты:
// src/pages/Search.js
import MeiliSearch from 'meilisearch'
import {useEffect, useState} from 'react'
- Затем добавьте следующие строки кода после переменной
params
в компонентеSearch
:
// src/pages/Search.js
const [books, setBooks] = useState([])
useEffect(() => {
const fetchData = async () => {
const client = new MeiliSearch({
host: 'http://127.0.0.1:7700',
})
const index = await client.getIndex('book')
const booksData = await index.search(params.query)
setBooks(booksData.hits)
}
fetchData()
}, [params.query])
Приведенный выше код вернет все подходящие результаты на основе поискового запроса и установит их в состояние books
. Теперь давайте отобразим найденные результаты.
- Измените div в операторе возврата, чтобы теперь он выглядел следующим образом:
// src/pages/Search.js
<div className='searchPage wrapper'>
<div className='books'>
{books?.map((book) => (
<Book
key={book.id}
title={book.title}
image={book.image}
authors={book.authors}
publisher={book.publisher}
publishDate={book.publishedDate}
id={book.id}
/>
))}
</div>
</div>
Таким образом, при поиске книг в строке поиска мы увидим результаты на странице поиска.
Реализация бесконечной прокрутки с помощью API Intersection Observer
Для функциональности бесконечной прокрутки мы ограничим количество возвращаемых книг из meilisearch пятнадцатью, а затем, когда мы прокрутим страницу до самого низа, мы получим и добавим еще пятнадцать данных.
Для этого мы воспользуемся Intersection Observer API, чтобы узнать, когда мы достигли нижней части страницы, затем для возврата книг по пятнадцать штук из meilisearch мы воспользуемся параметрами limit и offset, которые можно указать в объекте, передаваемом в качестве второго параметра метода search
.
Получение новых данных с помощью Intersection Observer
API Intersection Observer отслеживает, когда наблюдаемый элемент становится видимым или когда он достигает заданной позиции, а затем запускает функцию обратного вызова, переданную ему. Для использования этого API мы сначала создадим элемент в нижней части получаемых данных, который будет наблюдаемым элементом. Затем, когда этот элемент станет видимым, мы вызовем функцию обратного вызова, которая будет отвечать за получение и ожидание новых данных о книге.
- В файле Home.js добавьте следующие импорты:
import {useRef, useCallback} from 'react'
- После этого добавьте следующие строки кода после закрывающего тега (
</div>
)div
с className books.
// src/pages/Home.js
<div className='loader' ref={observerElem}>
{books.length !== 0 &&
<span>{hasNextPage ? 'Loading...' : 'no books left'}</span>
}
</div>
Выше мы создали элемент div
, который хотим наблюдать с помощью Intersection Observers. Мы добавили атрибут ref
, чтобы иметь возможность обращаться к нему напрямую. Приведенный выше div
будет отображать **Загрузку…* или отсутствие книг *** в зависимости от hasNextPage
, который будет булевым состоянием, которое будет истинным или ложным в зависимости от того, есть ли еще данные для извлечения.
- Далее, добавьте следующую строку кода после переменной
URL
:
// src/pages/Home.js
const observerElem = useRef(null)
const [hasNextPage, setHasNextPage] = useState(true)
- Далее, добавьте следующие строки кода после состояния
hasNextPage
:
// src/pages/Home.js
const handleObserver = useCallback((entries) => {
const [target] = entries
if(target.isIntersecting && hasNextPage) {
console.log('intersected')
}
}, [hasNextPage])
useEffect(() => {
const element = observerElem.current
const option = { threshold: 0 }
const observer = new IntersectionObserver(handleObserver, option);
observer.observe(element)
return () => observer.unobserve(element)
}, [hasNextPage, handleObserver])
Приведенный выше код определит, когда наблюдаемый элемент вошел в область просмотра, и вызовет функцию обратного вызова handleObserver
. Теперь, когда мы прокрутим страницу в браузере до самого низа и проверим в консоли **intersected* будет записан в лог.*.
Далее создадим функцию, которая будет получать и добавлять данные о книге всякий раз, когда мы прокручиваем страницу в самый низ. Для этого мы изменим функцию fetchData
так, чтобы она получала пятнадцать новых данных о книгах при каждом вызове, используя параметр limit и offset, а затем мы будем добавлять новые найденные книги в состояние books
.
Для этого,
- Во-первых, добавьте следующий код после состояния
hasNextPage
:
// src/pages/Home.js
const [offset, setOffset] = useState(0)
const [lastPage, setLastPage] = useState({})
- Затем измените функцию fetchData, чтобы она выглядела следующим образом:
// src/pages/Home.js
const fetchData = async () => {
const client = new MeiliSearch({
host: 'http://127.0.0.1:7700',
})
const index = await client.getIndex('book')
const booksData = await index.search('*', {
limit: 15,
offset: offset
})
setBooks([...books, ...booksData.hits])
setLastPage(booksData)
}
Далее нам нужно вызвать функцию fetchData()
в handleObserver
, чтобы при прокрутке страницы в самый низ она вызывалась.
- Измените функцию
handleObserver
так, чтобы она теперь выглядела следующим образом:
// src/pages/Home.js
const handleObserver = useCallback((entries) => {
const [target] = entries
if(target.isIntersecting && hasNextPage) {
fetchData()
}
}, [fetchData, hasNextPage])
- Наконец, добавьте следующую строку кода после функции
fetchData
:
// src/pages/Home.js
useEffect(() => {
setOffset(books.length)
if(books.length < lastPage.nbHits){
setHasNextPage(true)
}else{
setHasNextPage(false)
}
}, [books])
На этом мы закончили реализацию функции бесконечной прокрутки. Когда мы прокрутим страницу до самого низа, будут отображаться новые книги.
Заключение
В этом руководстве мы узнали, как реализовать бесконечную прокрутку и функцию поиска в Strapi с помощью meilisearch, создав приложение для книг.
Ссылки
-
Ознакомьтесь с документацией по Meilisearch.
-
Нажмите здесь, чтобы просмотреть весь исходный код этого урока.