Salesforce LWC集成Chart.js加载异常问题排查求助
问题描述
我在Salesforce首页实现了集成Chart.js的Lightning Web Component,该组件有时加载正常,有时显示空白,有时仅呈现无数据的图表,调整浏览器窗口大小后可能恢复正常。
当我在JavaScript中硬编码数据时,组件每次都能正常加载,但改为通过返回Map<String,Decimal>的Apex方法获取数据后,疑似渲染时机过早(数据尚未就绪)导致异常。
此外,浏览器刷新页面时组件极少正常加载,但点击Salesforce的“主页”标签时,组件大多能正常显示。
相关代码
JavaScript代码
import { LightningElement, track, wire } from 'lwc'; import { loadScript } from 'lightning/platformResourceLoader'; import ChartJs from '@salesforce/resourceUrl/ChartJsNew'; import Id from '@salesforce/user/Id'; import getvaluesMapUser from '@salesforce/apex/userChart_Util.getValuesMapUser'; export default class userChart extends LightningElement { isChartJsInitialized = false; chartLabels=[]; chartValues=[]; displayChart = false; userId = Id; @wire(getvaluesMapUser, { userId: '$userId' }) wiredValuesMap({ data, error }) { if (data) { const valuesMap = data; console.log(valuesMap); for (const key in valuesMap) { this.chartLabels.push(key); this.chartValues.push(valuesMap[key]); } if(Object.keys(valuesMap).length > 0){ this.displayChart = true; } else { this.displayChart = false; } } else if (error) { console.log('Error getting values map user'); console.error(error); } } renderedCallback() { if (this.isChartJsInitialized) { return; } this.isChartJsInitialized = true; Promise.all([ loadScript(this, ChartJs) ]) .then(() => { // Chart.js library loaded this.initializeChart(); }) .catch(error => { console.log('Error loading Chart.js'); console.error(error); }); } initializeChart() { const ctx = this.template.querySelector('canvas').getContext('2d'); new window.Chart(ctx, { data: { labels: this.chartLabels, datasets: [ { label: 'Values', data: this.chartValues, backgroundColor: 'rgba(14, 112, 199, 1)', borderColor: 'rgba(14, 112, 199, 1)', type: 'bar', borderWidth: 1, order: 1 } ] }, options: { responsive: true, scales: { y: { beginAtZero: true } } }, }); } }
HTML代码
<template> <template lwc:if={displayChart}> <lightning-card title="Values Chart" icon-name="custom:custom17"> <div> <canvas id="chart"></canvas> </div> </lightning-card> </template> <template lwc:else> <lightning-card title="Values Chart" icon-name="custom:custom17"> <div class="slds-m-around_medium"> <p>No Data To Display</p> </div> </lightning-card> </template> </template>
问题原因分析
- 异步时序不匹配:
renderedCallback加载完Chart.js后立即初始化图表,但Apex wire方法是异步的,此时数据还没返回,chartLabels和chartValues为空,导致无数据图表;硬编码数据是同步的,所以不会有这个问题。 - 渲染逻辑冲突:
displayChart控制canvas元素的渲染,但图表初始化只执行一次。当wire数据返回后,displayChart变为true,canvas才被渲染,但此时初始化代码已经跑完,找不到canvas元素,导致空白。 - 页面加载差异:点击“主页”标签时,Salesforce可能复用了组件实例或缓存数据,wire返回更快,刚好赶上图表初始化时机;刷新页面时所有资源重新加载,异步顺序难以匹配,所以失败概率高。
修复方案
核心思路:确保Chart.js加载完成、数据就绪、canvas元素存在三个条件都满足后,再初始化或更新图表
修改后的JavaScript代码:
import { LightningElement, wire } from 'lwc'; import { loadScript } from 'lightning/platformResourceLoader'; import ChartJs from '@salesforce/resourceUrl/ChartJsNew'; import Id from '@salesforce/user/Id'; import getvaluesMapUser from '@salesforce/apex/userChart_Util.getValuesMapUser'; export default class userChart extends LightningElement { isChartJsLoaded = false; chartLabels = []; chartValues = []; displayChart = false; userId = Id; chartInstance = null; // 保存图表实例,方便后续更新 @wire(getvaluesMapUser, { userId: '$userId' }) wiredValuesMap({ data, error }) { if (data) { // 重置数据,避免wire重复触发时数据累加 this.chartLabels = []; this.chartValues = []; const valuesMap = data; for (const key in valuesMap) { this.chartLabels.push(key); this.chartValues.push(valuesMap[key]); } this.displayChart = Object.keys(valuesMap).length > 0; // 数据就绪后,检查Chart.js是否加载完成,满足则更新/初始化图表 if (this.isChartJsLoaded && this.displayChart) { this.updateOrInitializeChart(); } } else if (error) { console.error('Error getting values map user', error); this.displayChart = false; } } renderedCallback() { if (this.isChartJsLoaded) { // Chart.js已加载,检查是否需要初始化/更新图表 this.updateOrInitializeChart(); return; } // 加载Chart.js loadScript(this, ChartJs) .then(() => { this.isChartJsLoaded = true; // 加载完成后,检查数据是否就绪 if (this.displayChart) { this.updateOrInitializeChart(); } }) .catch(error => { console.error('Error loading Chart.js', error); }); } updateOrInitializeChart() { // 三个条件:需要显示图表、canvas存在、数据非空 if (!this.displayChart || this.chartLabels.length === 0) return; const canvas = this.template.querySelector('canvas'); if (!canvas) return; const ctx = canvas.getContext('2d'); if (this.chartInstance) { // 已有图表实例,直接更新数据 this.chartInstance.data.labels = this.chartLabels; this.chartInstance.data.datasets[0].data = this.chartValues; this.chartInstance.update(); } else { // 初始化新图表 this.chartInstance = new window.Chart(ctx, { data: { labels: this.chartLabels, datasets: [ { label: 'Values', data: this.chartValues, backgroundColor: 'rgba(14, 112, 199, 1)', borderColor: 'rgba(14, 112, 199, 1)', type: 'bar', borderWidth: 1, order: 1 } ] }, options: { responsive: true, scales: { y: { beginAtZero: true } } }, }); } } }
关键修复点
- 条件校验:每次渲染都检查Chart.js加载状态、数据状态、canvas是否存在,满足条件才执行图表操作
- 数据重置:wire触发时清空旧数据,避免重复累加
- 图表实例复用:保存图表实例,数据更新时直接修改实例数据并调用
update(),无需重新创建 - 时序兼容:无论Chart.js先加载还是数据先返回,都能触发图表初始化/更新
内容的提问来源于stack exchange,提问作者user31943000
相关产品推荐
相关产品推荐

