Третий выпуск поста из четырех частей о том, как перенести идею из каркаса в производственное развертывание с помощью ReactJS. В этом посте мы создадим приложение для фильмов, используя библиотеку компонентов, созданную в предыдущем посте.
Первоначально это было размещено здесь.
Это третий пост в серии. Вы можете найти первый пост здесь
Где мы
Хорошо, так что до сих пор у нас есть
- Мы провели мозговой штурм над нашей блестящей идеей создать приложение для просмотра фильмов.
- Мы решили, какие функции необходимы в рамках MVP.
- Наша команда дизайнеров предоставила нам каркасы.
- Мы настроили наш проект как Monorepo.
- Мы настроили правила линтинга, средство форматирования кода и перехватчики коммитов.
- Мы настроили нашу библиотеку компонентов
- Мы добавили поддержку Typescript в нашу библиотеку компонентов.
- У нас есть сборник рассказов
- Мы добавили наши компоненты в библиотеку компонентов
- Мы добавили модульные тесты для наших компонентов
- Мы можем увидеть наши компоненты, продемонстрированные в Storybook.
Что мы собираемся делать сейчас
Итак, следующим шагом будет создание приложения для фильмов с использованием библиотеки компонентов. Мы будем использовать TMDB для получения сведений о нашем фильме. Мы будем поддерживать состояние нашего приложения с помощью Redux. Мы будем использовать Webpack для сборки нашего приложения. В конце этого поста мы должны были преобразовать наши каркасы в настоящий работающий веб-сайт.
TL;DR
Это пост из 4 частей
- Часть первая: каркасы и настройка проекта
- Часть вторая: настройка библиотеки компонентов
- Часть третья: Создание приложения Movie с использованием библиотеки компонентов
- Часть четвертая: Хостинг приложения Movie и настройка CI/CD
Исходный код доступен здесь
Демо-версия библиотеки компонентов доступна здесь
Демо-приложение Movie доступно здесь
Извлечение общей функциональности в core
Всегда рекомендуется извлекать общие сервисы, чтобы сохранить их СУХИМИ. Как мы извлекли общие компоненты в нашем предыдущем посте, мы извлечем общие функции в core
.
Что находится в core
Определение общей функциональности очень широкое, и есть несколько способов снять шкуру с курицы 🐔 Для нашего проекта мы будем извлекать наши вызовы API в core
Настройка core
Переместить в папку packages
cd packages
Создайте новую папку для нашего core
mkdir core
cd core
Инициализировать проект пряжи
yarn init
Следуя шагам по именованию, как мы это делали в предыдущем посте, наш package.json
выглядит так:
{
"name": "@awesome-movie-app/core",
"version": "1.0.0",
"description": "Core Services for Awesome Movie App",
"main": "index.js",
"repository": "[email protected]:debojitroy/movie-app.git",
"author": "Debojit Roy <[email protected]>",
"license": "MIT",
"private": true
}
Корпус core
Добавление axios
Мы будем делать много вызовов XHR для получения данных. Мы можем использовать собственные функции AJAX браузера или новый блестящий API fetch
. С таким количеством браузеров и различной реализацией fetch
безопаснее не использовать fetch
. Если мы решим включить fetch
, нам придется добавить необходимые полифиллы.
Поэтому гораздо лучше использовать axios
, который обеспечит правильную работу наших сетевых вызовов независимо от браузера пользователя.
Инициализация config
переменных
Поскольку core
является общей библиотекой, мы не хотим жестко кодировать или диктовать, как устанавливаются переменные среды. Мы хотели бы делегировать это вызывающему проекту для принятия решения.
Итак, мы создадим файл bootstrap
, который будет использоваться для инициализации конфигурации.
let config: { url: string; apiKey: string } = { url: "", apiKey: "" }
export const setConfig = (incomingConfig: { url: string; apiKey: string }) => { config = incomingConfig }
export const getConfig = () => config
Добавление search service
Одной из первых вещей в соответствии с нашим требованием было добавить службу поиска. Мы собираемся использовать Конечную точку поиска
После сопоставления ответа функциональность выглядит примерно так
import axios from "axios" import isNil from "lodash/isNil" import { getConfig } from "./bootstrap"
export interface SearchResult { popularity: number vote_count: number video: boolean poster_path: string id: number adult: boolean backdrop_path: string original_language: string original_title: string genre_ids: number[] title: string vote_average: number overview: string release_date: string }
export interface SearchResponse { page: number total_results: number total_pages: number results: SearchResult[] }
export const searchMovie = async ( queryString?: string ): Promise<SearchResponse> => { const config = getConfig()
if (isNil(queryString) || queryString.trim() === "") { return new Promise(resolve => { resolve({ page: 1, total_pages: 1, total_results: 0, results: [], }) }) }
const encodedQuery = encodeURI(queryString)
const result = await axios.get( `${config.url}/3/search/movie?api_key=${config.apiKey}&query=${encodedQuery}` )
return result.data }
Мы продолжим отображать остальной функционал, полный код доступен здесь
Настройка веб-приложения
Теперь, когда необходимые сервисы определены, мы сосредоточимся на создании веб-приложения.
Разделение кода таким образом помогает повторно использовать функциональность без многократного копирования и вставки.
Основные части нашего веб-приложения будут
- Общедоступные файлы
- Конфигурация веб-пакета
- Общие части
- Разделение по функциям
Настройка проекта веб-приложения
Перейдите в папку packages
cd packages
Создайте новую папку для нашего webapp
mkdir webapp
cd webapp
Инициализировать проект пряжи
yarn init
Следуя шагам по именованию, как мы это делали в предыдущем посте, наш package.json
выглядит так:
{
"name": "@awesome-movie-app/webapp",
"version": "1.0.0",
"description": "Web Application for Awesome Movie App",
"main": "index.js",
"repository": "[email protected]:debojitroy/movie-app.git",
"author": "Debojit Roy <[email protected]>",
"license": "MIT",
"private": true
}
Настройка public
активов
Поэтому для монтирования проекта React нам нужен элемент DOM, где React может взять на себя управление и внедрить элементы. Для этого нам нужен файл index.html
, который будет обслуживаться сервером до того, как React вступит во владение.
Мы сохраним это index.html
в нашей папке public
, но вы можете выбрать любое другое имя.
Вы можете найти файл здесь. Не стесняйтесь называть папку и файлы по своему усмотрению, но обязательно обновите их в конфигурации веб-пакета на следующем шаге.
Настройка веб-пакета
Мы будем использовать webpack
для упаковки нашего приложения. Вы можете выбрать любой другой упаковщик для своего проекта и внести соответствующие изменения.
Подготовьте папку config
mkdir config
Настройка общей конфигурации
Для нашей разработки local
мы будем использовать webpack dev server
и производственную сборку и минификацию для сборки production
. Но некоторые шаги будут общими для обоих, мы их вынесем в наш конфиг common
.
Итак, наш общий конфиг выглядит примерно так
// webpack.common.js const path = require("path") const MiniCssExtractPlugin = require("mini-css-extract-plugin") const { CleanWebpackPlugin } = require("clean-webpack-plugin") const HtmlWebPackPlugin = require("html-webpack-plugin")
const isEnvDevelopment = process.env.NODE_ENV === "development" const isEnvProduction = process.env.NODE_ENV === "production"
module.exports = { entry: { main: "./src/entry/index.tsx" }, resolve: { extensions: [".ts", ".tsx", ".js", ".jsx"], }, node: { fs: "empty", }, module: { rules: [ { test: /\.(js|jsx|mjs|ts|tsx)$/, exclude: /node_modules/, use: { loader: "babel-loader", }, }, { test: /\.css$/, use: [ "style-loader", { loader: MiniCssExtractPlugin.loader, options: { hmr: isEnvDevelopment, }, }, "css-loader", { loader: "postcss-loader", options: { ident: "postcss", plugins: () => [ require("postcss-flexbugs-fixes"), require("postcss-preset-env")({ autoprefixer: { flexbox: "no-2009", }, stage: 3, }), require("postcss-normalize"), ], sourceMap: isEnvProduction, }, }, ], // Don't consider CSS imports dead code even if the // containing package claims to have no side effects. // Remove this when webpack adds a warning or an error for this. // See https://github.com/webpack/webpack/issues/6571 sideEffects: true, }, { test: /\.(png|svg|jpg|gif)$/, use: ["file-loader"], }, { test: /\.(woff|woff2|eot|ttf|otf)$/, use: ["file-loader"], }, ], }, plugins: [ new CleanWebpackPlugin(), new HtmlWebPackPlugin({ title: "Awesome Movie App", template: "./public/index.html", filename: "./index.html", favicon: "./public/favicon.ico", }), ], }
Большинство вещей говорят сами за себя. Если вы новичок в webpack, я бы посоветовал ознакомиться с их потрясающей документацией.
Настройка конфига dev
С настройкой конфигурации common
мы хотели бы настроить нашу конфигурацию dev
. Мы хотим использовать webpack dev server
и hmr
с откатом маршрутизации.
Наша конфигурация разработчика выглядит так
//webpack.dev.js const path = require("path") const merge = require("webpack-merge") const MiniCssExtractPlugin = require("mini-css-extract-plugin") const common = require("./webpack.common.js")
module.exports = merge(common, { mode: "development", devtool: "inline-source-map", output: { path: path.join(__dirname, "../../dist/dist-dev"), filename: "[name].[contenthash].js", publicPath: "/", }, devServer: { contentBase: "./dist-dev", historyApiFallback: true, allowedHosts: [".debojitroy.com"], }, plugins: [ new MiniCssExtractPlugin({ filename: "[name].css", }), ], })
Сборка common
деталей
Общие детали — это элементы, не зависящие от функций, которые имеют сквозную функциональность.
Общие — Компоненты
Это общие компоненты, которые будут использоваться во всех функциях.
Общее — Конфигурация
Конфигурации для приложений, которые определены здесь.
Общий — Редукс
Здесь будут храниться специальные файлы Redux.
Общее — Маршруты
Здесь будут храниться определенные файлы маршрутизации.
Общие — утилиты
Здесь будут добавлены общие утилиты.
Особенности здания
Функции — это место, где будут сохранены фактические функции приложения. Думайте о каждой функции как об отдельной части приложения. Каждая функция сама по себе должна выделяться. В демонстрационных целях мы рассмотрим функцию SiteHeader
.
Заголовок сайта — Компоненты
Эта часть будет содержать все наши компоненты React, как следует из названия. В зависимости от требуемой функциональности мы разобьем нашу функцию на компоненты.
Заголовок сайта — Редукс
Сюда будут добавлены все файлы, связанные с Redux.
Я быстро пропускаю эти разделы, так как они являются стандартными материалами React/Redux, которые лучше объясняются во многих других местах.
Запуск веб-приложения
Добавление .env
Нам нужно объявить переменные конфигурации для запуска нашего приложения. На нашем производственном этапе мы будем делать это по-другому. Для локальной разработки добавим файл .env
и добавим его в .gitignore
, чтобы он не проверялся.
Go to webapp
cd packages/webapp
Создайте файл .env
vim .env
Добавьте значения конфигурации
API_URL=https://api.themoviedb.org
API_KEY=<Replace with actual key>
Подготовка сценария запуска
Теперь, когда у нас есть настройка .env
, последнее, что нам нужно сделать, это добавить скрипт start
.
Откройте package.json
внутри webapp
и добавьте это под scripts
"start": "cross-env development=true webpack-dev-server --config config/webpack.dev.js --open --port 8000"
Запуск веб-приложения локально
Как только мы закончим настройку webapp
, давайте попробуем запустить его локально.
Сначала создайте свой components
cd packages/components
yarn build-js:prod
Во-вторых, создайте свой core
cd packages/core
yarn build-js:prod
Наконец начните свой webapp
cd packages/webapp
yarn start
Если все прошло хорошо, вы должны увидеть что-то вроде этого
Фу!!! Это было долго.
Теперь последний шаг — настроить непрерывную интеграцию и развертывание, чтобы убедиться, что каждый раз, когда мы вносим изменения, они развертываются беспрепятственно. Вы можете прочитать об этом в последней части этой серии.