# Nuxt3 Chart.js 圖表開發指南
> 使用 [Chart.js](https://www.chartjs.org/docs/latest/) 與 [vue-chartjs](https://vue-chartjs.org/) 在 Nuxt3 中實現各種圖表
> 最後更新:2025年10月
<!-- ---
## 📚 目錄
- [安裝與設置](#安裝與設置)
- [圖表類型](#圖表類型)
- [長條圖 (Bar Chart)](#長條圖-bar-chart)
- [圓環圖 & 圓餅圖 (Doughnut & Pie)](#圓環圖--圓餅圖-doughnut--pie)
- [折線圖 (Line Chart)](#折線圖-line-chart)
- [氣泡圖 (Bubble Chart)](#氣泡圖-bubble-chart)
- [雷達圖 (Radar Chart)](#雷達圖-radar-chart)
- [散點圖 (Scatter Chart)](#散點圖-scatter-chart)
- [極坐標圖 (Polar Area)](#極坐標圖-polar-area)
--- -->
## 安裝與設置
### 1. 安裝套件
```bash
npm install vue-chartjs chart.js
```
### 2. Plugin 設定
建立 Chart.js 的 Nuxt Plugin 來註冊所需的圖表組件。
#### `/plugins/chartjs.js`
```javascript
import {
Chart,
CategoryScale,
LinearScale,
BarElement,
PointElement,
LineElement,
RadialLinearScale,
ArcElement,
Filler,
Title,
Tooltip,
Legend,
} from 'chart.js'
export default defineNuxtPlugin(() => {
Chart.register(
CategoryScale, // 類別軸(X軸)
LinearScale, // 線性軸(Y軸)
BarElement, // 長條圖元素
PointElement, // 點元素(折線圖、散點圖)
LineElement, // 線元素(折線圖)
RadialLinearScale,// 徑向軸(雷達圖)
ArcElement, // 弧形元素(圓餅圖、圓環圖)
Filler, // 填充區域
Title, // 標題
Tooltip, // 提示框
Legend, // 圖例
)
})
```
**說明**:
- Chart.js 3.x 採用 Tree-shaking 設計,需要明確註冊使用的組件
- 只註冊需要的組件可減少打包體積
---
### 3. Composables 設定
建立資料處理的 Composable 函式。
#### `composables/useChartData.js`
```javascript
export const useChartData = () => {
/**
* 提取圖表標籤
* @param {Array} chartData - 圖表資料陣列
* @returns {Array} 標籤陣列
*/
const getLabels = (chartData) => {
const labels = chartData.map((v) => v.label);
return labels;
};
/**
* 提取背景顏色
* @param {Array} chartData - 圖表資料陣列
* @param {String} defaultBackground - 預設背景色
* @returns {Array} 背景色陣列
*/
const getBackground = (chartData, defaultBackground) => {
const background = chartData.map((v) =>
v.backgroundColor !== undefined ? v.backgroundColor : defaultBackground
);
return background;
};
/**
* 提取圖表數據
* @param {Array} chartData - 圖表資料陣列
* @returns {Array} 數據陣列
*/
const getData = (chartData) => {
const data = chartData.map((v) => v.data);
return data;
};
return {
getLabels,
getBackground,
getData
};
}
```
---
### 4. 通用圖表組件
建立一個統一的圖表組件,支援多種圖表類型。
#### `components/Chart/index.vue`
```vue
<template>
<div class="chart-container">
<ClientOnly>
<!-- 長條圖 -->
<Bar
v-if="chartType === 'bar'"
:data="chartData"
:options="chartOptions"
/>
<!-- 氣泡圖 -->
<Bubble
v-else-if="chartType === 'bubble'"
:data="chartData"
:options="chartOptions"
/>
<!-- 圓環圖 -->
<Doughnut
v-else-if="chartType === 'doughnut'"
:data="chartData"
:options="chartOptions"
/>
<!-- 折線圖 -->
<Line
v-else-if="chartType === 'line'"
:data="chartData"
:options="chartOptions"
/>
<!-- 圓餅圖 -->
<Pie
v-else-if="chartType === 'pie'"
:data="chartData"
:options="chartOptions"
/>
<!-- 極坐標圖 -->
<PolarArea
v-else-if="chartType === 'polar-area'"
:data="chartData"
:options="chartOptions"
/>
<!-- 雷達圖 -->
<Radar
v-else-if="chartType === 'radar'"
:data="chartData"
:options="chartOptions"
/>
<!-- 散點圖 -->
<Scatter
v-else-if="chartType === 'scatter'"
:data="chartData"
:options="chartOptions"
/>
</ClientOnly>
</div>
</template>
<script setup>
import { Bar, Bubble, Doughnut, Line, Pie, PolarArea, Radar, Scatter } from 'vue-chartjs'
// Props 定義
const props = defineProps({
// 圖表類型
chartType: {
type: String,
default: 'pie',
validator: (value) => {
return ['bar', 'bubble', 'doughnut', 'pie', 'line', 'polar-area', 'radar', 'scatter'].includes(value)
}
},
// 圖表標題(長條圖、折線圖)
chartTitle: {
type: String,
default: ''
},
// 雷達圖外圍值
chartScope: {
type: Array,
default: () => []
},
// 圖表資料
data: {
type: Array,
default: () => []
},
// 預設背景色
defaultBackground: {
type: String,
default: '#E46651'
},
// 邊框顏色
borderColor: {
type: String,
default: ''
},
// 是否填充(折線圖)
fill: {
type: Boolean,
default: false
},
// 自訂 Options
chartOptions: {
type: Object,
default: () => ({})
}
})
// Composables
const { getLabels, getBackground, getData } = useChartData();
// 圖表資料設置
const chartData = ref({})
// 根據圖表類型設定資料結構
if (props.chartType === 'bar') {
// 長條圖
chartData.value = {
labels: getLabels(props.data),
datasets: [
{
label: props.chartTitle,
backgroundColor: getBackground(props.data, props.defaultBackground),
data: getData(props.data),
}
]
}
} else if (['pie', 'doughnut', 'polar-area'].includes(props.chartType)) {
// 圓餅圖、圓環圖、極坐標圖
chartData.value = {
labels: getLabels(props.data) || [],
datasets: [
{
backgroundColor: getBackground(props.data, props.defaultBackground),
borderColor: props.borderColor || 'transparent',
data: getData(props.data),
}
]
}
} else if (props.chartType === 'line') {
// 折線圖
chartData.value = {
labels: getLabels(props.data) || [],
datasets: [
{
label: props.chartTitle,
backgroundColor: getBackground(props.data, props.defaultBackground),
borderColor: props.borderColor || '#ccc',
fill: props.fill,
data: getData(props.data),
}
]
}
} else if (props.chartType === 'bubble') {
// 氣泡圖
chartData.value = {
datasets: props.data
}
} else if (['radar', 'scatter'].includes(props.chartType)) {
// 雷達圖、散點圖
chartData.value = {
labels: props.chartScope,
datasets: props.data
}
}
// 預設的 Options
const chartOptions = reactive({
responsive: true,
maintainAspectRatio: false
})
// 合併自訂 Options
if (props.chartOptions) {
Object.assign(chartOptions, props.chartOptions);
}
</script>
<style scoped>
.chart-container {
position: relative;
height: 400px;
width: 100%;
}
</style>
```
---
## 圖表類型
### 長條圖 (Bar Chart)
適合比較不同類別的數值。
#### 示意圖

#### 使用範例
```vue
<template>
<div>
<h2>前端框架使用統計</h2>
<Chart
chartType="bar"
:data="barData"
:chartTitle="title"
/>
</div>
</template>
<script setup>
const title = ref('前端框架')
const barData = reactive([
{
label: 'VueJs',
backgroundColor: '#41B883',
data: 40
},
{
label: 'EmberJs',
backgroundColor: '#E46651',
data: 20
},
{
label: 'ReactJs',
backgroundColor: '#00D8FF',
data: 80
},
{
label: 'AngularJs',
backgroundColor: '#DD1B16',
data: 10
},
])
</script>
```
#### Data 規格
| 屬性 | 類型 | 說明 | 必填 |
|------|------|------|------|
| `chartTitle` | String | 顯示在圖表上方的標題 | ❌ |
| `label` | String | 個別值的名稱 | ✅ |
| `backgroundColor` | String | 個別值的色彩(RGB、RGBA、HEX、Color Name) | ✅ |
| `data` | Number | 單一數值 | ✅ |
---
### 圓環圖 & 圓餅圖 (Doughnut & Pie)
適合顯示佔比關係。
#### 示意圖

#### 使用範例
```vue
<template>
<div>
<h2>框架使用佔比</h2>
<div class="chart-grid">
<!-- 圓環圖 -->
<div>
<h3>圓環圖</h3>
<Chart
chartType="doughnut"
:data="pieData"
/>
</div>
<!-- 圓餅圖 -->
<div>
<h3>圓餅圖</h3>
<Chart
chartType="pie"
:data="pieData"
/>
</div>
<!-- 極坐標圖 -->
<div>
<h3>極坐標圖</h3>
<Chart
chartType="polar-area"
:data="pieData"
/>
</div>
</div>
</div>
</template>
<script setup>
const pieData = reactive([
{
label: 'VueJs',
backgroundColor: '#41B883',
data: 40
},
{
label: 'EmberJs',
backgroundColor: '#E46651',
data: 20
},
{
label: 'ReactJs',
backgroundColor: '#00D8FF',
data: 80
},
{
label: 'AngularJs',
backgroundColor: '#DD1B16',
data: 10
},
])
</script>
<style scoped>
.chart-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
</style>
```
#### Data 規格
| 屬性 | 類型 | 說明 | 必填 |
|------|------|------|------|
| `label` | String | 個別值的名稱 | ✅ |
| `backgroundColor` | String | 個別值的色彩 | ✅ |
| `data` | Number | 單一數值 | ✅ |
---
### 折線圖 (Line Chart)
適合顯示趨勢變化。
#### 示意圖

#### 使用範例
```vue
<template>
<div>
<h2>框架使用趨勢</h2>
<Chart
chartType="line"
:data="lineData"
:chartTitle="title"
:fill="true"
borderColor="#41B883"
/>
</div>
</template>
<script setup>
const title = ref('前端框架趨勢')
const lineData = reactive([
{
label: 'VueJs',
backgroundColor: 'rgba(65, 184, 131, 0.2)',
data: 40
},
{
label: 'EmberJs',
backgroundColor: 'rgba(228, 102, 81, 0.2)',
data: 20
},
{
label: 'ReactJs',
backgroundColor: 'rgba(0, 216, 255, 0.2)',
data: 80
},
{
label: 'AngularJs',
backgroundColor: 'rgba(221, 27, 22, 0.2)',
data: 10
},
])
</script>
```
#### Data 規格
| 屬性 | 類型 | 說明 | 必填 |
|------|------|------|------|
| `chartTitle` | String | 顯示在圖表上方的標題 | ❌ |
| `label` | String | 個別值的名稱 | ✅ |
| `backgroundColor` | String | 填充區域的色彩(建議使用 RGBA) | ✅ |
| `data` | Number | 單一數值 | ✅ |
#### Props 參數
| 參數 | 類型 | 說明 | 預設值 |
|------|------|------|--------|
| `fill` | Boolean | 是否填充線下區域 | `false` |
| `borderColor` | String | 線條顏色 | `#ccc` |
---
### 氣泡圖 (Bubble Chart)
適合顯示三維數據(X、Y、大小)。
#### 示意圖

#### 使用範例
```vue
<template>
<div>
<h2>框架使用分布</h2>
<Chart
chartType="bubble"
:data="bubbleData"
/>
</div>
</template>
<script setup>
const bubbleData = reactive([
{
label: 'VueJs',
backgroundColor: 'rgba(65, 184, 131, 0.6)',
data: [
{ x: 20, y: 25, r: 5 },
{ x: 40, y: 10, r: 10 },
]
},
{
label: 'EmberJs',
backgroundColor: 'rgba(228, 102, 81, 0.6)',
data: [
{ x: 30, y: 22, r: 30 },
{ x: 10, y: 30, r: 15 }
]
},
{
label: 'ReactJs',
backgroundColor: 'rgba(0, 216, 255, 0.6)',
data: [
{ x: 20, y: 20, r: 10 }
]
},
{
label: 'AngularJs',
backgroundColor: 'rgba(221, 27, 22, 0.6)',
data: [
{ x: 15, y: 8, r: 30 }
]
},
])
</script>
```
#### Data 規格
| 屬性 | 類型 | 說明 | 必填 |
|------|------|------|------|
| `label` | String | 個別值的名稱 | ✅ |
| `backgroundColor` | String | 個別值的色彩(建議使用 RGBA) | ✅ |
| `data` | Array | 陣列物件 | ✅ |
| `data[].x` | Number | 橫坐標的值 | ✅ |
| `data[].y` | Number | 縱座標的值 | ✅ |
| `data[].r` | Number | 圓形大小(半徑) | ✅ |
---
### 雷達圖 (Radar Chart)
適合多維度數據比較。
#### 示意圖

#### 使用範例
```vue
<template>
<div>
<h2>能力分析</h2>
<Chart
chartType="radar"
:chartScope="radarScope"
:data="radarData"
/>
</div>
</template>
<script setup>
// 雷達圖外圍標籤
const radarScope = reactive([
'Eating',
'Drinking',
'Sleeping',
'Designing',
'Coding',
'Cycling',
'Running'
])
// 雷達圖資料(可以有多組)
const radarData = reactive([
{
label: 'My First dataset',
backgroundColor: 'rgba(179, 181, 198, 0.2)',
borderColor: 'rgba(179, 181, 198, 1)',
pointBackgroundColor: 'rgba(179, 181, 198, 1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(179, 181, 198, 1)',
data: [65, 59, 90, 81, 56, 55, 40]
},
{
label: 'My Second dataset',
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255, 99, 132, 1)',
pointBackgroundColor: 'rgba(255, 99, 132, 1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(255, 99, 132, 1)',
data: [28, 48, 40, 19, 96, 27, 100]
}
])
</script>
```
#### Data 規格
| 屬性 | 類型 | 說明 | 必填 |
|------|------|------|------|
| `chartScope` | Array | 雷達圖外圍的標籤 | ✅ |
| `label` | String | 資料集名稱 | ✅ |
| `backgroundColor` | String | 填充區域色彩 | ✅ |
| `borderColor` | String | 線條色彩 | ✅ |
| `pointBackgroundColor` | String | 點的背景色 | ✅ |
| `pointBorderColor` | String | 點的邊框色 | ✅ |
| `pointHoverBackgroundColor` | String | 滑鼠移入點的背景色 | ✅ |
| `pointHoverBorderColor` | String | 滑鼠移入點的邊框色 | ✅ |
| `data` | Array | 數值陣列(長度需與 chartScope 相同) | ✅ |
---
### 散點圖 (Scatter Chart)
適合顯示兩個變數之間的相關性。
#### 示意圖

#### 使用範例
```vue
<template>
<div>
<h2>框架性能分析</h2>
<Chart
chartType="scatter"
:data="scatterData"
/>
</div>
</template>
<script setup>
const scatterData = reactive([
{
label: 'VueJs',
backgroundColor: '#41B883',
data: [
{ x: 20, y: 25 },
{ x: 40, y: 10 },
]
},
{
label: 'EmberJs',
backgroundColor: '#E46651',
data: [
{ x: 30, y: 22 },
{ x: 10, y: 30 }
]
},
{
label: 'ReactJs',
backgroundColor: '#00D8FF',
data: [
{ x: 20, y: 20 }
]
},
{
label: 'AngularJs',
backgroundColor: '#DD1B16',
data: [
{ x: 15, y: 8 }
]
},
])
</script>
```
#### Data 規格
| 屬性 | 類型 | 說明 | 必填 |
|------|------|------|------|
| `label` | String | 個別值的名稱 | ✅ |
| `backgroundColor` | String | 個別值的色彩 | ✅ |
| `data` | Array | 陣列物件 | ✅ |
| `data[].x` | Number | 橫坐標的值 | ✅ |
| `data[].y` | Number | 縱座標的值 | ✅ |
---
### 極坐標圖 (Polar Area)
類似圓餅圖,但以面積而非角度表示比例。
#### 示意圖

#### 使用範例
```vue
<template>
<div>
<h2>框架熱門度</h2>
<Chart
chartType="polar-area"
:data="polarData"
/>
</div>
</template>
<script setup>
const polarData = reactive([
{
label: 'VueJs',
backgroundColor: 'rgba(65, 184, 131, 0.6)',
data: 40
},
{
label: 'EmberJs',
backgroundColor: 'rgba(228, 102, 81, 0.6)',
data: 20
},
{
label: 'ReactJs',
backgroundColor: 'rgba(0, 216, 255, 0.6)',
data: 80
},
{
label: 'AngularJs',
backgroundColor: 'rgba(221, 27, 22, 0.6)',
data: 10
},
])
</script>
```
---
## 進階設定
### 自訂 Options
所有圖表都支援自訂 `chartOptions` 來調整外觀和行為。
#### 範例:長條圖橫向顯示
```vue
<template>
<Chart
chartType="bar"
:data="barData"
:chartOptions="customOptions"
/>
</template>
<script setup>
const customOptions = {
indexAxis: 'y', // 橫向顯示
responsive: true,
plugins: {
legend: {
display: false
},
title: {
display: true,
text: '前端框架使用統計'
}
},
scales: {
x: {
beginAtZero: true
}
}
}
</script>
```
### 常用 Options 設定
```javascript
const chartOptions = {
// 響應式
responsive: true,
maintainAspectRatio: false,
// 插件設定
plugins: {
// 標題
title: {
display: true,
text: '圖表標題',
font: {
size: 18
}
},
// 圖例
legend: {
display: true,
position: 'top', // 'top' | 'bottom' | 'left' | 'right'
},
// 提示框
tooltip: {
enabled: true,
mode: 'index', // 'point' | 'index' | 'dataset'
intersect: false
}
},
// 軸設定(長條圖、折線圖、散點圖)
scales: {
x: {
display: true,
title: {
display: true,
text: 'X軸標題'
}
},
y: {
display: true,
beginAtZero: true,
title: {
display: true,
text: 'Y軸標題'
}
}
}
}
```
---
## 顏色設定參考
### 常用顏色格式
```javascript
// 1. HEX
backgroundColor: '#41B883'
// 2. RGB
backgroundColor: 'rgb(65, 184, 131)'
// 3. RGBA(推薦用於需要透明度的場景)
backgroundColor: 'rgba(65, 184, 131, 0.6)'
// 4. Color Name
backgroundColor: 'red'
```
### 推薦配色
```javascript
// 前端框架配色
const frameworkColors = {
vue: '#41B883', // Vue.js 綠
react: '#00D8FF', // React 藍
angular: '#DD1B16', // Angular 紅
ember: '#E46651', // Ember 橘紅
svelte: '#FF3E00', // Svelte 橘
next: '#000000', // Next.js 黑
}
// 數據可視化配色(彩虹色)
const rainbowColors = [
'#FF6B6B', // 紅
'#FFA500', // 橙
'#FFD93D', // 黃
'#6BCB77', // 綠
'#4D96FF', // 藍
'#9D84B7', // 紫
]
```
---
## 效能優化建議
### 1. 使用 ClientOnly
Chart.js 依賴瀏覽器 Canvas API,需要在客戶端渲染:
```vue
<template>
<ClientOnly>
<Chart chartType="bar" :data="barData" />
<template #fallback>
<div>載入圖表中...</div>
</template>
</ClientOnly>
</template>
```
### 2. 資料量過大時的處理
```javascript
// 資料抽樣
const sampledData = largeDataset.filter((_, index) => index % 10 === 0)
// 或使用聚合
const aggregatedData = aggregateByMonth(largeDataset)
```
### 3. 動態載入
對於不常用的圖表類型,可以動態載入:
```vue
<script setup>
const Radar = defineAsyncComponent(() =>
import('vue-chartjs').then(m => ({ default: m.Radar }))
)
</script>
```
---
## 常見問題
### Q1: 圖表不顯示?
**可能原因**:
1. 忘記用 `<ClientOnly>` 包裹
2. 忘記在 plugin 中註冊必要的組件
3. 容器沒有設定高度
**解決方案**:
```vue
<template>
<ClientOnly>
<div style="height: 400px;">
<Chart chartType="bar" :data="barData" />
</div>
</ClientOnly>
</template>
```
---
### Q2: 圖表不響應式?
**解決方案**:確保 `maintainAspectRatio` 設為 `false`
```javascript
const chartOptions = {
responsive: true,
maintainAspectRatio: false
}
```
---
### Q3: 如何更新圖表資料?
**方案一:使用 reactive/ref**
```vue
<script setup>
const chartData = ref([...])
// 更新資料
const updateData = () => {
chartData.value = [...newData]
}
</script>
```
**方案二:使用 watch 監聽**
```vue
<script setup>
const apiData = ref([])
const chartData = computed(() => {
return apiData.value.map(item => ({
label: item.name,
data: item.value,
backgroundColor: item.color
}))
})
// 當 API 資料更新時,圖表會自動重新渲染
</script>
```
---
### Q4: 如何匯出圖表為圖片?
```vue
<template>
<div>
<Chart ref="chartRef" chartType="bar" :data="barData" />
<button @click="downloadChart">下載圖表</button>
</div>
</template>
<script setup>
const chartRef = ref(null)
const downloadChart = () => {
const chart = chartRef.value.$refs.canvas
const url = chart.toDataURL('image/png')
const link = document.createElement('a')
link.download = 'chart.png'
link.href = url
link.click()
}
</script>
```
---
### Q5: 如何在圖表中顯示資料標籤?
需要安裝 `chartjs-plugin-datalabels` 插件:
```bash
npm install chartjs-plugin-datalabels
```
```javascript
// plugins/chartjs.js
import ChartDataLabels from 'chartjs-plugin-datalabels'
export default defineNuxtPlugin(() => {
Chart.register(
// ... 其他組件
ChartDataLabels
)
})
```
```javascript
// 在 chartOptions 中設定
const chartOptions = {
plugins: {
datalabels: {
display: true,
color: 'white',
font: {
weight: 'bold',
size: 14
},
formatter: (value) => {
return value + '%'
}
}
}
}
```
---
## 實戰範例
### 範例 1:動態資料載入
從 API 取得資料並顯示圖表。
```vue
<template>
<div>
<h2>銷售統計</h2>
<div v-if="loading">載入中...</div>
<Chart
v-else
chartType="bar"
:data="chartData"
:chartTitle="'月銷售額'"
:chartOptions="chartOptions"
/>
</div>
</template>
<script setup>
const loading = ref(true)
const salesData = ref([])
// 取得資料
const fetchSalesData = async () => {
try {
const { data } = await useFetch('/api/sales')
salesData.value = data.value
} catch (error) {
console.error('載入資料失敗', error)
} finally {
loading.value = false
}
}
// 轉換為圖表格式
const chartData = computed(() => {
return salesData.value.map(item => ({
label: item.month,
backgroundColor: item.amount > 100000 ? '#4CAF50' : '#FF9800',
data: item.amount
}))
})
// 自訂 Options
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
tooltip: {
callbacks: {
label: (context) => {
return `銷售額:NT$ ${context.parsed.y.toLocaleString()}`
}
}
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
callback: (value) => {
return 'NT$ ' + value.toLocaleString()
}
}
}
}
}
onMounted(() => {
fetchSalesData()
})
</script>
```
---
### 範例 2:互動式圖表切換
讓使用者選擇不同的圖表類型。
```vue
<template>
<div>
<h2>框架使用統計</h2>
<!-- 圖表類型選擇 -->
<div class="chart-controls">
<button
v-for="type in chartTypes"
:key="type.value"
:class="{ active: currentChartType === type.value }"
@click="currentChartType = type.value">
{{ type.label }}
</button>
</div>
<!-- 圖表 -->
<Chart
:chartType="currentChartType"
:data="frameworkData"
:chartTitle="'前端框架'"
/>
</div>
</template>
<script setup>
const currentChartType = ref('bar')
const chartTypes = [
{ value: 'bar', label: '長條圖' },
{ value: 'line', label: '折線圖' },
{ value: 'pie', label: '圓餅圖' },
{ value: 'doughnut', label: '圓環圖' },
{ value: 'radar', label: '雷達圖' },
]
const frameworkData = reactive([
{
label: 'Vue.js',
backgroundColor: '#41B883',
data: 40
},
{
label: 'React',
backgroundColor: '#00D8FF',
data: 80
},
{
label: 'Angular',
backgroundColor: '#DD1B16',
data: 10
},
{
label: 'Svelte',
backgroundColor: '#FF3E00',
data: 25
},
])
</script>
<style scoped>
.chart-controls {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.chart-controls button {
padding: 10px 20px;
border: 2px solid #ddd;
background: white;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
}
.chart-controls button:hover {
border-color: #41B883;
color: #41B883;
}
.chart-controls button.active {
background: #41B883;
color: white;
border-color: #41B883;
}
</style>
```
---
### 範例 3:多資料集比較
同時顯示多個資料系列。
```vue
<template>
<div>
<h2>各季度銷售比較</h2>
<Chart
chartType="line"
:data="multiDatasetChart.data"
:chartOptions="multiDatasetChart.options"
/>
</div>
</template>
<script setup>
const multiDatasetChart = reactive({
data: [
{
label: '2023 Q1',
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgb(255, 99, 132)',
fill: false
},
{
label: '2023 Q2',
data: [28, 48, 40, 19, 86, 27, 90],
backgroundColor: 'rgba(54, 162, 235, 0.2)',
borderColor: 'rgb(54, 162, 235)',
fill: false
},
{
label: '2023 Q3',
data: [45, 55, 60, 70, 65, 75, 85],
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderColor: 'rgb(75, 192, 192)',
fill: false
}
],
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '季度銷售趨勢'
},
legend: {
display: true,
position: 'top'
}
},
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: '銷售額(萬元)'
}
},
x: {
title: {
display: true,
text: '月份'
}
}
}
}
})
// 注意:對於折線圖的多資料集,需要直接使用 Chart.js 的格式
// 不能使用 composable 的 getLabels、getData 等方法
</script>
```
**重要提示**:當使用多資料集時,需要直接傳入 Chart.js 的標準格式:
```javascript
// 正確格式
const chartData = {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'],
datasets: [
{
label: 'Dataset 1',
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: 'rgba(255, 99, 132, 0.5)',
},
{
label: 'Dataset 2',
data: [28, 48, 40, 19, 86, 27, 90],
backgroundColor: 'rgba(54, 162, 235, 0.5)',
}
]
}
```
---
### 範例 4:響應式圖表(RWD)
根據螢幕大小調整圖表顯示。
```vue
<template>
<div class="chart-responsive">
<Chart
:chartType="isMobile ? 'doughnut' : 'bar'"
:data="chartData"
:chartOptions="responsiveOptions"
/>
</div>
</template>
<script setup>
const isMobile = ref(false)
// 檢測螢幕大小
const checkMobile = () => {
isMobile.value = window.innerWidth < 768
}
onMounted(() => {
checkMobile()
window.addEventListener('resize', checkMobile)
})
onUnmounted(() => {
window.removeEventListener('resize', checkMobile)
})
const responsiveOptions = computed(() => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: !isMobile.value,
position: isMobile.value ? 'bottom' : 'right'
}
}
}))
const chartData = reactive([
{ label: 'Vue.js', backgroundColor: '#41B883', data: 40 },
{ label: 'React', backgroundColor: '#00D8FF', data: 80 },
{ label: 'Angular', backgroundColor: '#DD1B16', data: 10 },
])
</script>
<style scoped>
.chart-responsive {
height: 400px;
}
@media (max-width: 768px) {
.chart-responsive {
height: 300px;
}
}
</style>
```
---
## 無障礙設計
### 1. 為圖表添加替代文字
```vue
<template>
<div>
<div
role="img"
:aria-label="chartDescription"
class="chart-wrapper">
<Chart chartType="bar" :data="chartData" />
</div>
<!-- 提供文字版數據 -->
<details class="visually-hidden">
<summary>圖表數據</summary>
<table>
<thead>
<tr>
<th>項目</th>
<th>數值</th>
</tr>
</thead>
<tbody>
<tr v-for="item in chartData" :key="item.label">
<td>{{ item.label }}</td>
<td>{{ item.data }}</td>
</tr>
</tbody>
</table>
</details>
</div>
</template>
<script setup>
const chartData = reactive([...])
const chartDescription = computed(() => {
const items = chartData.map(item => `${item.label}: ${item.data}`)
return `長條圖顯示以下數據:${items.join('、')}`
})
</script>
<style>
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
</style>
```
### 2. 確保顏色對比度
```javascript
// 使用對比度足夠的顏色
const accessibleColors = [
'#005A9C', // 深藍(對比度 8.59:1)
'#D32F2F', // 深紅(對比度 5.39:1)
'#388E3C', // 深綠(對比度 5.28:1)
'#F57C00', // 深橙(對比度 4.54:1)
]
```
---
## 效能監控
### 追蹤圖表渲染時間
```vue
<script setup>
const chartRef = ref(null)
onMounted(() => {
const startTime = performance.now()
nextTick(() => {
const endTime = performance.now()
console.log(`圖表渲染時間: ${(endTime - startTime).toFixed(2)}ms`)
})
})
</script>
```
---
## 參考資源
### 官方文件
- [Chart.js 官方文件](https://www.chartjs.org/docs/latest/)
- [vue-chartjs 官方文件](https://vue-chartjs.org/)
- [Chart.js 範例集](https://www.chartjs.org/docs/latest/samples/)
### 實用工具
- [Chart.js 配色工具](https://coolors.co/)
- [對比度檢查工具](https://webaim.org/resources/contrastchecker/)
- [Chart.js 線上編輯器](https://www.chartjs.org/samples/)
### 插件生態
- [chartjs-plugin-datalabels](https://chartjs-plugin-datalabels.netlify.app/) - 資料標籤
- [chartjs-plugin-annotation](https://www.chartjs.org/chartjs-plugin-annotation/) - 註釋標記
- [chartjs-plugin-zoom](https://www.chartjs.org/chartjs-plugin-zoom/) - 縮放功能
---
## 總結
### 快速選擇圖表類型
| 使用場景 | 推薦圖表 |
|---------|---------|
| 比較不同類別 | 長條圖 (Bar) |
| 顯示趨勢變化 | 折線圖 (Line) |
| 顯示佔比關係 | 圓餅圖 (Pie)、圓環圖 (Doughnut) |
| 多維度比較 | 雷達圖 (Radar) |
| 顯示分布 | 散點圖 (Scatter) |
| 三維數據 | 氣泡圖 (Bubble) |
### 最佳實踐
✅ **應該做的**:
- 使用 `<ClientOnly>` 包裹圖表
- 為容器設定明確高度
- 使用 reactive/ref 管理資料
- 提供無障礙替代方案
- 確保顏色對比度足夠
- 使用有意義的標籤和標題
❌ **不應該做的**:
- 在同一個頁面放置過多圖表
- 使用過於複雜的配色
- 忽略移動端體驗
- 忘記錯誤處理
- 使用過小的字體
- 過度動畫效果
### 疑難排解檢查清單
遇到問題時,依序檢查:
1. ✅ 是否用 `<ClientOnly>` 包裹?
2. ✅ Plugin 是否正確註冊?
3. ✅ 容器是否有設定高度?
4. ✅ 資料格式是否正確?
5. ✅ Console 是否有錯誤訊息?
6. ✅ Chart.js 版本是否相容?