Tengo esta página muy simple, que funciona correctamente:
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/vue@next"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.0/dist/chart.js"></script> </head> <body> <main id="vue-app"> <p>{{ name }}</p> <canvas id="chart-flow-rate"></canvas> </main> </body> <script> // Start VueJS const Application = { data() { return { name: "My Chart" }; } } vm = Vue.createApp(Application).mount('#vue-app'); // Use ChartJS const myChart = new Chart('chart-flow-rate', { type: 'bar', data: { labels: ['4', '2'], datasets: [{ data: [4, 2], }] } }); </script>Sin embargo, si invierto los bloques de JS para usar ChartJS primero y luego para crear la aplicación VueJS, la página ya no funciona: no se muestra el gráfico.
¿Por qué?
He notado que puedo cambiar un poco el body de mi HTML para poder usar ChartJS antes que VueJS:
<body> <main> <p id="vue-app">{{ name }}</p> <canvas id="chart-flow-rate"></canvas> </main> </body>De nuevo: ¿por qué ?
¡Muchas gracias! :)
Esto se debe a que VueJS usa DOM virtual y actualiza el DOM real en lotes: lo que le proporciona a su aplicación es una plantilla en línea, que VueJS analizará, interpolará y reescribirá en el DOM. Por lo tanto, si inicializa ChartJS primero, perderá la referencia al elemento DOM (se ha ido debido a una condición de carrera con VueJS, o Vue lo sobrescribe rápidamente una vez que calcula el DOM virtual y lo envía al DOM real).
La razón por la que funcionó cambiar su marcado es porque ahora VueJS ya no borra ni manipula el elemento que ChartJS usa para montar y representar el gráfico, ya que ya no es parte de la aplicación Vue sino que reside fuera de ella en el DOM normal.
De hecho, la solución más parsimoniosa es simplemente crear una instancia de ChartJS en el gancho mounted de su aplicación VueJS y después de esperar a que el DOM esté listo, es decir:
mounted: async function() { // Wait for the DOM to be rendered after next tick await this.$nextTick(); // Use ChartJS with the element that now exists in the DOM const myChart = new Chart('chart-flow-rate', { type: 'bar', data: { labels: ['4', '2'], datasets: [{ data: [4, 2], }] } }); } De hecho, iría un paso más allá y le imploraría que evite usar métodos de consulta DOM y aproveche el atributo ref y la propiedad de instancia $refs . En su plantilla, actualice su marcado para usar referencias:
<canvas ref="chart"></canvas> Dado que ChartJS acepta un elemento como primer argumento, simplemente puede acceder al elemento usando this.$refs.canvas :
const myChart = new Chart(this.$refs.chart, { ... })Vea la prueba de concepto a continuación:
// Start VueJS const Application = { data() { return { name: "My Chart" }; }, mounted: async function() { await this.$nextTick(); // Use ChartJS const myChart = new Chart(this.$refs.chart, { type: 'bar', data: { labels: ['4', '2'], datasets: [{ data: [4, 2], }] } }); } } vm = Vue.createApp(Application).mount('#vue-app'); <script src="https://unpkg.com/vue@next"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.0/dist/chart.js"></script> <main id="vue-app"> <p>{{ name }}</p> <canvas ref="chart"></canvas> </main>