One place for hosting & domains

      Включение рендеринга на стороне сервера для приложения React


      Введение

      Рендеринг на стороне сервера (SSR) — это популярная методика рендеринга одностраничного клиентского приложения (SPA) на сервере и последующей отправки на клиент полностью отрисованной страницы. Это позволяет использовать динамические компоненты в качестве статической разметки HTML.

      Такой подход может быть полезным для поисковой оптимизации (SEO), когда при индексации код JavaScript не обрабатывается надлежащим образом. Это также может быть полезно в ситуациях, когда загрузка большого блока JavaScript затруднена из-за медленной скорости сети.

      В этом учебном модуле мы инициализируем приложение React с помощью Create React App, а затем изменим проект, чтобы он активировал рендеринг на стороне сервера.

      После прохождения учебного модуля вы получите работающий проект с клиентским приложением React и серверным приложением Express.

      Примечание. Также Next.js позволяет использовать современный подход к созданию статических приложений React и приложений, рендеринг которых выполняется на сервере.

      Предварительные требования

      Для данного обучающего руководства вам потребуется следующее:

      Этот учебный модуль был проверен с версиями Node v14.4.0 и npm v6.14.5.

      Шаг 1 — Создание приложения React и изменение компонента приложения

      Вначале мы используем npx для запуска нового приложения React с помощью последней версии Create React App.

      Назовем наше приложение my-ssr-app:

      • npx create-react-app@3.4.1 my-ssr-app

      Перейдем в новый каталог с помощью команды cd:

      cd my-ssr-app
      

      В заключение мы запустим наше новое приложение на стороне клиента для проверки установки:

      Вы должны увидеть пример приложения React в окне браузера.

      Теперь создадим компонент <Home>:

      Затем добавим следующий код в файл Home.js:

      src/Home.js

      import React from 'react';
      
      export default props => {
        return <h1>Hello {props.name}!</h1>;
      };
      

      При этом будет создан заголовок <h1> с сообщением "Hello“, адресованным имени.

      Далее мы выполним рендеринг <Home> в компоненте <App>. Откройте файл App.js:

      Затем заменим существующие строки кода новыми строками кода:

      src/App.js

      import React from 'react';
      import Home from './Home';
      
      export default () => {
        return <Home name="Sammy" />;
      };
      

      Они передают name в компонент <Home> так, что ожидаемое сообщение будет выглядеть так: "Hello Sammy!".

      В файле index.js нашего приложения мы будем использовать метод ReactDOM hydrate вместо render, чтобы указать блоку рендеринга DOM, чтобы мы восстанавливаем приложение после рендеринга на стороне сервера.

      Откроем файл index.js:

      Замените содержимое файла index.js следующим кодом:

      index.js

      import React from 'react';
      import ReactDOM from 'react-dom';
      import App from './App';
      
      ReactDOM.hydrate(<App />, document.getElementById('root'));
      

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

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

      Теперь наше приложение готово, и мы настроим сервер, который будет отправлять готовую версию после рендеринга. Для сервера мы будем использовать Express. Добавим его в проект, введя следующую команду в окне терминала:

      • npm install express@4.17.1

      Также можно использовать yarn:

      Создайте каталог server рядом с каталогом src нашего приложения:

      Затем создайте новый файл index.js, содержащий код сервера Express:

      Добавим необходимые элементы импорта и определим некоторые константы:

      server/index.js

      import path from 'path';
      import fs from 'fs';
      
      import React from 'react';
      import express from 'express';
      import ReactDOMServer from 'react-dom/server';
      
      import App from '../src/App';
      
      const PORT = process.env.PORT || 3006;
      const app = express();
      

      Затем добавим код сервера с обработкой ошибок:

      server/index.js

      // ...
      
      app.get('/', (req, res) => {
        const app = ReactDOMServer.renderToString(<App />);
      
        const indexFile = path.resolve('./build/index.html');
        fs.readFile(indexFile, 'utf8', (err, data) => {
          if (err) {
            console.error('Something went wrong:', err);
            return res.status(500).send('Oops, better luck next time!');
          }
      
          return res.send(
            data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
          );
        });
      });
      
      app.use(express.static('./build'));
      
      app.listen(PORT, () => {
        console.log(`Server is listening on port ${PORT}`);
      });
      

      Как видите, мы можем импортировать наш компонент <App> из клиентского приложения непосредственно с сервера.

      Здесь происходит три важные вещи:

      • Мы предписываем Express вывести содержимое каталога build в виде статичных файлов.
      • Мы будем использовать метод ReactDOMServer, renderToString, для рендеринга нашего приложения в статичную строку HTML.
      • Затем мы считываем статичный файл index.html из готового клиентского приложения, вставляем статичное содержимое нашего приложения в <div> с id "root", а затем отправляем результат в качестве ответа на запрос.

      Шаг 3 — Настройка webpack, Babel и скриптов npm

      Чтобы наш серверный код работал, нам нужно объединить его в комплект и провести транспиляцию, используя webpack и Babel. Для этого добавим в проект зависимости dev, введя следующую команду в окне терминала:

      • npm install webpack@4.42.0 webpack-cli@3.3.12 webpack-node-externals@1.7.2 @babel/core@7.10.4 babel-loader@8.1.0 @babel/preset-env@7.10.4 @babel/preset-react@7.10.4 --save-dev

      Также можно использовать yarn:

      • yarn add webpack@4.42.0 webpack-cli@3.3.12 webpack-node-externals@1.7.2 @babel/core@7.10.4 babel-loader@8.1.0 @babel/preset-env@7.10.4 @babel/preset-react@7.10.4 --dev

      Примечание. В более ранней версии этого учебного модуля мы устанавливали babel-core, babel-preset-env и babel-preset-react-app. Эти пакеты с тех пор были архивированы, и вместо них используются версии с одним репозиторием.

      Далее мы создадим файл конфигурации Babel:

      После этого добавьте готовые настройки env и react-app:

      .babelrc.json

      {
        "presets": [
          "@babel/preset-env",
          "@babel/preset-react"
        ]
      }
      

      Примечание. В более ранней версии этого учебного модуля мы использовали файл .babelrc (без расширения .json). Это был файл конфигурации Babel 6, однако для Babel 7 он больше не используется.

      Теперь мы создадим конфигурацию webpack для сервера, который использует Babel Loader для транспиляции кода. Начните с создания файла:

      Затем добавьте следующие конфигурации в файл webpack.server.js:

      webpack.server.js

      const path = require('path');
      const nodeExternals = require('webpack-node-externals');
      
      module.exports = {
        entry: './server/index.js',
      
        target: 'node',
      
        externals: [nodeExternals()],
      
        output: {
          path: path.resolve('server-build'),
          filename: 'index.js'
        },
      
        module: {
          rules: [
            {
              test: /.js$/,
              use: 'babel-loader'
            }
          ]
        }
      };
      

      С этой конфигурацией наш транспилированный серверный комплект будет выводиться в папку server-build в файле с именем called index.js.

      Обратите внимание на использование target: 'node' и externals: [nodeExternals()] из webpack-node-externals. При этом опускаются файлы из node_modules в комплекте, сервер сможет получить доступ к этим файлам напрямую.

      Это завершает установку зависимости и конфигурации webpack и Babel.

      Теперь мы снова вернемся к файлу package.json и добавим вспомогательные скрипты npm:

      Мы добавим скрипты dev:build-server, dev:start и dev в файл package.json, чтобы легко выполнять сборку и подачу нашего приложения SSR:

      package.json

      "scripts": {
        "dev:build-server": "NODE_ENV=development webpack --config webpack.server.js --mode=development -w",
        "dev:start": "nodemon ./server-build/index.js",
        "dev": "npm-run-all --parallel build dev:*",
        ...
      },
      

      Мы используем nodemon для перезапуска сервера при внесении изменений. Также мы используем npm-run-all для параллельного выполнения нескольких команд.

      Давайте установим эти пакеты, введя следующие команды в окне терминала:

      • npm install nodemon@2.0.4 npm-run-all@4.1.5 --save-dev

      Также можно использовать yarn:

      • yarn add nodemon@2.0.4 npm-run-all@4.1.5 --dev

      Так вы можете запустить следующий код для сборки приложения на стороне клиента, объединения в пакет и транспиляции кода сервера и запуска сервера на порту :3006:

      Также можно использовать yarn:

      Наша конфигурация сервера webpack будет следить за изменениями, и в случае изменений наш сервер перезапустится. Однако для клиентского приложения нам нужно выполнять сборку каждый раз при внесении изменений. Для этого есть открытая проблема.

      Откройте в браузере адрес http://localhost:3006/ и вы увидите приложение после рендеринга на стороне сервера.

      Ранее исходный код показал следующее:

      Output

      <div id="root"></div>

      С внесенными изменениями исходный код показывает:

      Output

      <div id="root"><h1 data-reactroot="">Hello <!-- -->Sammy<!-- -->!</h1></div>

      При рендеринге на стороне сервера компонент <App> был успешно конвертирован в формат HTML.

      Заключение

      В этом учебном модуле мы инициализировали приложение React и активировали рендеринг на стороне сервера.

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

      Преимущество использования SSR заключается в наличии приложения, содержимое которого может просмотреть даже сборщик, не выполняющий код JavaScript. Это поможет для поисковой оптимизации (SEO) и отправки метаданных на каналы социальных сетей.

      SSR также часто помогает решить проблемы с производительностью, потому что полностью загруженное приложение отправляется с сервера при первом запросе. С необычными приложениями ситуация может меняться, потому что SSR требует довольно сложной настройки и создает более существенную нагрузку на сервер. Использование рендеринга на стороне сервера для приложения React зависит от конкретных потребностей и от того, какие компромиссы имеют смысл для вашего сценария использования.

      Если вы хотите узнать больше о React, почитайте нашу серию «Программирование на React.js» или посмотрите страницу тем React, где вы найдете упражнения и программные проекты.



      Source link

      Специальная разбивка на страницы с помощью React


      Введение

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

      Один из методов работы с большими наборами данных — это разбивка на страницы. Разбивка на страницы эффективно работает, когда вы заранее знаете размер набора данных (общее количество записей в наборе данных). Вам нужно загрузить только требуемую часть данных из набора в зависимости от взаимодействия конечного пользователя с элементом управления разбивкой на страницы. Эта методика используется для отображения результатов поиска в поисковике Google.

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

      Вот демонстрация того, что вы сделаете в этом учебном модуле:

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

      Предварительные требования

      Для данного обучающего руководства вам потребуется следующее:

      • [Node], nodejsустановленный на вашем компьютере. Процедура установки описана в документе «Установка Node.js и создание локальной среды разработки».
      • Пакет командной строки [create-react-app][`create-react-app] создает базовый код для вашего приложения React. Если вы используете версиюnpm < 5.2, возможно, вам потребуется установитьcreate-react-app` как глобальную зависимость.
      • Наконец, в этом обучающем модуле предполагается, что вы уже знакомы с React. Если это не так, вы можете ознакомиться с серией «Программирование на React.js», чтобы узнать больше о React.

      Этот учебный модуль был проверен с использованием Node v14.2.0, npm v6.14.4, react v16.13.1 и react-scripts v3.4.1.

      Шаг 1 — Настройка проекта

      Создайте новое приложение React, используя команду create-react-app. Вы можете назвать приложение как угодно, но в этом учебном модуле мы присвоим ему имя react-pagination:

      • npx create-react-app react-pagination

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

      Выполните следующую команду для установки требуемых зависимостей:

      • npm install bootstrap@4.1.0 prop-types@15.6.1 react-flags@0.1.13 countries-api@2.0.1 node-sass@4.14.1

      При этом будут установлены элементы bootstrap, prop-types, react-flags, countries-api и node-sass.

      Вы установили пакет bootstrap как зависимость для вашего приложения, поскольку вам потребуются некоторые стили, доступные по умолчанию. Также вы будете использовать стили компонента Bootstrap pagination.

      Чтобы включить Bootstrap в приложение, отредактируйте файл src/index.js:

      Добавьте следующую строку перед другими выражениями import:

      src/index.js

      import "bootstrap/dist/css/bootstrap.min.css";
      

      Теперь в вашем приложении будут доступны стили Bootstrap.

      Также вы установили react-flags как зависимость для вашего приложения. Чтобы получить доступ к иконкам флагов из вашего приложения, вам потребуется скопировать изображения иконок в каталог public вашего приложения.

      Создайте каталог img в вашем каталоге public:

      Скопируйте файлы изображения из flags в img:

      • cp -R node_modules/react-flags/vendor/flags public/img

      Это обеспечивает создание копии всех изображений react-flag в вашем приложении.

      Теперь мы добавили некоторые зависимости и можем запустить приложение, выполнив следующую команду с npm в каталоге проекта react-pagination:

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

      Представление приложения должно выглядеть как на следующем снимке экрана:

      Начальное представление — экран приветствия React

      Теперь вы готовы начать создание компонентов.

      Шаг 2 — Создание компонента CountryCard

      На этом шаге мы создадим компонент CountryCard. Компонент CountryCard выполняет рендеринг имени, региона и флага определенной страны.

      Для начала создадим каталог components в каталоге src:

      Затем создадим новый файл CountryCard.js в каталоге src/components:

      • nano src/components/CountryCard.js

      Добавим в него следующий блок кода:

      src/components/CountryCard.js

      import React from 'react';
      import PropTypes from 'prop-types';
      import Flag from 'react-flags';
      
      const CountryCard = props => {
        const {
          cca2: code2 = '', region = null, name = {}
        } = props.country || {};
      
        return (
          <div className="col-sm-6 col-md-4 country-card">
            <div className="country-card-container border-gray rounded border mx-2 my-3 d-flex flex-row align-items-center p-0 bg-light">
              <div className="h-100 position-relative border-gray border-right px-2 bg-white rounded-left">
                <Flag country={code2} format="png" pngSize={64} basePath="./img/flags" className="d-block h-100" />
              </div>
              <div className="px-3">
                <span className="country-name text-dark d-block font-weight-bold">{ name.common }</span>
                <span className="country-region text-secondary text-uppercase">{ region }</span>
              </div>
            </div>
          </div>
        )
      }
      
      CountryCard.propTypes = {
        country: PropTypes.shape({
          cca2: PropTypes.string.isRequired,
          region: PropTypes.string.isRequired,
          name: PropTypes.shape({
            common: PropTypes.string.isRequired
          }).isRequired
        }).isRequired
      };
      
      export default CountryCard;
      

      Для компонента CountryCard требуется объект country, содержащий данные о стране, которые будут выводиться. Как можно увидеть в списке propTypes для компонента CountryCard, объект country должен содержать следующие данные:

      • cca2 — 2-значный код страны
      • region — регион страны (например, «Африка»)
      • name.common — общее название страны (например, «Нигерия»)

      Вот образец объекта страны:

      {
        cca2: "NG",
        region: "Africa",
        name: {
          common: "Nigeria"
        }
      }
      

      Обратите внимание на рендеринг флага страны с использованием пакета react-flags. Вы можете посмотреть документацию react-flags, чтобы узнать больше о требуемых объектах и использовании пакета.

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

      На этом шаге мы создадим компонент Pagination. Компонент Pagination содержит логику построения, рендеринга и переключения страниц в элементе управления разбивкой на страницы.

      Создайте новый файл Pagination.js в каталоге src/components:

      • nano src/components/Pagination.js

      Добавим в него следующий блок кода:

      src/components/Pagination.js

      import React, { Component, Fragment } from 'react';
      import PropTypes from 'prop-types';
      
      class Pagination extends Component {
        constructor(props) {
          super(props);
          const { totalRecords = null, pageLimit = 30, pageNeighbours = 0 } = props;
      
          this.pageLimit = typeof pageLimit === 'number' ? pageLimit : 30;
          this.totalRecords = typeof totalRecords === 'number' ? totalRecords : 0;
      
          // pageNeighbours can be: 0, 1 or 2
          this.pageNeighbours = typeof pageNeighbours === 'number'
            ? Math.max(0, Math.min(pageNeighbours, 2))
            : 0;
      
          this.totalPages = Math.ceil(this.totalRecords / this.pageLimit);
      
          this.state = { currentPage: 1 };
        }
      }
      
      Pagination.propTypes = {
        totalRecords: PropTypes.number.isRequired,
        pageLimit: PropTypes.number,
        pageNeighbours: PropTypes.number,
        onPageChanged: PropTypes.func
      };
      
      export default Pagination;
      

      Компонент Pagination может принимать четыре специальных объекта, указанные в объекте propTypes.

      • onPageChanged — это функция, вызываемая с данными по текущему состоянию разбивки на страницы, только в случае изменения текущей страницы.
      • totalRecords указывает общее количество записей, которое требуется разбить на страницы. Это значение является обязательным.
      • pageLimit указывает количество отображаемых записей на каждой странице. Если этот параметр не указан, по умолчанию используется значение 30, определенное в constructor().
      • pageNeighbours указывает количество номеров дополнительных страниц, отображаемое на каждой стороне текущей страницы. Минимальное значение 0, максимальное значение 2. Если параметр не определен, по умолчанию используется значение 0, определенное в constructor().

      На следующем изображении показан эффект различных значений объекта pageNeighbours:

      Иллюстрация соседних страниц

      В функции constructor() мы рассчитываем общее количество страниц следующим образом:

      this.totalPages = Math.ceil(this.totalRecords / this.pageLimit);
      

      Обратите внимание, что здесь мы используем Math.ceil(), чтобы получить целое значение общего количества страниц. При этом также обеспечивается регистрация лишних записей на последней странице, особенно в случаях, когда количество лишних записей меньше количества записей, отображаемого на странице.

      Наконец, вы инициализировали состояние с установленным для свойства currentPage значением 1. Это свойство состояния нужно вам для внутреннего отслеживания текущей активной страницы.

      Затем вы создадите метод для генерирования номеров страниц.

      После import, но до класса Pagination нужно добавить следующие константы и функцию range:

      src/components/Pagination.js

      // ...
      
      const LEFT_PAGE = 'LEFT';
      const RIGHT_PAGE = 'RIGHT';
      
      /**
       * Helper method for creating a range of numbers
       * range(1, 5) => [1, 2, 3, 4, 5]
       */
      const range = (from, to, step = 1) => {
        let i = from;
        const range = [];
      
        while (i <= to) {
          range.push(i);
          i += step;
        }
      
        return range;
      }
      

      В классе Pagination после функции constructor нужно добавить следующий метод fetchPageNumbers:

      src/components/Pagination.js

      class Pagination extends Component {
        // ...
      
        /**
         * Let's say we have 10 pages and we set pageNeighbours to 2
         * Given that the current page is 6
         * The pagination control will look like the following:
         *
         * (1) < {4 5} [6] {7 8} > (10)
         *
         * (x) => terminal pages: first and last page(always visible)
         * [x] => represents current page
         * {...x} => represents page neighbours
         */
        fetchPageNumbers = () => {
          const totalPages = this.totalPages;
          const currentPage = this.state.currentPage;
          const pageNeighbours = this.pageNeighbours;
      
          /**
           * totalNumbers: the total page numbers to show on the control
           * totalBlocks: totalNumbers + 2 to cover for the left(<) and right(>) controls
           */
          const totalNumbers = (this.pageNeighbours * 2) + 3;
          const totalBlocks = totalNumbers + 2;
      
          if (totalPages > totalBlocks) {
            const startPage = Math.max(2, currentPage - pageNeighbours);
            const endPage = Math.min(totalPages - 1, currentPage + pageNeighbours);
            let pages = range(startPage, endPage);
      
            /**
             * hasLeftSpill: has hidden pages to the left
             * hasRightSpill: has hidden pages to the right
             * spillOffset: number of hidden pages either to the left or to the right
             */
            const hasLeftSpill = startPage > 2;
            const hasRightSpill = (totalPages - endPage) > 1;
            const spillOffset = totalNumbers - (pages.length + 1);
      
            switch (true) {
              // handle: (1) < {5 6} [7] {8 9} (10)
              case (hasLeftSpill && !hasRightSpill): {
                const extraPages = range(startPage - spillOffset, startPage - 1);
                pages = [LEFT_PAGE, ...extraPages, ...pages];
                break;
              }
      
              // handle: (1) {2 3} [4] {5 6} > (10)
              case (!hasLeftSpill && hasRightSpill): {
                const extraPages = range(endPage + 1, endPage + spillOffset);
                pages = [...pages, ...extraPages, RIGHT_PAGE];
                break;
              }
      
              // handle: (1) < {4 5} [6] {7 8} > (10)
              case (hasLeftSpill && hasRightSpill):
              default: {
                pages = [LEFT_PAGE, ...pages, RIGHT_PAGE];
                break;
              }
            }
      
            return [1, ...pages, totalPages];
          }
      
          return range(1, totalPages);
        }
      }
      

      Вначале вы определите две константы: LEFT_PAGE и RIGHT_PAGE. Эти константы будут использоваться для указания точек расположения элементов управления для перехода на следующую страницу слева и справа соответственно.

      Также вы определили вспомогательную функцию range(), которая поможет вам генерировать диапазоны чисел.

      Примечание. Если вы используете в проекте библиотеку утилит, например, Lodash, вы можете использовать функцию _.range(), предоставляемую Lodash. В следующем блоке кода показаны отличия между функцией range(), которую мы только что определили, и функцией от Lodash:

      range(1, 5); // returns [1, 2, 3, 4, 5]
      _.range(1, 5); // returns [1, 2, 3, 4]
      

      Далее мы определили метод fetchPageNumbers() в классе Pagination. Этот метод отвечает за выполнение базовой логики генерирования номеров страниц для отображения элементом управления разбивкой на страницы. Нам нужно, чтобы первая и последняя страницы всегда были видны.

      Вначале мы определили две переменные. totalNumbers представляет общее количество страниц, показываемое на элементе управления. totalBlocks показывает общее количество страниц плюс два дополнительных блока для индикаторов страниц слева и справа.

      Если значение totalPages не больше, чем totalBlocks, возвращается диапазон чисел от 1 до totalPages. В ином случае возвращается массив номеров страниц с LEFT_PAGE и RIGHT_PAGE в точках, где можно перейти на страницу слева или справа.

      Обратите внимание, что элемент управления разбивкой на страницы обеспечивает постоянную видимость первой и последней страниц. Элементы управления переходом на левую и правую страницы отображаются внутри.

      Теперь мы добавим метод render(), чтобы выполнить рендеринг элемента управления разбивкой на страницы.

      В классе Pagination после функции constructor и метода fetchPageNumbers нужно добавить следующий метод render:

      src/components/Pagination.js

      class Pagination extends Component {
        // ...
      
        render() {
          if (!this.totalRecords || this.totalPages === 1) return null;
      
          const { currentPage } = this.state;
          const pages = this.fetchPageNumbers();
      
          return (
            <Fragment>
              <nav aria-label="Countries Pagination">
                <ul className="pagination">
                  { pages.map((page, index) => {
      
                    if (page === LEFT_PAGE) return (
                      <li key={index} className="page-item">
                        <a className="page-link" href="https://www.digitalocean.com/community/tutorials/#" aria-label="Previous" onClick={this.handleMoveLeft}>
                          <span aria-hidden="true">&laquo;</span>
                          <span className="sr-only">Previous</span>
                        </a>
                      </li>
                    );
      
                    if (page === RIGHT_PAGE) return (
                      <li key={index} className="page-item">
                        <a className="page-link" href="https://www.digitalocean.com/community/tutorials/#" aria-label="Next" onClick={this.handleMoveRight}>
                          <span aria-hidden="true">&raquo;</span>
                          <span className="sr-only">Next</span>
                        </a>
                      </li>
                    );
      
                    return (
                      <li key={index} className={`page-item${ currentPage === page ? ' active' : ''}`}>
                        <a className="page-link" href="https://www.digitalocean.com/community/tutorials/#" onClick={ this.handleClick(page) }>{ page }</a>
                      </li>
                    );
      
                  }) }
      
                </ul>
              </nav>
            </Fragment>
          );
        }
      }
      

      Здесь мы генерируем массив номеров страниц, вызывая метод fetchPageNumbers(), который мы создали ранее. Затем выполняется рендеринг каждого номера страницы с использованием Array.prototype.map(). Обратите внимание, что необходимо регистрировать обработчики событий нажатия для каждого отображаемого номера страницы, чтобы обеспечить обработку нажатий.

      Также следует отметить, что элемент управления разбивкой на страницы не отображается, если объект totalRecords не был передан в компонент Pagination надлежащим образом или в случаях, когда имеется только 1 страница.

      В заключение мы определим методы обработчика событий.

      Добавьте следующее в классе Pagination после функции constructor, метода fetchPageNumbers и метода render:

      src/components/Pagination.js

      class Pagination extends Component {
        // ...
      
        componentDidMount() {
          this.gotoPage(1);
        }
      
        gotoPage = page => {
          const { onPageChanged = f => f } = this.props;
          const currentPage = Math.max(0, Math.min(page, this.totalPages));
          const paginationData = {
            currentPage,
            totalPages: this.totalPages,
            pageLimit: this.pageLimit,
            totalRecords: this.totalRecords
          };
      
          this.setState({ currentPage }, () => onPageChanged(paginationData));
        }
      
        handleClick = page => evt => {
          evt.preventDefault();
          this.gotoPage(page);
        }
      
        handleMoveLeft = evt => {
          evt.preventDefault();
          this.gotoPage(this.state.currentPage - (this.pageNeighbours * 2) - 1);
        }
      
        handleMoveRight = evt => {
          evt.preventDefault();
          this.gotoPage(this.state.currentPage + (this.pageNeighbours * 2) + 1);
        }
      }
      

      Вы определили метод gotoPage(), который изменяет состояние и устанавливает указанную страницу как currentPage. Он гарантирует, что аргумент page имеет значение не менее 1 и не более общего количества страниц. В завершение он вызывает функцию onPageChanged(), которая была передана как объект, с данными, указывающими новое состояние разбивки на страницы.

      При монтировании компонента мы переходим на первую страницу, вызывая this.gotoPage(1), как показано в методе жизненного цикла componentDidMount().

      Обратите внимание на использовании (this.pageNeighbours * 2) в handleMoveLeft() и handleMoveRight() для прокрутки номеров страниц влево и вправо соответственно в зависимости от текущего номера страницы.

      Вот демонстрация взаимодействия при движении слева направо.

      Движение слева направо при взаимодействии

      Мы закончили работу над компонентом Pagination. Теперь пользователи смогут использовать элементы навигации этого компонента для отображения разных страниц с флагами.

      Шаг 4 — Построение компонента App

      Обратите внимание, что теперь у нас имеются компоненты CountryCard и Pagination, и мы можем использовать их в нашем компоненте App.

      Измените файл App.js в каталоге src:

      Замените содержимое файла App.js следующими строками кода:

      src/App.js

      import React, { Component } from 'react';
      import Countries from 'countries-api';
      import './App.css';
      import Pagination from './components/Pagination';
      import CountryCard from './components/CountryCard';
      
      class App extends Component {
        state = { allCountries: [], currentCountries: [], currentPage: null, totalPages: null }
      
        componentDidMount() {
          const { data: allCountries = [] } = Countries.findAll();
          this.setState({ allCountries });
        }
      
        onPageChanged = data => {
          const { allCountries } = this.state;
          const { currentPage, totalPages, pageLimit } = data;
          const offset = (currentPage - 1) * pageLimit;
          const currentCountries = allCountries.slice(offset, offset + pageLimit);
      
          this.setState({ currentPage, currentCountries, totalPages });
        }
      }
      
      export default App;
      

      Здесь мы инициализируем состояние компонента App, используя следующие атрибуты:

      • allCountries — это массив всех стран в вашем приложении. Инициализируется как пустой массив ([]).
      • currentCountries — это массив всех стран, отображаемых на активной странице. Инициализируется как пустой массив ([]).
      • currentPage — номер активной страницы. Инициализируется как null.
      • totalPages — общее количество страниц со всеми записями стран. Инициализируется как null.

      Затем в методе жизненного цикла componentDidMount() мы доставляем все страны мира, используя пакет countries-api посредством вызова Countries.findAll(). Затем мы обновляем состояние приложения, устанавливая все страны мира как содержимое allCountries. Вы можете посмотреть [документацию по countries-api], countries-apiчтобы узнать больше о пакете.

      В заключение мы определили метод onPageChanged(), который будет вызываться при каждом переходе на новую страницу из элемента управления разбивкой на страницы. Этот метод будет передаваться в объект onPageChanged компонента Pagination.

      При использовании этого метода следует обратить внимание на две строки. Вот первая из этих строк:

      const offset = (currentPage - 1) * pageLimit;
      

      Значение offset указывает на начальный индекс для доставки записей для текущей страницы. Благодаря использованию (currentPage - 1) коррекция основана на нулевом значении. Допустим, мы отображаем 25 записей на каждой странице, и вы просматриваете страницу 5. Тогда значение коррекции будет ((5 - 1) * 25 = 100).

      Например, если вы доставляете записи по запросу из базы данных, этот образец запроса SQL покажет вам, как использовать данную коррекцию:

      SELECT * FROM `countries` LIMIT 100, 25
      

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

      Вторая строка выглядит так:

      const currentCountries = allCountries.slice(offset, offset + pageLimit);
      

      Здесь мы использовали метод Array.prototype.slice() для извлечения требуемого блока записей из массива allCountries, передавая offset как указатель начала блока и (offset + pageLimit) как указатель конца блока.

      Примечание. В этом учебном модуле мы не доставляли записи из внешнего источника. В реальном приложении записи обычно доставляются из базы данных или через API. Логику доставки записей можно разместить в методе onPageChanged() компонента App.

      Допустим, вы используете вымышленную конечную точку API /api/countries?page={current_page}&limit={page_limit}. В следующем блоке кода показано, как доставлять страны по запросу из API, используя пакет axios HTTP:

      onPageChanged = data => {
        const { currentPage, totalPages, pageLimit } = data;
      
        axios.get(`/api/countries?page=${currentPage}&limit=${pageLimit}`)
          .then(response => {
            const currentCountries = response.data.countries;
            this.setState({ currentPage, currentCountries, totalPages });
          });
      }
      

      Теперь вы можете закончить компонент App, добавив метод render().

      В классе App после componentDidMount и onPageChanged нужно добавить следующий метод render:

      src/App.js

      class App extends Component {
        // ... other methods here ...
      
        render() {
          const { allCountries, currentCountries, currentPage, totalPages } = this.state;
          const totalCountries = allCountries.length;
      
          if (totalCountries === 0) return null;
      
          const headerClass = ['text-dark py-2 pr-4 m-0', currentPage ? 'border-gray border-right' : ''].join(' ').trim();
      
          return (
            <div className="container mb-5">
              <div className="row d-flex flex-row py-5">
                <div className="w-100 px-4 py-5 d-flex flex-row flex-wrap align-items-center justify-content-between">
                  <div className="d-flex flex-row align-items-center">
                    <h2 className={headerClass}>
                      <strong className="text-secondary">{totalCountries}</strong> Countries
                    </h2>
                    { currentPage && (
                      <span className="current-page d-inline-block h-100 pl-4 text-secondary">
                        Page <span className="font-weight-bold">{ currentPage }</span> / <span className="font-weight-bold">{ totalPages }</span>
                      </span>
                    ) }
                  </div>
                  <div className="d-flex flex-row py-4 align-items-center">
                    <Pagination totalRecords={totalCountries} pageLimit={18} pageNeighbours={1} onPageChanged={this.onPageChanged} />
                  </div>
                </div>
                { currentCountries.map(country => <CountryCard key={country.cca3} country={country} />) }
              </div>
            </div>
          );
        }
      }
      

      В методе render() мы выполняем рендеринг общего количества стран, текущей страницы, общего количества страниц, элемента управления <Pagination> и <CountryCard> для каждой страны на текущей странице.

      Обратите внимание, что вы передали ранее определенный метод onPageChanged() в объект onPageChanged элемента управления <Pagination>. Это очень важно для регистрации изменений страниц из компонента Pagination. Мы выводим 18 стран на одной странице.

      Сейчас приложение выглядит, как показано на следующем снимке экрана:

      Снимок экрана приложения с 248 указанными странами и номерами страниц сверху и до конца каждой страницы

      Теперь у нас имеется компонент App, позволяющий отображать различные компоненты CountryCard и Pagination, разделяющие содержимое на отдельные страницы. Далее мы рассмотрим применение стилей к нашему приложению.

      Шаг 5 — Добавление специальных стилей

      Возможно вы заметили, что мы добавляли определенные специальные классы в ранее созданные компоненты. Давайте определим некоторые правила стилей для этих классов в файле src/App.scss.

      Файл App.scss будет выглядеть, как следующий фрагмент кода:

      src/App.scss

      /* Declare some variables */
      $base-color: #ced4da;
      $light-background: lighten(desaturate($base-color, 50%), 12.5%);
      
      .current-page {
        font-size: 1.5rem;
        vertical-align: middle;
      }
      
      .country-card-container {
        height: 60px;
        cursor: pointer;
        position: relative;
        overflow: hidden;
      }
      
      .country-name {
        font-size: 0.9rem;
      }
      
      .country-region {
        font-size: 0.7rem;
      }
      
      .current-page,
      .country-name,
      .country-region {
        line-height: 1;
      }
      
      // Override some Bootstrap pagination styles
      ul.pagination {
        margin-top: 0;
        margin-bottom: 0;
        box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
      
        li.page-item.active {
          a.page-link {
            color: saturate(darken($base-color, 50%), 5%) !important;
            background-color: saturate(lighten($base-color, 7.5%), 2.5%) !important;
            border-color: $base-color !important;
          }
        }
      
        a.page-link {
          padding: 0.75rem 1rem;
          min-width: 3.5rem;
          text-align: center;
          box-shadow: none !important;
          border-color: $base-color !important;
          color: saturate(darken($base-color, 30%), 10%);
          font-weight: 900;
          font-size: 1rem;
      
          &:hover {
            background-color: $light-background;
          }
        }
      }
      

      Измените файл App.js так, чтобы он ссылался на App.scss, а не на App.css.

      Примечание. Для получения дополнительной информации ознакомьтесь с документацией по Create React App.

      src/App.js

      import React, { Component } from 'react';
      import Countries from 'countries-api';
      import './App.scss';
      import Pagination from './components/Pagination';
      import CountryCard from './components/CountryCard';
      

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

      Снимок экрана приложения, страница 1 из 14, со стилями

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

      Заключение

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

      Полный исходный код этого учебного модуля можно найти в репозитории build-react-pagination-demo на GitHub. Также вы можете посмотреть работающую демонстрацию этого учебного модуля на Code Sandbox.

      Если вы хотите узнать больше о React, почитайте нашу серию «Программирование на React.js» или посмотрите страницу тем React, где вы найдете упражнения и программные проекты.



      Source link

      Создание сайта Drupal 9 на локальном компьютере с помощью Docker и DDEV


      Автор выбрал Diversity in Tech Fund для получения пожертвования в рамках программы Write for DOnations.

      Введение

      DDEV — это инструмент с открытым исходным кодом, использующий Docker для создания локальной среды разработки для различных структур PHP. Благодаря возможностям контейнеризации DDEV может существенно упростить работу над несколькими проектами, использующими разные технологические комплексы и несколько облачных серверов. В состав DDEV входят шаблоны для WordPress, Laravel, Magento, TYPO3, Drupal и т. д.

      Версия Drupal 9 была выпущена 3 июня 2020 г. для Drupal CMS. Drupal — это популярная структура PHP для создания и обслуживания сайтов и приложений любого масштаба, отличающаяся удобством использования и большой библиотекой модулей и тем.

      В этом обучающем модуле вы начнете создавать сайт Drupal 9 на своем локальном компьютере, используя DDEV. Это позволит вам сначала создать свой сайт, а затем, когда вы будете готовы, развернуть проект на производственном сервере.

      Предварительные требования

      Для данного обучающего руководства вам потребуется следующее:

      Примечание. Вы можете создать сайт Drupal 9 с помощью DDEV на удаленном сервере, но в этом случае вам потребуется решение для доступа к localhost в браузере. Команда DDEV ddev share работает с ngrok, создавая защищенный туннель к серверу для вас и других пользователей для просмотра сайта в разработке. Для личного использования вы также можете установить графический пользовательский интерфейс на удаленном сервере и подключаться к сайту в разработке через браузер, используя этот интерфейс. Для этого следуйте указаниям руководства по установке и настройке VNC в Ubuntu 20.04. Чтобы графический пользовательский интерфейс был еще быстрее, следуйте указаниям нашего руководства по настройке удаленного рабочего места с помощью X2Go в Ubuntu 20.04.

      Шаг 1 — Установка DDEV

      На этом шаге мы установим DDEV на локальный компьютер. Вариант 1 содержит инструкции для macOS, а Вариант 2 — инструкции для Linux. Мы тестировали этот обучающий модуль с DDEV версии 1.15.0.

      Вариант 1 — Установка DDEV в macOS

      DDEV рекомендует пользователям macOS использовать для установки инструмента диспетчер пакетов Homebrew. Используйте следующую команду brew для установки последнего стабильного выпуска:

      • brew tap drud/ddev && brew install drud/ddev/ddev

      Если вы предпочитаете абсолютно новейшую версию, вы можете использовать brew для установки ddev-edge:

      • brew tap drud/ddev-edge && brew install drud/ddev-edge/ddev

      Если у вас уже установлена версия DDEV или вы хотите обновить имеющуюся версию, закройте DDEV и используйте brew для обновления установки:

      • ddev poweroff
      • brew upgrade ddev

      После установки или обновления DDEV используйте команду ddev version для проверки вашего программного обеспечения:

      Результат должен будет выглядеть следующим образом:

      Output

      DDEV-Local version v1.15.0 commit v1.15.0 db drud/ddev-dbserver-mariadb-10.2:v1.15.0 dba phpmyadmin/phpmyadmin:5 ddev-ssh-agent drud/ddev-ssh-agent:v1.15.0 docker 19.03.8 docker-compose 1.25.5 os darwin router drud/ddev-router:v1.15.0 web drud/ddev-webserver:v1.15.0

      DDEV включает мощный инструмент CLI (или интерфейс командной строки). Запустите ddev, чтобы узнать о самых распространенных командах:

      Результат будет выглядеть следующим образом:

      Output

      Create and maintain a local web development environment. Docs: https://ddev.readthedocs.io Support: https://ddev.readthedocs.io/en/stable/#support Usage: ddev [command] Available Commands: auth A collection of authentication commands composer Executes a composer command within the web container config Create or modify a ddev project configuration in the current directory debug A collection of debugging commands delete Remove all project information (including database) for an existing project describe Get a detailed description of a running ddev project. exec Execute a shell command in the container for a service. Uses the web service by default. export-db Dump a database to a file or to stdout help Help about any command hostname Manage your hostfile entries. import-db Import a sql file into the project. import-files Pull the uploaded files directory of an existing project to the default public upload directory of your project. list List projects logs Get the logs from your running services. pause uses 'docker stop' to pause/stop the containers belonging to a project. poweroff Completely stop all projects and containers pull Pull files and database using a configured provider plugin. restart Restart a project or several projects. restore-snapshot Restore a project's database to the provided snapshot version. sequelpro This command is not available since sequel pro.app is not installed share Share project on the internet via ngrok. snapshot Create a database snapshot for one or more projects. ssh Starts a shell session in the container for a service. Uses web service by default. start Start a ddev project. stop Stop and remove the containers of a project. Does not lose or harm anything unless you add --remove-data. version print ddev version and component versions Flags: -h, --help help for ddev -j, --json-output If true, user-oriented output will be in JSON format. -v, --version version for ddev Use "ddev [command] --help" for more information about a command.

      Дополнительную информацию об использовании DDEV CLI можно найти в официальной документации по DDEV.

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

      Вариант 2 — Установка DDEV в Linux

      В операционной системе Linux вы можете установить DDEV, используя Homebrew для Linux или официальный скрипт для установки. Для начала обновите в Ubuntu список пакетов в диспетчере пакетов apt (вы можете использовать apt в Debian или аналогичный диспетчер пакетов для вашего дистрибутива Linux):

      Установите обязательные пакеты из официального репозитория Ubuntu:

      • sudo apt install build-essential apt-transport-https ca-certificates software-properties-common curl

      Эти пакеты позволят вам загрузить сценарий установки DDEV из официального репозитория на GitHub.

      Загрузите скрипт:

      • curl -O https://raw.githubusercontent.com/drud/ddev/master/scripts/install_ddev.sh

      Перед запуском скрипта откройте его в nano или предпочитаемом текстовом редакторе и проверьте его содержимое:

      nano install_ddev.sh
      

      Просмотрев и проверив содержание скрипта, сохраните и закройте файл. Теперь вы готовы запустить установочный скрипт.

      Используйте команду chmod, чтобы сделать скрипт исполняемым:

      Запустите скрипт:

      В процессе установки вам может понадобиться подтвердить некоторые настройки или ввести пароль пользователя sudo. После завершения установки DDEV будет доступен в вашей операционной системе Linux.

      Запустите команду ddev version для проверки программного обеспечения:

      Результат должен будет выглядеть следующим образом:

      Output

      DDEV-Local version v1.15.0 commit v1.15.0 db drud/ddev-dbserver-mariadb-10.2:v1.15.0 dba phpmyadmin/phpmyadmin:5 ddev-ssh-agent drud/ddev-ssh-agent:v1.15.0 docker 19.03.8 docker-compose 1.25.5 os linux router drud/ddev-router:v1.15.0 web drud/ddev-webserver:v1.15.0

      DDEV — мощный инструмент CLI (интерфейс командной строки). Запустите команду ddev без опций, чтобы узнать о некоторых распространенных командах:

      Результат будет выглядеть следующим образом:

      Output

      Create and maintain a local web development environment. Docs: https://ddev.readthedocs.io Support: https://ddev.readthedocs.io/en/stable/#support Usage: ddev [command] Available Commands: auth A collection of authentication commands composer Executes a composer command within the web container config Create or modify a ddev project configuration in the current directory debug A collection of debugging commands delete Remove all project information (including database) for an existing project describe Get a detailed description of a running ddev project. exec Execute a shell command in the container for a service. Uses the web service by default. export-db Dump a database to a file or to stdout help Help about any command hostname Manage your hostfile entries. import-db Import a sql file into the project. import-files Pull the uploaded files directory of an existing project to the default public upload directory of your project. list List projects logs Get the logs from your running services. pause uses 'docker stop' to pause/stop the containers belonging to a project. poweroff Completely stop all projects and containers pull Pull files and database using a configured provider plugin. restart Restart a project or several projects. restore-snapshot Restore a project's database to the provided snapshot version. sequelpro This command is not available since sequel pro.app is not installed share Share project on the internet via ngrok. snapshot Create a database snapshot for one or more projects. ssh Starts a shell session in the container for a service. Uses web service by default. start Start a ddev project. stop Stop and remove the containers of a project. Does not lose or harm anything unless you add --remove-data. version print ddev version and component versions Flags: -h, --help help for ddev -j, --json-output If true, user-oriented output will be in JSON format. -v, --version version for ddev Use "ddev [command] --help" for more information about a command.

      Дополнительную информацию об использовании DDEV CLI можно найти в официальной документации по DDEV.

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

      Шаг 2 — Развертывание нового сайта Drupal 9 с помощью DDEV

      Запустив DDEV, вы можете использовать его для создания файловой системы Drupal, установки Drupal 9 и создания стандартного проекта сайта.

      Вначале мы создадим корневой каталог проекта и перейдем в него. Все остальные команды мы будем запускать отсюда. В этом обучающем модуле мы будем использовать имя каталога d9test, но вы можете выбрать любое предпочитаемое имя. Обратите внимание, что DDEV не очень хорошо обрабатывает имена с дефисами. Желательно избегать использования таких имен каталогов, как my-project или drupal-site-1.

      Создайте корневой каталог проекта и перейдите в него:

      DDEV отлично создает деревья каталогов, соответствующие определенным платформам CMS. Используйте команду ddev config для создания специальной структуры каталогов для Drupal 9:

      • ddev config --project-type=drupal9 --docroot=web --create-docroot

      Результат должен будет выглядеть следующим образом:

      Output

      Creating a new ddev project config in the current directory (/Users/sammy/d9test) Once completed, your configuration will be written to /Users/sammy/d9test/.ddev/config.yaml Created docroot at /Users/sammy/d9test/web You have specified a project type of drupal9 but no project of that type is found in /Users/sammy/d9test/web Ensuring write permissions for d9new No settings.php file exists, creating one Existing settings.php file includes settings.ddev.php Configuration complete. You may now run 'ddev start'.

      Поскольку вы передали аргумент --project-type=drupal9 в команду ddev config, DDEV создаст несколько подкаталогов и файлов, используемых по умолчанию для организации сайта Drupal. Ваше дерево каталогов проекта теперь будет выглядеть так:

      A Drupal 9 directory tree

      .
      ├── .ddev
      │   ├── .gitignore
      │   ├── config.yaml
      │   ├── db-build
      │   │   └── Dockerfile.example
      │   └── web-build
      │       └── Dockerfile.example
      └── web
          └── sites
              └── default
                  ├── .gitignore
                  ├── settings.ddev.php
                  └── settings.php
      
      6 directories, 7 files
      

      .ddev/ будет главной папкой для конфигурации ddev. web/ будет корневым каталогом документов нового проекта и будет содержать определенные файлы settings. Вы подготовили структуру для создания нового проекта Drupal.

      Следующим шагом будет инициализация платформы, когда мы создадим необходимые контейнеры и сетевые конфигурации. DDEV выполняет привязку к портам 80 и 443, и если вы используете на своем компьютере веб-сервер Apache или другие веб-серверы или службы, использующие эти порты, остановите эти службы, прежде чем продолжать.

      Используйте команду ddev start для инициализации платформы:

      Эта команда создаст все контейнеры Docker для вашего проекта, включая веб-контейнер, контейнер базы данных и phpmyadmin. После инициализации вы увидите следующий вывод (номер порта может отличаться):

      Output

      ... Successfully started d9test Project can be reached at http://d9test.ddev.site http://127.0.0.1:32773

      Примечание. Помните, что DDEV запускает контейнеры Docker скрыто. Если вы хотите просмотреть эти контейнеры или убедиться, что они работают, используйте команду docker ps:

      Вместе с другими запущенными контейнерами вы увидите четыре новых контейнера с отдельными образами: php-myadmin, ddev-webserver, ddev-router и ddev-dbserver-mariadb.

      Команда ddev start успешно построила контейнеры и вывела результаты с двумя URL. Хотя в выводимом сообщении говорится, что проект можно найти по URL-адресам http://d9test.ddev.site и http://127.0.0.1:32773, сейчас при попытке открыть любой из этих URL будет выведено сообщение об ошибке. Начиная с Drupal 8, ядро Drupal и модули contrib работают как зависимости. Поэтому нужно предварительно завершить установку Drupal с помощью диспетчера пакетов Composer для проектов PHP, прежде чем в браузере получится что-либо загрузить.

      Одна из самых полезных и элегантных возможностей DDEV — возможность передачи команд Composer через DDEV CLI и в среду с контейнерами. Это означает, что вы можете отделить конфигурацию вашего компьютера от среды разработки. Вам больше не нужно будет управлять путями к различным файлам и зависимостями или решать проблемы с версиями, часто возникающие при разработке PHP на локальном компьютере. Более того, вы сможете быстро и с минимумом трудозатрат переключаться между разными проектами, использующими разные структуры и технологические комплексы.

      Используйте команду ddev composer для загрузки проекта drupal/recommended-project. Она загрузит ядро Drupal, библиотеки и другие связанные ресурсы, а затем создаст проект по умолчанию:

      • ddev composer create "drupal/recommended-project"

      Загрузите заключительный компонент Drush (оболочка Drupal). В этом обучающем модуле мы будем использовать только одну команду drush и предложим альтернативный вариант, но в целом drush — это мощный интерфейс командной строки для разработки Drupal, который может повысить эффективность вашей работы.

      Используйте команду ddev composer для установки drush:

      • ddev composer require "drush/drush"

      Мы создали проект Drupal 9 по умолчанию и выполнили установку drush. Теперь мы откроем проект в браузере и настроим параметры сайта.

      Шаг 3 — Настройка проекта Drupal 9

      Вы установили Drupal 9 и теперь можете открыть свой новый проект в браузере. Для этого вы можете повторно запустить команду ddev start и скопировать один из двух выводимых ей URL, или использовать следующую команду, которая автоматически откроет ваш сайт в новом окне браузера:

      Откроется стандартный мастер установки Drupal.

      Установщик Drupal 9 из браузера

      Теперь у вас есть два варианта. Вы можете использовать этот пользовательский интерфейс и следовать указаниям мастера по установке, или вы можете вернуться в терминал и передать команду drush через ddev. Последний вариант автоматизирует процесс установки и задает admin как имя пользователя и пароль.

      Вариант 1 — Использование мастера

      Снова откройте мастер в браузере. В разделе Choose language (Выбор языка) выберите язык из выпадающего меню и нажмите Save and continue (Сохранить и продолжить). Выберите профиль установки. Вы можете выбрать вариант Standard (Стандартный), Minimal (Минимальный) или Demo (Демо). Сделайте выбор и нажмите Save and continue (Сохранить и продолжить). Drupal автоматически проверит ваши требования, настроит базу данных и установит ваш сайт. Последним шагом будет настройка нескольких конфигураций. Добавьте имя сайта и адрес электронной почты сайта с вашим доменом. Затем выберите имя пользователя и пароль. Выберите надежный пароль и сохраните ваши учетные данные в безопасном месте. В заключение добавьте частный адрес электронной почты, который вы регулярно проверяете, внесите региональные настройки и нажмите Save and continue (Сохранить и продолжить).

      Приветственное сообщение Drupal 9 с предупреждением о разрешениях

      Ваш новый сайт загрузится и выведет приветственное сообщение.

      Вариант 2 — Использование командной строки

      Запустите в корневом каталоге проекта следующую команду ddev exec для установки сайта Drupal по умолчанию с помощью drush:

      • ddev exec drush site:install --account-name=admin --account-pass=admin

      Сайт будет создан так же, как и при использовании мастера, но с некоторыми базовыми конфигурациями. Для имени пользователя и пароля будет установлено значение admin.

      Запустите сайт для просмотра в браузере:

      Теперь вы готовы начать создание своего сайта, но предварительно рекомендуется проверить наличие правильных разрешений для каталога /sites/web/default. При локальной работе это не составляет существенной проблемы, но если вы перенесете эти разрешения на производственный сервер, возникнет риск для безопасности.

      Шаг 4 — Проверка разрешений

      Во время установки мастера или при первой загрузке приветственной страницы вы можете увидеть предупреждение о настройках разрешений для каталога /sites/web/default и одного файла внутри этого каталога: settings.php.

      После запуска установочного скрипта Drupal попробует установить для каталога web/sites/default разрешения на чтение и исполнение для всех групп: это параметр разрешений 555. Также он попытается установить для default/settings.php разрешение «только чтение» или 444. Если вы увидите это предупреждение, запустите эти две команды chmod из корневого каталога проекта. В противном случае возникнет риск для безопасности:

      • chmod 555 web/sites/default
      • chmod 444 web/sites/default/settings.php

      Чтобы убедиться в наличии правильных разрешений, запустите эту команду ls с аргументами a, l, h и d:

      • ls -alhd web/sites/default web/sites/default/settings.php

      Убедитесь, что разрешения соответствуют следующему выводу:

      Output

      dr-xr-xr-x 8 sammy staff 256 Jul 21 12:56 web/sites/default -r--r--r-- 1 sammy staff 249 Jul 21 12:12 web/sites/default/settings.php

      Вы готовы начать создание сайта Drupal 9 на локальном компьютере.

      Шаг 5 — Создание первого поста в Drupal

      Чтобы протестировать определенную функцию Drupal, вы создадите пост с помощью пользовательского веб-интерфейса.

      Нажмите на начальной странице сайта кнопку Content (Содержимое) в левом верхнем меню. Нажмите синюю кнопку Add content (Добавить содержимое). Откроется новая страница. Нажмите Article (Статья), и появится еще одна страница.

      Строка создания статьи в Drupal 9

      Добавьте любые предпочитаемые заголовок и содержимое. Также вы можете добавить изображение, например, одни из обоев DigitalOcean. Когда вы будете готовы, нажмите синюю кнопку Save (Сохранить).

      На вашем сайте появится ваш первый пост.

      Созданный пост в Drupal 9

      Теперь вы создаете сайт Drupal 9 на локальном компьютере без взаимодействия с сервером благодаря Docker и DDEV. На следующем шаге мы будем управлять контейнером DDEV с учетом вашего рабочего процесса.

      Шаг 6 — Управление контейнером DDEV

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

      Вы можете остановить DDEV в любое время для освобождения ресурсов. Запустите следующую команду в каталоге вашего проекта:

      DDEV доступен глобально, так что вы сможете запускать команды ddev из любого места, если вы зададите проект DDEV:

      Также вы можете просмотреть все проекты одновременно с помощью команды ddev list:

      В DDEV имеется много других полезных команд.

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

      Заключение

      В этом обучающем модуле мы использовали Docker и возможности контейнеризации для разработки сайта Drupal на локальной системе с помощью DDEV. Также DDEV интегрируется с несколькими интегрированными средами разработки и имеет встроенную поддержку отладки PHP для Atom, PHPStorm и Visual Studio Code (vscode). Далее вы можете узнать больше о создании сред разработки для Drupal с DDEV или для разработки с использованием других структур PHP, таких как WordPress.



      Source link