Code splitting в MDX

Code splitting в контексте MDX позволяет разбивать JavaScript-код, связанный с компонентами и контентом, на отдельные фрагменты, которые загружаются по мере необходимости. Это критически важно для производительности, особенно в больших приложениях с большим количеством статей или документации, где каждый MDX-файл может содержать сложные React-компоненты.


Загрузка компонентов по требованию

MDX изначально компилируется в React-компоненты. Каждый MDX-файл при этом превращается в модуль, который можно импортировать. Для code splitting используется динамический импорт:

import React, { Suspense, lazy } from 'react';

const MyMDXComponent = lazy(() => import('./example.mdx'));

export default function App() {
  return (
    <Suspense fallback={<div>Загрузка...</div>}>
      <MyMDXComponent />
    </Suspense>
  );
}

Ключевые моменты:

  • lazy() позволяет отложить загрузку MDX-компонента до момента его рендера.
  • Suspense обеспечивает отображение fallback-контента до загрузки компонента.
  • Динамический импорт создаёт отдельный бандл для каждого MDX-файла, уменьшая размер основного бандла.

Разделение контента и логики компонентов

MDX позволяет смешивать Markdown и React-компоненты. Для оптимизации важно разделять:

  1. Статический контент — текст, заголовки, списки. Он компилируется в JavaScript-код MDX-компонента.
  2. Интерактивные компоненты — кнопки, графики, визуализации. Их можно загружать отдельно с помощью динамического импорта:
const InteractiveChart = lazy(() => import('../components/Chart'));

export const MDXContent = () => (
  <>
    <h1>Анализ данных</h1>
    <Suspense fallback={<div>Загрузка графика...</div>}>
      <InteractiveChart />
    </Suspense>
  </>
);

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


Оптимизация маршрутизации

При использовании React Router или Next.js можно реализовать ленивую загрузку MDX на уровне маршрутов:

const DocsPage = lazy(() => import('./docs/GettingStarted.mdx'));

<Route path="/docs/getting-started" element={
  <Suspense fallback={<div>Загрузка документации...</div>}>
    <DocsPage />
  </Suspense>
} />

Преимущества:

  • MDX-контент загружается только при посещении конкретного маршрута.
  • Основной бандл остаётся лёгким, улучшая SEO и скорость начальной загрузки.

Использование @mdx-js/runtime и динамического импорта

Если MDX-контент создаётся динамически (например, загружается из CMS), можно использовать @mdx-js/runtime:

import { MDXProvider } from '@mdx-js/react';
import { mdx } from '@mdx-js/runtime';
import { Suspense, lazy } from 'react';

const components = {
  Chart: lazy(() => import('./Chart'))
};

export default function DynamicMDX({ content }) {
  return (
    <MDXProvider components={components}>
      <Suspense fallback={<div>Загрузка...</div>}>
        {mdx(content)}
      </Suspense>
    </MDXProvider>
  );
}

Особенности:

  • Компоненты передаются через MDXProvider.
  • Каждый интерактивный компонент можно лениво импортировать.
  • Это позволяет разделять загрузку MDX-контента и сложных визуализаций.

Советы по эффективному code splitting

  • Делить MDX-файлы логически: один файл = один модуль. Не объединять все статьи в один бандл.
  • Лениво загружать тяжёлые компоненты: графики, таблицы, видео.
  • Использовать fallback-контент: улучшает UX при медленном интернете.
  • Интегрировать с инструментами сборки: Webpack и Vite автоматически создают чанки для динамических импортов.

Пример структуры проекта с ленивой загрузкой MDX

src/
  components/
    Chart.jsx
    VideoPlayer.jsx
  docs/
    Introduction.mdx
    Advanced.mdx
  App.jsx

В App.jsx:

import { Suspense, lazy } from 'react';

const Introduction = lazy(() => import('./docs/Introduction.mdx'));
const Advanced = lazy(() => import('./docs/Advanced.mdx'));

export default function App() {
  return (
    <div>
      <Suspense fallback={<div>Загрузка...</div>}>
        <Introduction />
      </Suspense>
    </div>
  );
}

MDX и code splitting вместе позволяют создавать масштабируемые, быстрые и интерактивные веб-приложения, где контент и сложные React-компоненты загружаются только по мере необходимости. Это снижает нагрузку на основной бандл и улучшает пользовательский опыт.