Розробка фронтенду на TypeScript для 1С-Бітрікс

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Розробка фронтенду на TypeScript для 1С-Бітрікс
Середня
~1-2 тижні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Розробка фронтенду на TypeScript для 1С-Бітрікс

Розрив між PHP-бекендом Бітрікс та фронтендом на ванільному JavaScript — стандартна історія. Компоненти каталогу, кошик, фільтри, особистий кабінет — кожна з цих сутностей живе на перетині PHP-логіки та клієнтського коду. TypeScript закриває прогалини на цьому стику: контракт між PHP та JS описується у типах, а компілятор ловить невідповідності до деплою.

Розробка фронтенду на TypeScript для 1С-Бітрікс

Архітектура фронтенду Бітрікс-проекту

Два полярних підходи до фронтенду Бітрікс:

  1. MPA (Multi-Page Application) — класичний підхід. PHP рендерить сторінки, TypeScript додає інтерактивність: фільтр на AJAX, кошик через REST, галереї, карти. Кожен компонент — окремий ізольований TS-модуль.

  2. SPA/Headless — React або Vue на TypeScript споживають Бітрікс як API. Повний фронтенд написаний на TypeScript, PHP забезпечує лише дані та бізнес-логіку.

Більшість проєктів — перший підхід з елементами другого в критичних частинах (каталог, кошик).

Структура фронтенду в шаблоні Бітрікс

/local/templates/my_site/
├── src/
│   ├── components/
│   │   ├── catalog/
│   │   │   ├── CatalogFilter.ts
│   │   │   ├── ProductCard.ts
│   │   │   └── CartButton.ts
│   │   ├── cart/
│   │   │   ├── CartDrawer.ts
│   │   │   └── CartCounter.ts
│   │   └── common/
│   │       ├── Modal.ts
│   │       └── Tooltip.ts
│   ├── api/
│   │   ├── catalog.ts
│   │   ├── cart.ts
│   │   └── user.ts
│   ├── types/
│   │   ├── bitrix.d.ts
│   │   └── api.ts
│   ├── utils/
│   │   ├── http.ts
│   │   └── format.ts
│   └── main.ts
├── dist/
├── package.json
├── tsconfig.json
└── vite.config.ts

Компонент кошика на TypeScript

Повноцінний приклад компонента кошика з типами:

// api/cart.ts
interface CartItem {
    id:       number;
    name:     string;
    price:    number;
    quantity: number;
    img:      string | null;
}

interface CartState {
    items:      CartItem[];
    totalPrice: number;
    totalCount: number;
    currency:   string;
}

interface AddToCartPayload {
    productId: number;
    quantity:  number;
    properties?: Record<string, string>;
}

export async function addToCart(payload: AddToCartPayload): Promise<CartState> {
    const formData = new FormData();
    formData.append('sessid',     BX.bitrix_sessid());
    formData.append('action',     'addItem');
    formData.append('product_id', String(payload.productId));
    formData.append('quantity',   String(payload.quantity));

    if (payload.properties) {
        Object.entries(payload.properties).forEach(([k, v]) => {
            formData.append(`props[${k}]`, v);
        });
    }

    const res = await fetch('/local/ajax/cart.php', {
        method: 'POST',
        body:   formData,
    });

    if (!res.ok) throw new Error(`Cart error: ${res.status}`);

    const json = await res.json();
    if (json.status !== 'success') throw new Error(json.error ?? 'Cart error');

    return json.cart as CartState;
}
// components/cart/CartButton.ts
import { addToCart }   from '@/api/cart';
import { updateCartUI } from './CartCounter';

export function initAddToCartButtons(): void {
    document.querySelectorAll<HTMLButtonElement>('[data-action="add-to-cart"]')
        .forEach(btn => btn.addEventListener('click', handleAddToCart));
}

async function handleAddToCart(e: MouseEvent): Promise<void> {
    const btn = e.currentTarget as HTMLButtonElement;
    const productId = Number(btn.dataset['productId']);
    if (!productId) return;

    btn.disabled = true;
    btn.classList.add('loading');

    try {
        const cart = await addToCart({ productId, quantity: 1 });
        updateCartUI(cart.totalCount, cart.totalPrice);
        showAddedFeedback(btn);
    } catch (err) {
        console.error('Add to cart failed:', err);
        showError(btn);
    } finally {
        btn.disabled = false;
        btn.classList.remove('loading');
    }
}

Інтеграція з PHP-компонентами через data-атрибути

Передача даних з PHP у TypeScript без глобальних змінних:

// template.php компонента каталогу
<div
    id="catalog-app"
    data-section-id="<?= (int)$arResult['SECTION']['ID'] ?>"
    data-iblock-id="<?= (int)$arParams['IBLOCK_ID'] ?>"
    data-initial-filter='<?= htmlspecialchars(
        json_encode($arResult['FILTER_PARAMS']), ENT_QUOTES
    ) ?>'
>
    <?php // SSR-верстка для першого завантаження ?>
</div>
// TypeScript зчитує дані типізовано
const appEl = document.getElementById('catalog-app');
if (!appEl) throw new Error('#catalog-app not found');

const sectionId  = Number(appEl.dataset['sectionId']);
const iblockId   = Number(appEl.dataset['iblockId']);
const rawFilter  = appEl.dataset['initialFilter'] ?? '{}';
const initFilter = JSON.parse(rawFilter) as FilterState;

Стан без React: патерн EventEmitter

Для MPA-проєктів не завжди потрібен React. Легкий EventEmitter синхронізує компоненти:

// utils/EventBus.ts
type Handler<T> = (payload: T) => void;

class EventBus {
    private handlers: Map<string, Handler<unknown>[]> = new Map();

    on<T>(event: string, handler: Handler<T>): void {
        const list = this.handlers.get(event) ?? [];
        list.push(handler as Handler<unknown>);
        this.handlers.set(event, list);
    }

    emit<T>(event: string, payload: T): void {
        this.handlers.get(event)?.forEach(h => h(payload as unknown));
    }
}

export const bus = new EventBus();

// Використання: кошик сповіщає хедер
bus.emit<CartState>('cart:updated', newCartState);
bus.on<CartState>('cart:updated', (cart) => {
    updateCartCounter(cart.totalCount);
});

Збірка та деплой

// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
    root:  'src',
    build: {
        outDir:          '../dist',
        emptyOutDir:     true,
        rollupOptions: {
            input: {
                main:    'src/main.ts',
                catalog: 'src/pages/catalog.ts',
                cart:    'src/pages/cart.ts',
            },
        },
    },
    resolve: {
        alias: { '@': '/src' },
    },
});

Окремі entrypoint для кожного розділу сайту — лише потрібний JS завантажується на сторінці.

Терміни

Завдання Терміни
Налаштування TypeScript + Vite, архітектура модулів 1–2 дні
Розробка компонентів каталогу (фільтр, список, картка) 3–5 днів
Розробка кошика та міні-кошика в шапці 2–3 дні
Міграція існуючого JS-коду на TypeScript 2–5 днів (залежить від обсягу)