去年參與的幾個專案有使用的 EChart 這個套件,結果被業主掃出有資安問題。綜合考量之下,決定改用 Chart.js 來取代 EChart。在這邊整理一下 Chart.js 在 Vue3 的使用方法,考量多使用一個套件就要多考量一分資安風險,所以就不合併使用 vue-chart.js 。
安裝
npm install chart.js引入
<script setup>
// 引入
import Chart from "chart.js/auto";
import { onMounted } from "vue";
// 以下為程式碼
</script>Chart.js 資料結構
以下先以柱狀圖進行說明,同樣的方法也可應用在折線圖上。
資料為一個物件,裡面分別有 labes 及 datasets 屬性,兩者皆為陣列:
- labels: 為圖表 X 軸上的分類區間,為一個陣列(array)。
- datasets:為資料集,為一個陣列(array),裡面的結構為一個一個物件,每個物件都有 label 及 data 屬性。
- label 為資料分類名稱
- data 為要畫成柱狀的高度數值。
const dataObj = {
// labels 為橫軸上的分類,為一個陣列
labels: [
"Mon",
"Tue",
"Wed",
"Thur",
"Fri",
"Sat",
"Sun",
],
// datasets資料集為一個陣列,裡面的資料類型為一個一個物件
datasets: [
// 物件的屬性有 label 與 data
{
// label 為資料分類的名稱
label: "鋼鐵人",
// data 為資料內容,是一個陣列,相同 index 的數值會放在同一個橫軸的分類區間
data: [10, 20, 30, 50, 40, 30, 10],
},
{
label: "美國隊長",
data: [20, 30, 40, 40, 30, 20, 20],
},
{
label: "蜘蛛人",
data: [30, 40, 50, 30, 20, 10, 30],
},
],
};資料陣列與圖表的比對:data 陣列裡同樣 index 的資料會放在相同的橫軸區間。

初始化(畫出圖表)
Chart.js 是以 canvas 的方式算繪,在這裡,我先設定填滿外容器(在建立圖表時,maintainAspectRatio要設為 false),所以外容器的寬高也要在 CSS 中設定好。
<canvas>也要設好 id 名稱,在這裡 id 為 heroChart。
<template>
<div class="card-content-image">
<canvas id="heroChart" ></canvas>
</div>
</template>接著在 onMounted 時,取得 canvas 的 DOM。
const ctx = document.getElementById("heroChart");使用 Chart.js 給的 new Chart() 方法畫出圖表,第一個參數 ctx 為上面取得的 canvas DOM 元素,第二個參數則是圖表類型等相關設定,為一個物件結構。
- type:為圖表類型,可以是 bar(長條圖)、line(折線圖)
- data:放入上面定義的資料集 dataObj
- options:視覺及版面配置選項
- responsive:布林值,配合 CSS 設定自適應。
- maintainAspectRatio:布林值,設為 false,才會跟隨外容器的長寬比例呈現。
- plugins:設定圖例位置及標題。
onMounted(() => {
const ctx = document.getElementById("heroChart");
let chart = new Chart(ctx, {
// type 可以是 bar,也可以是 line
type: "bar",
data: dataObj,
options: {
responsive: true,
// 如果不設 maintainAspectRatio,寬度會固定
maintainAspectRatio: false,
plugins: {
legend: {
// datasets裡每一個物件會變成圖例,出現在給定的位置
position: "top",
},
title: {
display: true,
text: "超級英雄出勤次數",
},
},
},
});
});完整程式碼
<script setup>
import Chart from 'chart.js/auto';
import { onMounted } from 'vue';
const dataObj = {
// labels 為橫軸上的分類,為一個陣列
labels: [
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Sunday',
],
// datasets資料集為一個陣列,裡面的資料類型為一個一個物件
datasets: [
// 物件的屬性有 label 與 data
{
// label 為資料分類的名稱
label: '鋼鐵人',
// data 為資料內容,是一個陣列,相同 index 的數值會放在同一個橫軸的分類區間
data: [10, 20, 30, 50, 40, 30, 10],
},
{
label: '美國隊長',
data: [20, 30, 40, 40, 30, 20, 20],
},
{
label: '蜘蛛人',
data: [30, 40, 50, 30, 20, 10, 30],
},
],
};
onMounted(() => {
const ctx = document.getElementById('heroChart');
let chart = new Chart(ctx, {
// type 可以是 bar,也可以是 line
type: 'bar',
data: dataObj,
options: {
responsive: true,
// 如果不設 maintainAspectRatio,寬度會固定
maintainAspectRatio: false,
plugins: {
legend: {
// datasets裡每一個物件會變成圖例,出現在給定的位置
position: 'top',
},
// 圖表標題
title: {
display: true,
text: '超級英雄出勤次數',
},
},
},
});
});
</script>
<template>
<div class="card-content-image">
<canvas id="heroChart" width="100%" height="380"></canvas>
</div>
</template>
<style scoped>
.card-content-image {
width: 500px;
height: 400px;
border: 1px solid blue;
}
@media (max-width: 992px) {
.card-content-image {
width: 300px;
height: 200px;
border: 1px solid red;
}
}
</style>程式碼範例
- 一般使用
https://stackblitz.com/edit/vitejs-vite-juctg8?file=src%2FApp.vue&terminal=dev - 資料設為 ref 及 將 new Chart() 放入函式
https://stackblitz.com/edit/vitejs-vite-zcth4t?file=src%2FApp.vue&terminal=dev
參考資料
- https://alicialin2020.medium.com/chart-js-自學分享-243e11851a47
- https://www.runoob.com/chartjs/chartjs-usage.html
- https://pluscdev.com/tutorial-chartjs/
- https://chiaohu0211.medium.com/vue3運用chart-js做出甘特圖-4b43cc1df998
- https://www.appsloveworld.com/chartjs/100/15/vue3-chart-js-not-rendering-source-code
- https://www.jishuge.cn/?p=68
