Современная архитектура Chart.js основана на модульной системе, в которой практически все функциональные части графика существуют как отдельные регистрируемые сущности. Это позволяет уменьшать итоговый размер бандла за счёт подключения только необходимых компонентов и одновременно даёт возможность расширять библиотеку собственными элементами.
Регистрация выполняется через глобальный реестр, доступный в объекте
Chart. Любой компонент — элемент, контроллер, шкала или
плагин — должен быть явно зарегистрирован до использования.
import { Chart, registerables } from 'chart.js';
Chart.register(...registerables);
Константа registerables содержит полный набор
стандартных компонентов библиотеки. Такой способ удобен для быстрого
старта, но в реальных проектах чаще используется точечная
регистрация.
Элементы отвечают за визуальное представление отдельных частей графика. Каждый тип диаграммы использует собственный набор элементов.
Наиболее распространённые:
ArcElement — дуги (pie, doughnut)LineElement — линии линейных графиковPointElement — точки данныхBarElement — столбцыРегистрация выполняется явно:
import {
Chart,
ArcElement,
LineElement,
PointElement,
BarElement
} from 'chart.js';
Chart.register(ArcElement, LineElement, PointElement, BarElement);
Отсутствие регистрации хотя бы одного используемого элемента приводит к ошибкам рендера, поскольку Chart.js не имеет права неявно подключать зависимости.
Контроллеры определяют поведение конкретного типа графика: как интерпретируются данные, как строятся элементы и как формируется логика отрисовки.
Примеры стандартных контроллеров:
BarControllerLineControllerPieControllerDoughnutControllerРегистрация контроллеров:
import {
Chart,
BarController,
LineController,
PieController,
DoughnutController
} from 'chart.js';
Chart.register(BarController, LineController, PieController, DoughnutController);
Контроллер является центральным звеном между данными и визуальными элементами. Без него Chart.js не понимает, как преобразовать dataset в графическое представление.
Шкалы отвечают за систему координат, масштабирование и преобразование значений в пиксели.
Основные шкалы:
CategoryScale — категориальная ось (часто X)LinearScale — линейная шкалаLogarithmicScale — логарифмическая шкалаTimeScale — временная шкалаПример подключения:
import {
Chart,
CategoryScale,
LinearScale,
LogarithmicScale,
TimeScale
} from 'chart.js';
Chart.register(CategoryScale, LinearScale, LogarithmicScale, TimeScale);
Шкалы являются обязательным компонентом даже для простых графиков, поскольку без них невозможно корректно вычислить координаты точек.
Плагины расширяют функциональность Chart.js, не изменяя базовую структуру графика. Они позволяют внедрять кастомную логику: аннотации, водяные знаки, обработку событий, дополнительную отрисовку.
Регистрация плагина:
const customPlugin = {
id: 'customPlugin',
beforeDraw(chart) {
const { ctx, width, height } = chart;
ctx.save();
ctx.fillText('Текст поверх графика', width / 2, height / 2);
ctx.restore();
}
};
Chart.register(customPlugin);
Каждый плагин обязан иметь уникальный id, иначе возможны
конфликты при регистрации нескольких расширений.
Chart.js предоставляет агрегированный набор всех стандартных компонентов:
import { Chart, registerables } from 'chart.js';
Chart.register(...registerables);
Этот подход включает:
Такой вариант увеличивает размер бандла, но гарантирует отсутствие ошибок из-за нехватки зарегистрированных зависимостей.
В production-сборках предпочтительно подключать только используемые части библиотеки.
Пример минимальной конфигурации для линейного графика:
import {
Chart,
LineController,
LineElement,
PointElement,
LinearScale,
CategoryScale
} from 'chart.js';
Chart.register(
LineController,
LineElement,
PointElement,
LinearScale,
CategoryScale
);
Такой подход уменьшает размер итогового JavaScript и ускоряет загрузку приложения.
Архитектура Chart.js допускает создание собственных контроллеров, элементов и плагинов, что позволяет расширять библиотеку за пределы стандартных возможностей.
Кастомный контроллер наследуется от базового
DatasetController и определяет собственную логику
построения данных.
import { DatasetController, Chart } from 'chart.js';
class CustomController extends DatasetController {
draw() {
const ctx = this.chart.ctx;
this.getMeta().data.forEach((element, index) => {
const value = this.getDataset().data[index];
ctx.save();
ctx.fillStyle = 'blue';
ctx.fillRect(element.x, element.y, value, 10);
ctx.restore();
});
}
}
CustomController.id = 'customController';
CustomController.defaults = {
dataElementType: 'point'
};
Chart.register(CustomController);
Ключевые моменты:
id обязателен для регистрацииdraw() управляет рендерингомthis.getDataset()Элементы определяют, как отдельная часть данных будет визуализирована.
import { Element } from 'chart.js';
class CustomElement extends Element {
draw(ctx) {
const { x, y } = this.getProps(['x', 'y']);
ctx.save();
ctx.beginPath();
ctx.arc(x, y, 6, 0, Math.PI * 2);
ctx.fillStyle = 'red';
ctx.fill();
ctx.restore();
}
}
CustomElement.id = 'customElement';
Chart.register(CustomElement);
Такие элементы часто используются в специализированных графиках, где стандартных форм недостаточно.
Плагины могут использовать хуки жизненного цикла графика:
beforeInitafterDatasetsDrawbeforeUpdateafterRenderПример:
const lifecyclePlugin = {
id: 'lifecyclePlugin',
beforeDatasetsDraw(chart) {
const ctx = chart.ctx;
ctx.save();
ctx.font = '12px sans-serif';
ctx.fillText('Перед отрисовкой датасетов', 10, 20);
ctx.restore();
},
afterRender(chart) {
const ctx = chart.ctx;
ctx.save();
ctx.fillText('После рендера', 10, 40);
ctx.restore();
}
};
Chart.register(lifecyclePlugin);
Плагины получают полный доступ к контексту графика, но не должны напрямую изменять внутреннее состояние без необходимости.
Регистрация компонентов должна учитывать зависимости между ними. Например:
Типичная ошибка — регистрация плагина до подключения базовых шкал или контроллеров, что приводит к отсутствию данных в lifecycle-хуках.
Повторная регистрация компонента с одинаковым id
приводит к его перезаписи. Это может использоваться для переопределения
поведения стандартных компонентов, но требует осторожности.
Chart.register({
id: 'tooltip',
beforeInit() {
console.log('Переопределённый tooltip');
}
});
Такая практика применяется при глубокой кастомизации, но ухудшает предсказуемость поведения библиотеки.
При использовании bundler’ов (Vite, Webpack, Rollup) регистрация должна выполняться до создания экземпляра графика.
import { Chart, LineController, LineElement, CategoryScale, LinearScale } from 'chart.js';
Chart.register(LineController, LineElement, CategoryScale, LinearScale);
new Chart(ctx, {
type: 'line',
data: {
labels: ['A', 'B', 'C'],
datasets: [{ data: [1, 2, 3] }]
}
});
Отсутствие предварительной регистрации приводит к runtime-ошибкам или пустому рендеру графика.
Внутренний реестр Chart.js представляет собой набор карт:
elementscontrollersscalespluginsКаждая категория хранит зарегистрированные сущности по их
id. При инициализации графика происходит разрешение
зависимостей и сбор финального графа рендеринга.
Эта архитектура обеспечивает: