VueJS + Chartjs - 图表仅在代码更改后呈现
VueJS + Chartjs - Chart only renders after code change
我正在使用以下教程(为 NPM 包拉取下载统计信息)为我绘制的 webapp 打下基础:
- https://hackernoon.com/lets-build-a-web-app-with-vue-chart-js-and-an-api-544eb81c4b44
- https://github.com/apertureless/npm-stats
我从教程中提取了以下代码并对其进行了修改,因此它可以执行纯基础知识。获取数据并呈现数据。具体来自这些:
- https://github.com/apertureless/npm-stats/blob/develop/src/pages/Start.vue
- https://github.com/apertureless/npm-stats/blob/develop/src/components/LineChart.vue
请注意: 代码执行 API 调用并检索数据没有问题。但是,如果我更改代码,它只会在图表中呈现该数据。例如,将线条的颜色更改为其他颜色。如果有意义的话,它似乎只适用于下一个 'cycle'。数据呈现后,如果我刷新该页面,它又是空白的。我怀疑这与页面时间有关。但是不确定从哪里开始或我在寻找什么。
App.Vue
<template>
<v-app style="background-color: rgb(228, 228, 228);">
<section class="One">
<v-card class="One" color="rgb(255, 255, 255)" >
<LineChart :chart-data="downloads" :chart-labels="labels"/>
</v-card>
</section>
</v-app>
</template>
<script>
import axios from 'axios';
import LineChart from './components/test3.vue';
export default {
name: 'App',
components: {
LineChart,
},
data () {
return {
package: '',
packageName: '',
loaded: false,
loading: false,
downloads: [],
downloadsYear: [],
downloadsMonth: [],
downloadsWeek: [],
labels: [],
labelsYear: [],
labelsMonth: [],
labelsWeek: [],
showError: false,
showSettings: false,
errorMessage: 'Please enter a package name',
periodStart: '',
periodEnd: new Date(),
rawData: '',
totalDownloads: '',
dailyPng: null,
weeklyPng: null,
monthlyPng: null,
yearlyPng: null
}
},
mounted(){
this.loaded = false
axios.get(`https://api.npmjs.org/downloads/range/2017-01-01:2017-04-19/vue`)
.then(response => {
this.rawData = response.data.downloads
this.downloads = response.data.downloads.map(entry => entry.downloads)
this.labels = response.data.downloads.map(entry => entry.day)
this.packageName = response.data.package
this.totalDownloads = this.downloads.reduce((total, download) => total + download)
this.setURL()
this.groupDataByDate()
this.loaded = true
this.loading = false
})
.catch(err => {
this.errorMessage = err.response.data.error
this.loading = false
})
},
};
</script>
图表组件:
<script>
import { Line } from 'vue-chartjs'
export default {
extends: Line,
props: {
chartData: {
type: Array,
required: false
},
chartLabels: {
type: Array,
required: true
}
},
data () {
return {
gradient: null,
options: {
showScale: true,
scales: {
yAxes: [{
ticks: {
beginAtZero: false,
},
gridLines: {
display: true,
color: '#EEF0F4',
borderDash: [5, 15]
}
}],
xAxes: [ {
gridLines: {
display: true,
color: '#EEF0F4',
borderDash: [5, 15]
}
}]
},
tooltips: {
backgroundColor: '#4F5565',
titleFontStyle: 'normal',
titleFontSize: 18,
bodyFontFamily: "'Proxima Nova', sans-serif",
cornerRadius: 3,
bodyFontColor: '#20C4C8',
bodyFontSize: 14,
xPadding: 14,
yPadding: 14,
displayColors: false,
mode: 'index',
intersect: false,
callbacks: {
title: tooltipItem => {
return ` ${tooltipItem[0].xLabel}`
},
label: (tooltipItem, data) => {
let dataset = data.datasets[tooltipItem.datasetIndex]
let currentValue = dataset.data[tooltipItem.index]
return ` ${currentValue.toLocaleString()}`
}
}
},
legend: {
display: false
},
responsive: true,
maintainAspectRatio: false
}
}
},
mounted () {
this.gradient = this.$refs.canvas
.getContext('2d')
.createLinearGradient(0, 0, 0, 450)
this.gradient.addColorStop(0, 'rgba(52, 217, 221, 0.6)')
this.gradient.addColorStop(0.5, 'rgba(52, 217, 221, 0.25)')
this.gradient.addColorStop(1, 'rgba(52, 217, 221, 0)')
this.renderChart({
labels: this.chartLabels,
datasets: [
{
label: 'downloads',
borderColor: '#249EBF',
pointBackgroundColor: 'rgba(0,0,0,0)',
pointBorderColor: 'rgba(0,0,0,0)',
pointHoverBorderColor: '#249EBF',
pointHoverBackgroundColor: '#fff',
pointHoverRadius: 4,
pointHitRadius: 10,
pointHoverBorderWidth: 1,
borderWidth: 1,
backgroundColor: this.gradient,
data: this.chartData
}
]
}, this.options)
setTimeout(() => {
this.download()
}, 500)
},
methods: {
formatNumber (num) {
let numString = Math.round(num).toString()
let numberFormatMapping = [[6, 'm'], [3, 'k']]
for (let [numberOfDigits, replacement] of numberFormatMapping) {
if (numString.length > numberOfDigits) {
let decimal = ''
if (numString[numString.length - numberOfDigits] !== '0') {
decimal = '.' + numString[numString.length - numberOfDigits]
}
numString = numString.substr(0, numString.length - numberOfDigits) + decimal + replacement
break
}
}
return numString
}
}
}
</script>
您需要通知子组件重新渲染自己。
添加一个 watcher
是一种方法,观察数据变化并更新它。
另一种更简单的方法是,向其添加一个 key
道具。
在你的 App.vue
中,这样做:
<LineChart :chart-data="downloads" :chart-labels="labels" :key="downloads.length"/>
这里我使用 downloads
的长度作为键值。这是向您展示如何使用 key
的简单临时解决方案。在您的应用中,您应该使用其他一些值作为键,以防不同的 api 调用 returns 相同长度的数据。
您还可以将键设置为另一个值,并在每次调用 api 时更改此值。
我正在使用以下教程(为 NPM 包拉取下载统计信息)为我绘制的 webapp 打下基础:
- https://hackernoon.com/lets-build-a-web-app-with-vue-chart-js-and-an-api-544eb81c4b44
- https://github.com/apertureless/npm-stats
我从教程中提取了以下代码并对其进行了修改,因此它可以执行纯基础知识。获取数据并呈现数据。具体来自这些:
- https://github.com/apertureless/npm-stats/blob/develop/src/pages/Start.vue
- https://github.com/apertureless/npm-stats/blob/develop/src/components/LineChart.vue
请注意: 代码执行 API 调用并检索数据没有问题。但是,如果我更改代码,它只会在图表中呈现该数据。例如,将线条的颜色更改为其他颜色。如果有意义的话,它似乎只适用于下一个 'cycle'。数据呈现后,如果我刷新该页面,它又是空白的。我怀疑这与页面时间有关。但是不确定从哪里开始或我在寻找什么。
App.Vue
<template>
<v-app style="background-color: rgb(228, 228, 228);">
<section class="One">
<v-card class="One" color="rgb(255, 255, 255)" >
<LineChart :chart-data="downloads" :chart-labels="labels"/>
</v-card>
</section>
</v-app>
</template>
<script>
import axios from 'axios';
import LineChart from './components/test3.vue';
export default {
name: 'App',
components: {
LineChart,
},
data () {
return {
package: '',
packageName: '',
loaded: false,
loading: false,
downloads: [],
downloadsYear: [],
downloadsMonth: [],
downloadsWeek: [],
labels: [],
labelsYear: [],
labelsMonth: [],
labelsWeek: [],
showError: false,
showSettings: false,
errorMessage: 'Please enter a package name',
periodStart: '',
periodEnd: new Date(),
rawData: '',
totalDownloads: '',
dailyPng: null,
weeklyPng: null,
monthlyPng: null,
yearlyPng: null
}
},
mounted(){
this.loaded = false
axios.get(`https://api.npmjs.org/downloads/range/2017-01-01:2017-04-19/vue`)
.then(response => {
this.rawData = response.data.downloads
this.downloads = response.data.downloads.map(entry => entry.downloads)
this.labels = response.data.downloads.map(entry => entry.day)
this.packageName = response.data.package
this.totalDownloads = this.downloads.reduce((total, download) => total + download)
this.setURL()
this.groupDataByDate()
this.loaded = true
this.loading = false
})
.catch(err => {
this.errorMessage = err.response.data.error
this.loading = false
})
},
};
</script>
图表组件:
<script>
import { Line } from 'vue-chartjs'
export default {
extends: Line,
props: {
chartData: {
type: Array,
required: false
},
chartLabels: {
type: Array,
required: true
}
},
data () {
return {
gradient: null,
options: {
showScale: true,
scales: {
yAxes: [{
ticks: {
beginAtZero: false,
},
gridLines: {
display: true,
color: '#EEF0F4',
borderDash: [5, 15]
}
}],
xAxes: [ {
gridLines: {
display: true,
color: '#EEF0F4',
borderDash: [5, 15]
}
}]
},
tooltips: {
backgroundColor: '#4F5565',
titleFontStyle: 'normal',
titleFontSize: 18,
bodyFontFamily: "'Proxima Nova', sans-serif",
cornerRadius: 3,
bodyFontColor: '#20C4C8',
bodyFontSize: 14,
xPadding: 14,
yPadding: 14,
displayColors: false,
mode: 'index',
intersect: false,
callbacks: {
title: tooltipItem => {
return ` ${tooltipItem[0].xLabel}`
},
label: (tooltipItem, data) => {
let dataset = data.datasets[tooltipItem.datasetIndex]
let currentValue = dataset.data[tooltipItem.index]
return ` ${currentValue.toLocaleString()}`
}
}
},
legend: {
display: false
},
responsive: true,
maintainAspectRatio: false
}
}
},
mounted () {
this.gradient = this.$refs.canvas
.getContext('2d')
.createLinearGradient(0, 0, 0, 450)
this.gradient.addColorStop(0, 'rgba(52, 217, 221, 0.6)')
this.gradient.addColorStop(0.5, 'rgba(52, 217, 221, 0.25)')
this.gradient.addColorStop(1, 'rgba(52, 217, 221, 0)')
this.renderChart({
labels: this.chartLabels,
datasets: [
{
label: 'downloads',
borderColor: '#249EBF',
pointBackgroundColor: 'rgba(0,0,0,0)',
pointBorderColor: 'rgba(0,0,0,0)',
pointHoverBorderColor: '#249EBF',
pointHoverBackgroundColor: '#fff',
pointHoverRadius: 4,
pointHitRadius: 10,
pointHoverBorderWidth: 1,
borderWidth: 1,
backgroundColor: this.gradient,
data: this.chartData
}
]
}, this.options)
setTimeout(() => {
this.download()
}, 500)
},
methods: {
formatNumber (num) {
let numString = Math.round(num).toString()
let numberFormatMapping = [[6, 'm'], [3, 'k']]
for (let [numberOfDigits, replacement] of numberFormatMapping) {
if (numString.length > numberOfDigits) {
let decimal = ''
if (numString[numString.length - numberOfDigits] !== '0') {
decimal = '.' + numString[numString.length - numberOfDigits]
}
numString = numString.substr(0, numString.length - numberOfDigits) + decimal + replacement
break
}
}
return numString
}
}
}
</script>
您需要通知子组件重新渲染自己。
添加一个 watcher
是一种方法,观察数据变化并更新它。
另一种更简单的方法是,向其添加一个 key
道具。
在你的 App.vue
中,这样做:
<LineChart :chart-data="downloads" :chart-labels="labels" :key="downloads.length"/>
这里我使用 downloads
的长度作为键值。这是向您展示如何使用 key
的简单临时解决方案。在您的应用中,您应该使用其他一些值作为键,以防不同的 api 调用 returns 相同长度的数据。
您还可以将键设置为另一个值,并在每次调用 api 时更改此值。