You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

问题原因分析
  1. 异步时序不匹配:renderedCallback加载完Chart.js后立即初始化图表,但Apex wire方法是异步的,此时数据还没返回,chartLabels和chartValues为空,导致无数据图表;硬编码数据是同步的,所以不会有这个问题。
  2. 渲染逻辑冲突:displayChart控制canvas元素的渲染,但图表初始化只执行一次。当wire数据返回后,displayChart变为true,canvas才被渲染,但此时初始化代码已经跑完,找不到canvas元素,导致空白。
  3. 页面加载差异:点击“主页”标签时,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 23:44:51