Дипломная работа: Разработка сервиса для свободного обмена одеждой

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Nodemon: полезная утилита для разработки, которая позволяет запускать и обновлять сервер автоматически, когда в файлы вносятся изменения.

Логика приложения разделена: выделена папка Controllers, в которой хранятся файлы, отвечающие за манипуляцию с данными, логику обработки данных во всем приложении.

Маршруты (папка routes) содержат адреса конечных точек (endpoints), и обращения к необходимым функциям, реализованных в контроллерах [16]. Для считывания или изменения данных используется экспортированная mongoose Schema той коллекции, с которой необходимо работать. На рисунке12 изображена схема взаимодействия внутри сервера, когда приходит запрос с клиентской части.

Рис. 12. Схема обработки запроса в серверной части приложения

Рассмотрим взаимодействие на примере получения данных обо всех товарах пользователя.

Шаг 1. Отправка данных с клиентской части

Для управления состоянием приложения в клиентской части используется Redux и Redux-thunk. Получение данных - это как раз thunkфункция. В ней выполняется GET-запрос по адресу `/api/items/:id'. Ранее в настройках axiosбыл указан параметр «BaseURL: `http:/localhost:5000'» поэтому можно указать только относительный путь.

Синтаксис ES6 позволяет использовать интерполяцию, и вставлять значение параметра прямо в строку, используя конструкцию ${<Параметр>}.

export const getAllMine = (userId: string) => (

dispatch: Dispatch<AppActionType>

) => {

dispatch(sendLoading());

axios

.get(`/api/items/${userId}`)

.then((res) => dispatch(sendItems(res.data)))

.catch((error) => dispatch(sendErrors(error.response.data)));

};

Шаг 2. Поиск конечной точки по маршруту

В папке «api/routes/» находится файл «items.js», где с помощью объекта router из библиотеки Express.jsможно получить доступ к функции контроллера«.getAllMine()».

const express = require('express');

const router = express.Router();

const items = require('../../controllers/items.controller');

//@route GET api/items/:id

//@desc получаем все товары пользователя

router.get('/:id', items.getAllMine);

Также, для того, чтобы приложение имело доступ к этим маршрутам, необходимо экспортировать их в главный файл «server.js», и вызвать команду «app.use()».

const items = require('./routes/api/items');

const app = express();

app.use('/api/items', items);

Шаг 3. Вызов функции в контроллере

Функция «.getAllMine()» - асинхронная. Реализация функции может быть на основе промисов (как в примере ниже), так и используя ключевые слова «async»,«await». Функция принимает до трех параметров: первый параметр - это объект запроса, второй параметр - объект ответа, который отправится на клиентскую сторону, третий параметр - это функция «.next()», которая может указывать, что выполнение еще не закончено, и требуется вызвать следующую функцию.

//найти товары пользователя

exports.getAllMine = (req, res) => {

const userId = req.params.id;

Items.findOne({ userId })

.then((items) => {

if (!items) {

return res.status(400).send('Ошибка пользователя нет в базе товаров!');

} else {

if (items.items) {

return res.status(200).send(items.items);

} else

return res

.status(400)

.send(

'Пока у вас нет товаров, чтобы обменяться! Добавьте новый товар!'

);

}

})

.catch((err) => {

return res.status(500).send(err);

});

Шаг 4. Возвращение результата запроса

Возвращение результата происходит внутри потребителя «.then()». Сначала для результата устанавливается HTTP-статус, а затем с помощью метода «.send()» отправляются необходимые данные. При этом «.send()» принимает как JSONтак и обычные типы JavaScript.

Подключение к базе данных и настройка удаленного сервера

Необходимо иметь MongoDBна компьютере, на котором ведется разработка. Установочный дистрибутив MongoDB«весит» 263МБ для операционной системы Windows 10.

Сама база данных будет развернута на удаленном сервере с помощью облачного сервиса MongoDBAtlas. В рамках бесплатного тарифа M0 MongoDBAtlasпредоставляет 500 мегабайт для хранения запросов и коллекций документов. Подключиться к удаленной базе данных можно и через локальный клиент MongoDBCompass. Для того, чтобы можно было получить доступ к удаленной базе данных необходимо добавить IPадрес в список разрешенныхIPадресов, создать пользователя и назначить ему соответствующие права доступа. Затем получить строку подключения MongoURI. Эту строку можно использовать в приложении.

База данных подключена к серверной части, через строку подключения (MongoURI) и метод «.connect()», который принимает MongoURI (для удобства MongoURI вынесен в отдельный модуль). Затем можно указать дополнительные параметры.

const mongoose = require('mongoose');

const db = require('./config/keys').mongoURI;

//db подключение

mongoose

.connect(db, {

useNewUrlParser: true,

useUnifiedTopology: true,

useFindAndModify: false

})

.then(() => console.log('Подключено к базе!'))

.catch((err) => {

console.log('Подключение невозможно');

console.log(err);

process.exit();

});

Разработка клиентской части сервиса

В этом разделе описано, почему выбран функциональный подход к разработке компонентов React, перечислены реализованные компоненты, дано описание процесса управления состоянием клиентского приложения с помощью технологии Redux и механизм отправки данных на сервер и получения данных. Также в конце раздела представлено описание реализованных визуальных форм.

Исходный код клиентской части приложения для свободного обмена одеждой опубликован в виде репозитория на GitHub. Доступ к исходному коду по ссылке: https://github.com/plastya-flomaster/swop-frontend.

Функциональный подход к созданию компонентов React

Функциональные компоненты в Reactдают возможность использовать новую технологию ReactHooks.Hooks -- нововведение в React 16.8, которое позволяет использовать состояние и другие возможности React без написания классов.

В React нет способа «присоединить» повторно используемое поведение к компоненту (например, подключение к хранилищу). Типичное крупное React-приложение представляет собой глубокое дерево иерархии компонентов, покружённых провайдерами, консьюмерами, компонентами высшего порядка, рендер-пропсами и другими абстракциями. Это проблема React, поэтому с 16.8 версии был предложен более удобный способ повторно использовать логику вокруг состояния.

С помощью Hooks можно извлечь логику состояния из компонента, чтобы её протестировать или повторно использовать. Hooks позволяют повторно использовать логику состояния, не затрагивая дерево компонентов. Благодаря этому, Hooks легко использовать в разных компонентах и делиться ими с сообществом.

В официальной документации Reactприводится два основных правила для использования Hooks:

Hooks следует вызывать только на верхнем уровне. Недопустим вызов hooks внутри циклов, условий или вложенных функций.

Hooks следует вызывать только из функциональных компонентов React. Недопустим вызов hooks из обычных JavaScript-функций. (Не распространяется на пользовательские hooks).

Более того, функциональный подход в написании компонентов позволяет облегчить восприятие и написание кода, исчезает парадигма жизненного цикла компонента, которая была неотъемлемой частью классовых компонентов. Hooks описывают все так, как оно должно быть в любой момент времени, и помогают не думать о том, как компоненту реагировать на изменения. В будущем чистые функции будут выигрывать по скорости работы в сравнении с классами из-за отсутствия методов жизненного цикла. Функции легче: для создания функции не нужно создавать класс, наследоваться от интерфейса (ключевое слово «extends»), не нужно создавать конструктор. А использование чистых функций уменьшает вероятность написания плохого кода. У разработчика нет необходимости использовать контекст. Код избавляется от конструкции thisи становится более удобочитаемым. С помощью чистых функций создаются компоненты без внутреннего состояния. Описание компонентов с помощью чистых функций создает меньше кода, следовательно, код легче поддерживать. Чистые функции намного проще тестировать. Необходимо просто передать propsи ожидать определенных действий.

Реализованные компоненты сервиса

В исходном коде проекта компоненты хранятся в папках, которые объединяют компоненты по смыслу. Самые объемные компоненты - компоненты, отвечающие за рендеринг целых страниц. Они находятся в папке «Pages». Таких компонентов всего шесть:

Компонент «Главная страница».

Компонент «Страница помощь».

Компонент «Страница редактирования данных о пользователе».

Компонент «Сведения о пользователе».

Компонент «Страница совпадений».

Вспомогательный компонент «Страница 404». Рендерится тогда, когда пользователь перешел на некорректный URL.

Для входа в приложение реализовано два компонента: «Окно регистрации пользователя» и «Окно входа в приложение».

Отображение ленты товаров (карточки товаров) реализовано вместе с вспомогательными кнопками «свайп влево» и «свайп вправо». Это компоненты группы «Cards».

Большое количество компонентов создано, для реализации функционала товаров: компонент для добавления и отображения информации о товаре, отображение всех товаров пользователя, отображения одного товара в сокращенном виде.

Также для сервиса реализовано два Reactrouter'а. Роутеры позволяют рендерить определённый компонент во время обращения к нему (например, при изменении URLили переходе по ссылке). Технологии динамического роутинга позволяют не держать в памяти приложения все варианты, а отображать только то, с чем работает пользователь, а остальным компонентам присваивать значение «null».

В приложении есть и более мелкие компоненты, которые не содержат логики или она достаточно простая, например: компонент «UserPic» служит, чтобы отобразить имя и фотографию пользователя, компонент «TagsInput» - самостоятельно реализованное поле ввода тегов и др.

Управление состоянием. React + Redux

Redux- это контейнер предсказуемых состояний для JavaScript приложений. Redux возник из идеи Flux -архитектурного паттерна, созданного инженерами из Facebook.Redux, с точки зрения кодаJavaScript- это объект, внутри которого лежат данные. Он используется остальными частями приложения для их хранения, изменения и извлечения. В терминологии Redux он называется контейнер, так как данные хранятся внутри.

Официальная документация Reduxрекомендует использовать свою библиотеку в тех случаях, если:

Существуют обоснованные объемы данных, меняющихся со временем.

Необходим один источник информации для состояния приложения.

Хранение состояния приложения в компоненте верхнего уровня уже недостаточно или небезопасно.

Основными терминами Redux являются:

State - состояние приложения (или его части) хранящееся в Store. Также есть специальный initialState - объект, описывающий начальное состояние приложения.

Store - все состояние приложения сохранено внутри этого объекта. Единственный способ изменить дерево состояния - это вызвать action.

Action - объект, который описывает то, что должно произойти. ПО соглашению, каждый Actionимеет тип (type) и загрузку (payload). Тип классически задается строкой, а загрузка это та часть нового состояния, которое предстоит изменить в Reducer.

Reducer - функция, которая ожидает Actionи в соответствии с тем, какой Actionпришел -возвращает новое состояние. Важно отметить то, что состояние никогда не мутирует, а всегда изменяется и замещается. Это позволяет Redux стабильно управлять состоянием всего приложения.

На рисунке 13 продемонстрирована схема взаимодействия при использовании технологии Redux. Как видно из схемы, действия могут поступать с представления от пользователя, или приходить из некого внешнего API. А представление ожидает изменения состояния в Store.

Рис. 13. Поток данных Redux + React.

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

В проект была загружена библиотека Reduxс помощью пакетного менеджера NPM(Команда для загрузки«npminstall --saveredux»). Поскольку разработка ведется на языке Typescript, необходимо было также установить «@types»для библиотеки Redux. Затем аналогичной командой нужно установить библиотеку-связку с React «react-redux» и «redux-devtools»для того, чтобы стала возможна отладка в браузере.

Дальнейшая настройка Reduxбудет описана по шагам. Рассмотрим процесс настройки управления состояниями товаров.

Шаг 1. Инициализация Store и подключение Dev-tools

Для того, чтобы избежать путаницы и структурировать исходный код проекта, была создана новая директории «Redux», где выделено еще три директории: «Actions», «Stores», «Reducers». Каждая директория, соответственно хранит файлы, относящиеся к этим сущностям Redux. Все файлы в этих папках будут иметь расширение «.ts», поэтому потребуется дополнительный код, отвечающий за типизацию создаваемых объектов.

В папке Storesсоздан файл «Store.ts». Ниже представлен полный код, описывающий хранилище состояний в Redux.

import { createStore, applyMiddleware, compose } from 'redux';

import thunk from 'redux-thunk';

import rootReducer from '../Reducers/rootReducer';

const initialState = {};

export type AppState = ReturnType<typeof rootReducer>

const store = createStore(

rootReducer,