# 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) 適合比較不同類別的數值。 #### 示意圖 ![截圖 2024-02-02 下午2.39.05](https://hackmd.io/_uploads/rJhMCZ59a.png) #### 使用範例 ```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) 適合顯示佔比關係。 #### 示意圖 ![截圖 2024-02-02 下午2.39.50](https://hackmd.io/_uploads/rJ98C-99p.png) #### 使用範例 ```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) 適合顯示趨勢變化。 #### 示意圖 ![截圖 2024-02-02 下午2.44.49](https://hackmd.io/_uploads/HJ7K1z9qp.png) #### 使用範例 ```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、大小)。 #### 示意圖 ![截圖 2024-02-02 下午2.49.02](https://hackmd.io/_uploads/ry0dgf9qa.png) #### 使用範例 ```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) 適合多維度數據比較。 #### 示意圖 ![截圖 2024-02-02 下午2.57.16](https://hackmd.io/_uploads/S1RQmfqqa.png) #### 使用範例 ```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) 適合顯示兩個變數之間的相關性。 #### 示意圖 ![截圖 2024-02-02 下午3.11.54](https://hackmd.io/_uploads/By6pBzq5T.png) #### 使用範例 ```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) 類似圓餅圖,但以面積而非角度表示比例。 #### 示意圖 ![截圖 2024-02-02 下午2.39.59](https://hackmd.io/_uploads/rykvRbccp.png) #### 使用範例 ```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 版本是否相容?