Написать пост

Нестандартный подход в разработке: как мы интегрировали React с «Битрикс»

Аватарка пользователя Дмитрий Талызин

Как интегрировать фронтендную часть на React в сайт, построенный на Bitrix, с использованием Next.js и AJAX.

Наша команда сталкивается с самыми разными ситуациями при решении задач клиента. И вот одна из них.

Клиент попросил сделать апгрейд сайта, который мы разработали несколько лет назад — профессиональный ресурс для диджеев JesteiPool. Сервис позволяет диджеям скачивать сеты для своей профессиональной деятельности. Сайт был написан на «1С-Битрикс».

Заказчик решился на глобальные изменения:

  1. Полностью изменить дизайн.
  2. Добавить элементы соцсети — ленту новостей; функцию подписки на обновления различных исполнителей и плейлисты; отображение порядка треков и плейлистов должно автоматически меняться в зависимости от предпочтений, выбранных пользователем.
  3. Сделать беспрерывное воспроизведение плейлистов пользователей.

Важное условие — все изменения необходимо было реализовать на «1С-Битрикс». Это связано со следующими факторами:

  • На сайте накопилось много контента.
  • Заказчик привык к интерфейсу админки «1С-Битрикс».

Однако в процессе изучения требований заказчика стало понятно, что реализовать изменения на сайте, разработанном на «1С-Битрикс», проблематично. Трудности оказались связаны с технологией работы классических монолитных сайтов.

Как работают монолитные сайты

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

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

Поэтому мы поняли, что нам нужно двигаться в сторону SPA (Single Page Application).

SPA – это веб-приложение, которое использует единственный HTML-документ в качестве оболочки для всех веб-страниц. Благодаря этому взаимодействие с пользователем организуется через динамически подгружаемые HTML, CSS, JavaScript, как правило, посредством AJAX.

Для создания такого веб-приложения мы решили бэк оставить на Битриксе, просто создав удобное REST API на его базе, а фронт же писать на React или Vue.

React или Vue

React — библиотека JavaScript. Это профессиональный продукт, который постоянно развивается. Vue — JavaScript-фреймворк с открытым исходным кодом для создания пользовательских интерфейсов, разрабатывался любительским сообществом. Но React популярнее и имеет более интересные перспективы.

Мы решили остановиться на React. Тем более всегда полезно получить опыт, который наверняка пригодится в дальнейшей работе.

Плюсы технологии React

  1. Высокая скорость работы высоконагруженных систем. Благодаря технологии Single Page Application вместо перезагрузки всей страницы контент меняется точечно. Сервер возвращает пользователю легкий JSON вместо тяжеловесного HTML. Благодаря этому возможна непрерывная работа плеера.
  2. Разделены роли фронтенд- и бэкенд-разработчиков. Клиентские и серверные приложения взаимодействуют посредством технологии Rest API.
  3. Модульность React, которая позволяет просто, быстро и дешево обслуживать сайт.
    Но мы выделили один огромный минус сайтов с технологией Single Page Application — они не подходят для SEO-оптимизации. Для пользователя с сервера подгружаются данные в формате JSON, в браузере формируется сама страница, и когда поисковик заходит на такую страницу, он видит не HTML, а Java-скрипт.

Надстройка Next.js над React для SEO

Чтобы решить эту задачу, мы использовали надстройку Next.js над React для SEO. При этом используется серверный рендеринг. Когда пользователь заходит на сайт в первый раз, страница формируется на сервере и возвращается. Но потом страницы формируются в браузере. А так как поисковики получают информацию с сервера, SEO в этом случае отлично работает.

Результат работы

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

1. Главная страница

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

Нестандартный подход в разработке: как мы интегрировали React с «Битрикс» 1

2. Редактирование своих плейлистов в личном кабинете.

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

Нестандартный подход в разработке: как мы интегрировали React с «Битрикс» 2
			import React, { FC, useState } from 'react';

import {
  closestCenter, DndContext, KeyboardSensor, PointerSensor, useSensor, useSensors
} from '@dnd-kit/core';

import {
  arrayMove, rectSortingStrategy, SortableContext, sortableKeyboardCoordinates, useSortable
} from '@dnd-kit/sortable';

import { CSS } from '@dnd-kit/utilities';

import { LibraryPlaylist } from '@/api/Library/libraryPlaylists';

import PlaylistEditTile
  from '@/ui/components/Library/Playlists/PlaylistsEditTilesList/PlaylistsEditTile/PlaylistEditTile';

import styles from './PlaylistsEditTilesList.module.scss';

interface PlaylistsEditTilesListProps {
  sortedPlaylists: LibraryPlaylist[];
  setSortedPlaylists: (playlists: LibraryPlaylist[]) => void;
}

const SortableItem = (props: {
  id?: any;
  playlist?: any;
  style?: any;
  disableDrag?: boolean;
  setDisableDrag?: any;
}) => {
  const { attributes, listeners, setNodeRef, transform, transition } = useSortable({
	id: props.id,
	disabled: props.disableDrag
  });

  const { playlist, style } = props;
  
  const innerStyle = {
	transform: CSS.Transform.toString(transform),
	transition,
	...style
  };

  return (
	<PlaylistEditTile
  	innerStyle={innerStyle}
  	setNodeRef={setNodeRef}
  	key={playlist.id}
  	width={'100%'}
  	height={'100%'}
  	attributes={attributes}
  	listeners={listeners}
  	playlist={playlist}
  	setDisableDrag={props.setDisableDrag}
	/>
  );
};

const PlaylistsEditTilesList: FC<PlaylistsEditTilesListProps> = ({
  sortedPlaylists,
  setSortedPlaylists
}) => {
  const [disableDrag, setDisableDrag] = useState(false);
  const sensors = useSensors(
	useSensor(PointerSensor, {
  	activationConstraint: {
    	distance: 10
  	}
	}),
	useSensor(KeyboardSensor, {
  	coordinateGetter: sortableKeyboardCoordinates
	})
  );
  const handleDragEnd = (event: { active: any; over: any }) => {
	const { active, over } = event;
	if (active.id !== over.id) {
  	const oldIndex = sortedPlaylists.findIndex((playlist) => playlist.id === active.id);
  	const newIndex = sortedPlaylists.findIndex((playlist) => playlist.id === over.id);
  	const newArray = arrayMove(sortedPlaylists, oldIndex, newIndex);
  	setSortedPlaylists(newArray);
	}
  };

  return (
	<>
  	<DndContext
    	sensors={sensors}
    	collisionDetection={closestCenter}
    	onDragEnd={handleDragEnd}
    	autoScroll={false}
  	>

    	<SortableContext
      	items={sortedPlaylists}
      	strategy={rectSortingStrategy}
    	>
    	
      	<div className={styles.container}>
        	{sortedPlaylists.map((playlist) => (
          	<SortableItem
            	playlist={playlist}
            	key={playlist.id}
            	id={playlist.id}
            	disableDrag={disableDrag}
            	setDisableDrag={setDisableDrag}
          	/>
        	))}
      	</div>
    	</SortableContext>
  	</DndContext>
	</>
  );
};

export default PlaylistsEditTilesList;
		

3. Поп-ап с настройками плеера.

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

			import { FC, useRef, useState } from 'react';
import { useSelector } from 'react-redux';
import classNames from 'classnames';
import { fadeInStyle, useOpacity } from '@/utils/useOpacity';
import PlayerPopupActionButtons from './components/PlayerPopupActionButtons/PlayerPopupActionButtons';
import PlayerPopupSizeButtons from './components/PlayerPopupSizeButtons/PlayerPopupSizeButtons';
import styles from './PlayerSettingsPopup.module.scss';
import { RootState } from '@/store/types';

interface PlayerSettingsPopupProps {
  buttonRef: React.RefObject<HTMLButtonElement>;
  setIsPopupOpen: (isPopupOpen: boolean) => void;
  isInitialPage: boolean;
  isPlayerSmall: boolean;
  setIsPlayerSmall: (isPlayerSmall: boolean) => void;
  trackId: string;
}

const PlayerSettingsPopup: FC<PlayerSettingsPopupProps> = ({
  buttonRef,
  setIsPopupOpen,
  trackId,
  isPlayerSmall,
  setIsPlayerSmall,
}) => {
  const isLightTheme = useSelector<RootState, boolean>((state) => state.appState.isLightTheme);
  const position = buttonRef.current?.getBoundingClientRect();
  const heightRef = useRef<HTMLDivElement>(null);
  const heightShift = heightRef.current?.clientHeight;
  const [isClosing, setIsClosing] = useState(false);
  const handleClose = (e: any) => {
	if (e.target === e.currentTarget) {
  	setIsClosing(true);
  	setTimeout(() => {
    	setIsPopupOpen(false);
  	}, 200);
	}
  };
  const opacity = useOpacity();
  return (
	<div
  	style={{
    	...fadeInStyle(opacity, 0),
  	}}
  	className={classNames(styles.container, {
    	[styles.light]: isLightTheme,
    	[styles.closing]: isClosing,
  	})}

  	onClick={handleClose}
	>
  	<div
    	style={{
      	position: 'absolute',
      	top: (position?.top || 0) - (heightShift || 0),
      	left: (position?.left || 0) + 30,
    	}}
    	ref={heightRef}
  	>

    	<PlayerPopupActionButtons
      	trackId={trackId}
      	show={true /*!isInitialPage*/}
      	close={() => setIsPopupOpen(false)}
    	/>

    	<PlayerPopupSizeButtons isPlayerSmall={isPlayerSmall} setIsPlayerSmall={setIsPlayerSmall} />
  	</div>
	</div>
  );
};

export default PlayerSettingsPopup;
		

Для работы на React необходима более высокая квалификация разработчика. Это не просто верстка, а работа в отдельном приложении. Проект пишет фронтенд-разработчик, а не верстальщик.

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

Следите за новыми постами
Следите за новыми постами по любимым темам
5К открытий5К показов