Vue — vue-chartjs でデータ可視化
テクノロジ系 / L15 data-visualization — vue-chartjs でデータ可視化
Vue — vue-chartjs でデータ可視化
モジュール明示登録と再利用可能なチャート部品
data-visualization / ch01
このチャプターの到達ゴール
| 翌日できること |
|---|
必要な chart.js モジュールを ChartJS.register で明示登録してチャートを描ける |
register を1つ外すと描画が消えることを実機で確認し、何を登録すべきか tree-shaking の観点で言える |
labels / data を props 化して、同じチャート部品を別データで再利用できる |
data と options を分離し、色を CSS 変数に逃がしてテーマ連動を壊さない |
今日の題材:学習スキル自己評価ダッシュボード
5 分野(読解・実装・テスト・設計・運用)の理解度を レーダーチャートで可視化します。
データは架空。たとえばこんな配列です。
const labels = ['読解', '実装', 'テスト', '設計', '運用'] const scores = [4, 3, 2, 4, 3] // 5段階の自己評価
ゴールは「この5つの数字を、誰かに見せられる図にする」こと。
そして 同じ部品を月別売上の棒グラフにも使い回せる状態を目指します。
Part A — なぜ「明示登録」が要るのか
まず動かしてみる(完成版)
<script setup lang="ts">
import { Radar } from 'vue-chartjs'
import {
Chart as ChartJS,
RadialLinearScale, PointElement, LineElement,
Filler, Tooltip, Legend,
} from 'chart.js'
ChartJS.register(
RadialLinearScale, PointElement, LineElement,
Filler, Tooltip, Legend,
)
const data = {
labels: ['読解', '実装', 'テスト', '設計', '運用'],
datasets: [{ label: '今月', data: [4, 3, 2, 4, 3] }],
}
</script>
<template>
<Radar :data="data" />
</template>これでレーダーチャートが描画されます。注目は ChartJS.register(...) の行です。
chart.js は「全部入り」ではない
昔のグラフライブラリは、import した瞬間に全機能が付いてきました。
chart.js は逆で、使う部品だけを自分で登録する設計です。
graph LR ALL[chart.js<br/>全機能] -->|tree-shaking| BUNDLE[実際のバンドル<br/>= register したものだけ]
理由は tree-shaking。
「使っていないコードはビルド時に削ぎ落とす」仕組みに乗せるため、
chart.js は「何を使うか」をコード上で明示させます。
登録していない部品は、バンドルに含まれず存在しないも同然になります。
レーダーチャートに要る部品
レーダー1枚を描くだけでも、複数の部品の組み合わせです。
| 登録するもの | 役割 |
|---|---|
RadialLinearScale | 中心から放射状に伸びる軸(レーダー特有) |
PointElement | 各頂点の点 |
LineElement | 頂点をつなぐ線 |
Filler | 線の内側の塗りつぶし |
Tooltip | ホバー時の数値表示 |
Legend | 凡例(label の表示) |
棒グラフなら BarElement と CategoryScale / LinearScale、
というように チャートの種類ごとに必要な部品が違います。
register は「最初に1回」
ChartJS.register(...) は、アプリ起動時に1回効けば十分です。
- 部品ごとのコンポーネントに毎回書く必要はない
- 共通の入口(
main.tsや専用の登録ファイル)にまとめるのが定石 - 「描けない」と思ったら、まず その部品を register したかを疑う
> このトピックでは流れを追いやすいよう、各サンプルの <script setup> 内に
> register を書いています。実プロジェクトでは1箇所に集約してください。
Part B — 再利用可能なチャート部品を props で
べた書きの何が困るのか
さきほどの完成版は、データがコンポーネントの中に埋まっています。
<script setup lang="ts">
const data = {
labels: ['読解', '実装', 'テスト', '設計', '運用'],
datasets: [{ label: '今月', data: [4, 3, 2, 4, 3] }],
}
</script>これだと「先月分のレーダー」をもう1枚出したいとき、
同じファイルをまるごとコピーするしかありません。
月別売上の棒グラフにも当然使い回せない。
labels と data を props で受け取る
データを外から渡せる形にします。defineProps<Props>() で型を付けます。
<!-- SkillRadar.vue -->
<script setup lang="ts">
import { computed } from 'vue'
import { Radar } from 'vue-chartjs'
// (register は入口ファイルで実施済みとする)
interface Props {
labels: string[]
values: number[]
seriesLabel?: string
}
const props = defineProps<Props>()
const chartData = computed(() => ({
labels: props.labels,
datasets: [{ label: props.seriesLabel ?? 'スコア', data: props.values }],
}))
</script>
<template>
<Radar :data="chartData" />
</template>同じ部品を2箇所で使い回す
呼び出し側は、データを渡すだけ。
<template> <!-- 今月 --> <SkillRadar :labels="fields" :values="thisMonth" series-label="今月" /> <!-- 先月(同じ部品・別データ) --> <SkillRadar :labels="fields" :values="lastMonth" series-label="先月" /> </template>
部品の中身は一切書き換えていません。
これが「再利用可能なチャート部品」です。観測できるゴールは
「同じチャート部品を、別データで2箇所に置けること」。
props で来た配列は信用しすぎない
labels が5個なのに values が4個だと、レーダーは黙って崩れます。
API が不揃いの配列長で返してくることは普通に起こります。
const isValid = computed(() => props.labels.length === props.values.length && props.values.length > 0 )
<template> <Radar v-if="isValid" :data="chartData" /> <p v-else class="chart-warn">表示できるデータがありません</p> </template>
「描けない入力を、描く前に弾く」。これも部品の責務です。
Part C — 登録漏れの罠を実機で体験する
いちばん効く学び方は「壊して観察する」
ここがこのトピックの核です。完成版の register から、1行だけコメントアウトします。
ChartJS.register( RadialLinearScale, PointElement, LineElement, // Filler, ← これを外してみる Tooltip, Legend, )
そして画面を更新してみてください。
何が起きるか
| 外したもの | 画面の症状 |
|---|---|
RadialLinearScale | 何も描画されない(軸がないので描きようがない) |
Filler | 線は出るが内側の塗りが消える |
Tooltip | ホバーしても数値が出ない |
怖いのは RadialLinearScale を外したとき。
赤いエラーが画面いっぱいに出るわけではなく、
ただ白い四角が残るだけのことがあります。コンソールに小さく
未登録の警告が出るだけで、見落としやすい。
「エラーが出ないバグ」がいちばん危険
graph TD
A[register 漏れ] --> B{症状}
B --> C[画面が真っ白]
B --> D[一部だけ欠ける]
C --> E[原因がコードに現れない<br/>= 探しにくい]
D --> E- 例外で止まってくれれば、スタックトレースが場所を教えてくれる
- register 漏れは静かに失敗するので、原因にたどり着きにくい
- だから「描けないときは、まず register を疑う」を手癖にする
観測できるゴール:「register を1つコメントアウトすると描画が消える」を自分の目で確認したこと。
直し方は単純
外した部品を register に戻すだけ。
ChartJS.register( RadialLinearScale, PointElement, LineElement, Filler, // ← 戻す Tooltip, Legend, )
大事なのは手順ではなく、「描けない=登録漏れを最初に疑う」という反射が
身についたかどうかです。一度自分で壊しておくと、本番でハマる時間が桁違いに短くなります。
Part D — data / options 分離と色の CSS 変数化
options を全部のコンポーネントにベタ書きしない
options(軸の最小・最大、レスポンシブ設定など)を各部品に直書きすると、
同じ設定が散らばって、変えるたびに全部直すことになります。
// ❌ チャートごとに毎回これを書いている
const options = {
responsive: true,
maintainAspectRatio: false,
scales: { r: { min: 0, max: 5, beginAtZero: true } },
}レーダー3枚あれば、この塊が3回コピペされる。
1箇所だけ直し忘れて、図ごとに目盛りが違う、が定番の事故です。
data と options は役割が違う
| 中身 | 変わる頻度 | |
|---|---|---|
data | labels と数値(何を描くか) | データ取得のたびに変わる |
options | 軸範囲・レスポンシブ(どう描くか) | ほぼ固定 |
dataは props で外から渡す(Part B)optionsは部品の中に1つだけ持つ、または共通ファイルから import する
「変わるもの」と「変わらないもの」を分けるのが分離の本質です。
responsive と maintainAspectRatio
レイアウト崩れの原因は、たいていこの2つの設定です。
const options = {
responsive: true, // 親要素のサイズに追従
maintainAspectRatio: false, // 縦横比の固定を解除 = 親の高さに従う
}maintainAspectRatio: true(既定)だと、横幅に対して高さが勝手に決まる- ダッシュボードで高さを CSS で決めたいなら
falseにして、
ラッパー要素に明示的な高さを与える
<template>
<div class="chart-box"><Radar :data="d" :options="o" /></div>
</template>
<style scoped>
.chart-box { height: 320px; }
</style>固定サイズ版とレスポンシブ版を並べて見る
崩れは「比べる」と一目で分かります。
<template>
<!-- 固定サイズ:画面を狭めてもサイズが変わらない -->
<div style="width: 300px; height: 300px;">
<Radar :data="d" :options="{ responsive: false }" />
</div>
<!-- レスポンシブ:親に追従して縮む -->
<div class="chart-box">
<Radar :data="d" :options="{ responsive: true, maintainAspectRatio: false }" />
</div>
</template>ブラウザ幅を狭めて、片方だけ崩れる/追従するのを確認してください。
色をハードコードしない
// ❌ 16進をベタ書き:テーマを変えると全チャートを手修正
datasets: [{ data, backgroundColor: '#4f86c6', borderColor: '#2c5d9b' }]色を直書きすると、ダークモードやブランド色変更のたびに
全チャートを探して回ることになります。CSS 変数に逃がします。
:root {
--chart-accent: #4f86c6;
--chart-fill: rgba(79, 134, 198, 0.25);
}const css = getComputedStyle(document.documentElement)
const accent = css.getPropertyValue('--chart-accent').trim()
const fill = css.getPropertyValue('--chart-fill').trim()
datasets: [{ data, borderColor: accent, backgroundColor: fill }]--chart-accent を1行変えれば、全チャートの色が揃って変わる。これが合格基準です。
まとめ — 再利用できるチャート部品の地図
graph TD REG[入口で register<br/>使う部品を明示登録] --> CMP[チャート部品] PROPS[props: labels / values] --> CMP OPT[options: 軸・responsive<br/>= 部品内に1つ] --> CMP VAR[CSS変数: 色] --> CMP CMP --> V1[今月レーダー] CMP --> V2[先月レーダー] CMP --> V3[棒グラフへ転用]
このチャプターで学んだこと
- chart.js は tree-shaking 前提。使う部品を
registerしないと静かに描画されない - 描けないときは、エラーを探す前にまず register 漏れを疑う
labels/valuesを props 化すれば、同じ部品を別データで使い回せるdata(何を)とoptions(どう)を分け、色は CSS 変数でテーマ連動を守る
演習(別紙 hands-on)
「汚れた starter」をリファクタする:
- register を1つ外して描画が消えることを実機確認→ 戻して復旧
- べた書きのチャートを
labels/valuesの props 化し、配列長バリデーションを追加 - 同じ部品を別データで2箇所に再利用(今月/先月)
- 固定サイズ版とレスポンシブ版を並べ、崩れを比較して修正
- ハードコードされた色を CSS 変数に巻き取り、1行変更で全チャートが変わることを確認