В данной статье мы рассмотрим правильный подход к Frontend разработке в Bitrix. А именно разделение на расширения которые сможем подключать в любой момент времени. + напишем SPA приложение на Vue и React.

P.S. данная статья получится очень сложной, но я попытаюсь написать как можно проще и понятнее и при этом не упускать важные моменты.


Простое расширение

Hidden text

Расширения нужны для того чтобы разделять JavaScript на модули и использовать тогда когда они нам нужны. Условно у вас есть любая библиотека. Но вы не хотите подгружать её везде.

С одной стороны вы можете просто подключить скрипт там где вам надо используя тег <script> или подключить через API Bitrix и не обязательно хранить JS в глобальном файле. Но тогда мы теряем возможность подгружать JS + CSS в любой момент времени используя библиотеку Bitrix.

Особенно данный подход хорош когда идет разработка UI компонентов. По этому все таки отдельный JS лучше хранить как расширение.

Для проекта это будет плюс так как это проще поддерживать. Да и сама структура и готовые функции от разработчиков Bitrix - к этому располагают.

Для начала стоит узнать где хранить наши расширения иначе API Bitrix работать не будет. Они должны находится в папке local / js / папка с раширениями / ваши_расширения.


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

Структура папок
Структура папок

Чтобы наше расширение можно было подключить, у нас внутри обязательно должен быть конфигурационный файл (config.php).

Пока что в нем опишем простое подключение css и js файлов.

<?if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();

return [
	'css' => './style.css',
	'js' => './script.js',
];

Далее немного стилизуем наше расширение.

.hello-world {
    font-size: 40px;
    font-style: italic;
    color: blueviolet;
}

Напишем простой JavaScript код.

document.addEventListener("DOMContentLoaded", () => {
  document.querySelector("body").innerHTML = "<h1 class='hello-world'>Hello world from ext</h1>";
});

И после открываем файл где собираемся вызвать наше расширение и подключаем его.

<?require($_SERVER["DOCUMENT_ROOT"]."/bitrix/header.php");
Bitrix\Main\UI\Extension::load("myextensions.myext");

Получаем вот такой результат:

index.php - страница на которой подключил свое расширение
index.php - страница на которой подключил свое расширение

Теперь поучимся рантаймить наши расширения. Для этого удаляем строчку с подключением нашего расширения.

<?Bitrix\Main\UI\Extension::load("myextensions.myext");

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

document.querySelector("body").innerHTML =
  "<h1 class='hello-world'>Hello world from ext</h1>";

Далее открываем консоль на нашей странице и пишем следующий код.

BX.Runtime.loadExtension('myextensions.myext')

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

Результат динамичной загрузки расширения.
Результат динамичной загрузки расширения.

Подробнее с рантаймами расширений можно ознакомиться в документации: https://dev.1c-bitrix.ru/api_help/js_lib/runtime/Runtime_loadExtension.php

Теперь поучимся подключать зависимости.


Подключение зависимостей

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

Так скажем сделали свое API / Библиотеку / Зависимость, не важно.

Для примера я сделаю набор функций которые будут использоваться для других модулей расширений.

В utils размещу вот такие функции.

function sum(a, b) {
  return a + b;
}

function multiplication(a, b) {
  return a * b;
}

А в первом расширении подключу зависимости в config.php.

<?if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();

return [
	'css' => './style.css',
	'js' => './script.js',
	'rel' => [
		'myextensions.utils'
	]
];

Далее я могу использовать свои функции внутри первого расширения.

console.log(sum(1, 2));
console.log(multiplication(2, 2));
Результат выполнения
Результат выполнения

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

const sumResult = sum(1, 2);
const multiplicationResult = multiplication(2, 2);

Вот так будет выглядеть структура дочернего расширения.

Структура файлов
Структура файлов

Далее конфигурация.

<?if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();

return [
	'js' => './script.js',
	'rel' => [
		'myextensions.myext'
	]
];

И сам скрипт.

console.log(sumResult);
console.log(multiplicationResult);

Результат так же увидите в консоли, а теперь переходим к инструменту сборки расширений от разработчиков Bitrix.


Bitrix CLI

@bitrix/cli — консольный инструмент Битрикс-разработчика. Основная цель — упростить и автоматизировать разработку фронтенда для проектов на 1С-Битрикс: Управление Сайтом и Битрикс24.

Данная цитата взята из документации: https://dev.1c-bitrix.ru/learning/course/index.php?COURSE_ID=43&LESSON_ID=12435&LESSON_PATH=3913.3516.4776.3635.12435

В целом не самая удобная штука, но работать с ней можно.

Hidden text

Опять же многие могут написать:
Зачем? Почему? Почему нельзя использовать другие сборщики, собрать все в bundle и подключить экстеншен. Ну или просто выкинуть битрикс как API и нормально писать фронт.

Сразу отвечу что так можно делать и предпочтительно если не хотите страдать с настройкой и дополнением сборщика npm пакетами.

Опять же Bitrix CLI больше как + для тех кто хочет получить сертификат. Ну и прям для фанатов битры само собой.

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

Чтобы bitrix cli работал нам внутри нашего расширения нужно создать конфигурационный файл bundle.config.js

Структура файлов
Структура файлов

Далее добавляем в bundle.config.js следующий код

module.exports = {
  input: "./script.js",
  output: "./dist.js",
};

В данном коде мы принимаем script.js и на выходе получаем собранный наш script.js - так называемый bundle, на данный момент не будем запариваться с названиями и папками.

В терминале переходим в local и пишем команду

bitrix build

Перед этим я удалил весь код в script.js и у нас должно было получится что то вроде этого

/* eslint-disable */
(function (exports) {
	'use strict';



}((this.window = this.window || {})));
//# sourceMappingURL=dist.js.map

Ну если вы не удаляли код из script.js соответственно там должен быть еще и ваш код.

Далее рассмотрим некоторые свойства из документации и узнаем как они работают.

Hidden text
// Неймспейс, в который будут добавлены все экспорты из файла, 
// указанного в input. Например, 'BX.Main.Filter'
namespace: string,

Данная опция добавляет в window объект соответствующий строке namespace, через точку указывается вложенность объекта.

/* eslint-disable */
this.BX = this.BX || {};
(function (exports) {
	'use strict';



}((this.BX.MyExt = this.BX.MyExt || {})));
//# sourceMappingURL=dist.js.map

plugins: {
		// Переопределяет параметры Babel.
		// Можно указать собственные параметры Babel
		// https://babeljs.io/docs/en/options
		// Если указать false, то код будет собран без транспиляции
		babel: boolean | Object,
		
		// Дополнительные плагины Rollup, 
		// которые будут выполняться при сборке бандлов 
		custom: Array<string | Function>,
          
        //В документации данное свойство не добавили
        //Данная опция позволяет разрешать использование плагинов или отключать
        //По дефолту false
        resolve: Boolean
	},

Данная опция будет крайне полезная если будете собирать к примеру React или Vue приложения.

// Включает или отключает минификацию. 
// По умолчанию отключено. 
// Может принимать объект настроек Terser:
// false — не минифицировать (по умолчанию)
// true — минифицировать с настройками по умолчанию 
// object — минифицировать с указанными настройками 
minification: boolean | object,

Из описания все понятно. Рекомендую для продакшена использовать данное свойство.

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

Благодаря Bitrix CLI мы можем использовать ES6 Modules в разработке наших расширений.

Простые примеры с импортами не имеют смысла лучше покажу сразу сборку SPA на Vue.


SPA на Vue

Стоит сразу пояснить пару моментов, а кому они не интересны - смотрите дальше.

Hidden text

Сначала мы будем использовать интегрированный Vue комнандой Bitrix так как CLI c ним работает и позволяет использовать его на максимум.

Кому этот вариант не подходить, он может внутри поставить свой Vue с Typescript и чем угодно, и просто уже без Bitrix CLI собирать свое приложение, а после в config.php указываем путь к нашему собранному бандлу.

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

В дальнейшем я покажу как я собирал React и на основе этого уже сможете делать и другие штуки.

Для создания SPA на Vue для начала надо импортануть её (я буду использовать Vue3)

import { createApp } from "ui.vue3";

Логика импортов тут точно такая же как при подключении расширения. А так же при сборке наш config.php дополниться нужными зависимостями автоматически.

Напишем для начала простое приложение без компонентов.

import { createApp } from "ui.vue3";

document.addEventListener("DOMContentLoaded", () => {
  createApp({
    data() {
      return {
        counter: 1,
      };
    },
    mounted() {
      setInterval(() => {
        this.counter++;
      }, 1000);
    },
    template: `<div>{{ counter }}</div>`,
  }).mount("#root");
});

Так как размещать верстку внутри не очень удобно, людям которые используют VSCode рекомендую поставить это расширение чтобы облегчить работу и читаемость своего кода, ну или другие подобные плагины.

https://marketplace.visualstudio.com/items?itemName=pushqrdx.inline-html

Далее вынесем это все в компонент и потом сделаем роутинг.

import { createApp } from "ui.vue3";
import App from "./components/App";

document.addEventListener("DOMContentLoaded", () => {
  createApp(App).mount("#root");
});

Статья уже получается довольно большой по этому просто покажу структуру и каждый файл.

Структура нашего SPA
Структура нашего SPA

Index.js

import { createApp } from "ui.vue3";
import { createRouter, createWebHashHistory } from "ui.vue3.router";

import Test from "./pages/Test";
import About from "./pages/About";
import App from "./App";

const router = createRouter({
  routes: [
    {
      path: "/",
      component: Test,
    },
    {
      path: "/about",
      component: About,
    },
  ],
  history: createWebHashHistory(),
});

document.addEventListener("DOMContentLoaded", () => {
  createApp(App).use(router).mount("#root");
});

App.js

import About from "./pages/About";
import Test from "./pages/Test";

export default {
  components: { About, Test },
  template: /*html*/ `<div>
        <nav>
            <ul>
                <li><router-link to="/">Test</router-link></li>
                <li><router-link to="/about">About</router-link></li>
            </ul>
        </nav>
        <main>
            <router-view />
        </main>
    </div>`,
};

Test.js

export default {
  data() {
    return {
      name: "Тестовая страница",
    };
  },
  template: /*html*/ `
    <div>
      <h1>{{ name }}</h1>
    </div>
  `,
};

About.js

import Counter from "../components/Counter";

export default {
  name: "About",
  components: {
    Counter,
  },
  template: /*html*/ `
    <div>
        <h2>Счетчик на странице About</h2>
        <Counter/>
    </div>
  `,
};

Counter/index.js и style.css

import "./style.css";

export default {
  name: "Counter",
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    reset() {
      this.count = 0;
    },
  },
  mounted() {
    setInterval(() => this.count++, 300);
  },
  template: /*html*/ `
    <div>
      <button class="button-counter" @click="reset">reset count</button>

      counter : {{ count }}
    </div>
  `,
};
.button-counter {
    background-color: blueviolet;
    color: white;
}

Далее запускаем сборщик. И наблюдаем следующий результат.

Hidden text

можно так же писать bitrix build -w

и он будет отслеживать изменения в ваших файлах и собирать заново при каждом сохранении.

И вот наш роутинг готов. Можно было сюда запихнуть ещё работу с Pinia или Vuex из коробки, но я думаю сами разберетесь там не сложно.

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

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

Сборка React внутри Bitrix

А теперь все будет очень просто и знакомо для Frontend разработчиков.

Заходим в терминал, переходим в директорию где хранятся наши расширения и просто пишем npx create-react-app [ur project name] [template]

После этого

  1. Переходим в наш проект и запускаем команду для билда, в моем случае это будет npm run build.

  2. Далее внутри создаем config.php.

  3. И запускаем подключаем наше расширение на страницу.

  4. И проверяем результат.

config.php
config.php
Что должно было получиться.
Что должно было получиться.

У вас будет проблема только с путями к картинке, но это не проблема)))

Точно так же можно работать с абсолютно любыми приложениями, кроме того пока разработчики Bitrix не завезут нормальную настройку своего сборщика, данных подход будет оптимальным, так как Frontend разработчики будут комфортно работать в своей среде, и даже смогут использовать JS от Bitrix, если будут получать данные от контроллеров/модулей/компонентов Bitrix

Возможно ли настроить сборку React через Bitrix CLI?

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

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

Комментарии (4)


  1. Moonlight1401
    27.04.2024 07:42

    Зачем в заголовке статьи слово "экстеншен" на кириллице, если далее по статье употребляется удобоваримое слово "расширение"?


    1. ZiZIGY Автор
      27.04.2024 07:42

      Справедливое замечание) уже поправил


  1. vfadeev_sam
    27.04.2024 07:42

    Зачем мне Битрикс, если я пишу на Vue?


    1. ZiZIGY Автор
      27.04.2024 07:42

      Тут идет речь о фронтовской части в Битрикс (да, можно выкинуть её как API, но условия бывают разные). А если конкретнее то идет речь о том, как стоит организовать работу фронтов если такие имеются, и если планируется использование каких то npm пакетов. Чтобы не подключать их через CDN и всегда была возможность пересобрать фронт. + Битрикс понадобится если на проекте есть бэкэнд модули или ООП компоненты с которыми придется взаимодействовать через BX.ajax