Appearance
性能优化
RayChart 基于 Three.js 实时渲染 3D 场景,性能主要受数据量和材质复杂度影响。本章介绍内置的优化机制与推荐做法。
数据量控制
内置容量限制
笛卡尔图表(柱状图、折线图、散点图等)单屏可渲染的类别数由网格宽度与最小带宽决定:
- 默认
grid3D.boxWidth为 20,最小带宽为 1.6,因此单屏默认约显示 12 个类别 - 数据量超出容量时,会自动启用滚动窗口,只渲染窗口内的数据(见 坐标系统 中
xAxis3D.window的说明)
因此不需要担心一次性传入大量数据导致崩溃,但为了让关键数据完整可见,建议:
- 单系列数据量控制在数百以内(类别较多时显式配置
xAxis3D.window) - 大规模历史数据(如监控曲线)用采样或聚合减少点数
数据采样
大数据量时先采样再渲染:
javascript
// 原始数据(如 10000 个点)
const rawData = Array.from({ length: 10000 }, (_, i) => i)
// 等间隔采样到 1000 个点
const sampledData = rawData.filter((_, i) => i % 10 === 0)
const option = {
series: [{ data: sampledData }],
xAxis3D: { data: sampledData.map((_, i) => String(i)) }
}数据聚合
将数据聚合到更粗的粒度,适合时间序列:
javascript
// 按小时聚合分钟数据(取平均值)
const aggregatedData = minuteData.reduce((acc, item, i) => {
const hourIndex = Math.floor(i / 60)
if (!acc[hourIndex]) {
acc[hourIndex] = { sum: 0, count: 0 }
}
acc[hourIndex].sum += item.value
acc[hourIndex].count++
return acc
}, []).map(item => item.sum / item.count)渲染优化
避免频繁更新
option 更新会触发图表重新渲染并播放入场动画,高频更新会带来额外开销:
javascript
// ❌ 100ms 更新一次:频繁重建场景
setInterval(() => {
chartOption.value = { ...chartOption.value, series: [{ data: newData }] }
}, 100)
// ✅ 降低更新频率,如 1000ms
setInterval(() => {
chartOption.value = { ...chartOption.value, series: [{ data: newData }] }
}, 1000)使用 v-if 而非 v-show
图表组件挂载时会创建 WebGL 上下文与场景,不可见的图表应彻底卸载:
vue
<!-- ❌ v-show 仅隐藏,WebGL 资源仍被占用 -->
<RayBar v-show="visible" :option="option" />
<!-- ✅ v-if 在不可见时卸载组件,释放 GPU 资源 -->
<RayBar v-if="visible" :option="option" />及时销毁
组件卸载时框架会释放场景资源。显式调用 dispose() 可立即回收(如页面销毁前):
vue
<script setup>
import { ref, onUnmounted } from 'vue'
const chartRef = ref()
onUnmounted(() => {
chartRef.value?.dispose()
})
</script>资源管理
RayChart 内置全局资源管理器(单例),自动复用与回收 Three.js 资源:
- 材质/几何体/纹理池 - 相同配置的资源自动复用,避免重复创建
- LRU 清理 - 超过 30 秒未被引用的资源会被标记,每 60 秒自动清理一次
- 缓存上限 - 每种资源最多缓存 100 个,超出后按最近最少使用策略淘汰
以上策略均为内置行为,无需手动配置。唯一的手动入口是 ResourceManager.getInstance()(如需提前触发 cleanup() 可调用)。
性能监控
全局监控器
RayChart 提供 globalMonitor(性能监控)与 DEV_MONITOR(同实例别名):
javascript
import { globalMonitor } from 'raychart'注意
globalMonitor 仅在开发环境生效(内部由 import.meta.env.DEV 控制)。生产构建中所有调用自动变为空操作,不会产生任何开销,因此可以放心在代码中保留监控逻辑。
手动计时
javascript
// start / end 配对使用
const start = globalMonitor.start('render')
// ... 执行要测量的操作
const duration = globalMonitor.end('render', start)
console.log(`耗时: ${duration}ms`)函数计时
javascript
// 同步函数
const result = globalMonitor.measure('calculation', () => {
return heavyCalculation()
})
// 异步函数
const data = await globalMonitor.measureAsync('fetchData', async () => {
return await fetch('/api/data')
})查看统计
getStats(label) 返回指定标签的统计信息,未采样时返回 null:
javascript
const stats = globalMonitor.getStats('render')
if (stats) {
console.log('平均:', stats.avg.toFixed(2) + 'ms')
console.log('最小:', stats.min.toFixed(2) + 'ms')
console.log('最大:', stats.max.toFixed(2) + 'ms')
console.log('采样次数:', stats.count)
}统计字段:count(采样次数)、total(总耗时)、avg(平均)、min(最小)、max(最大)、last(最近一次)。
查看全部标签的统计:
javascript
// 获取报告对象
const report = globalMonitor.getReport()
// 直接打印到控制台
globalMonitor.printReport()清除与开关
javascript
globalMonitor.clear('render') // 清除指定标签
globalMonitor.clear() // 清除全部
globalMonitor.setEnabled(false) // 临时关闭监控
globalMonitor.isEnabled() // 查询是否开启图表配置优化
降低材质复杂度
PBR 材质的高光参数(metalness、roughness)与后处理特效是主要开销来源:
javascript
// ❌ 高复杂度:高金属度 + 开启 bloom
{
series: [{
data: largeDataset,
itemStyle: {
metalness: 0.8,
roughness: 0.2
}
}],
postprocessing: {
bloom: { enabled: true, strength: 1.5 }
}
}
// ✅ 优化后:降低金属度,关闭后处理
{
series: [{
data: sampledDataset, // 采样后的数据
itemStyle: {
metalness: 0.3,
roughness: 0.5
}
}]
}后处理
RayChart 目前仅内置 bloom(泛光)效果,通过 postprocessing.bloom 配置。它对性能影响显著,追求流畅度时建议关闭(详见 后处理)。
简化光照
引擎自带四点光照系统(环境光 + 主光 + 补光 + 轮廓光),不配置 lights 时自动启用:
javascript
// ✅ 默认光照:不配置 lights 即可,引擎自动处理
{
series: [{ data: [120, 200, 150] }],
xAxis3D: { data: ['A', 'B', 'C'] }
}手动配置过多光源会增加逐帧计算量,建议保持少量光源:
javascript
// 两个光源足以满足大部分场景
lights: [
{ type: 'Ambient', intensity: 0.6 },
{ type: 'Directional', intensity: 0.8, position: { x: 10, y: 15, z: 10 } }
]移动端优化
检测设备
javascript
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
const option = {
series: [{
data: isMobile ? sampledData : fullData
}],
xAxis3D: {
data: categories
},
postprocessing: {
bloom: { enabled: !isMobile } // 移动端关闭 bloom
}
}降低渲染分辨率
通过组件的 width / height 属性控制画布尺寸,移动端使用较小画布可显著降低 GPU 负担:
vue
<template>
<RayBar
:option="option"
:width="isMobile ? '100%' : '800px'"
:height="isMobile ? '400px' : '600px'"
/>
</template>性能检查清单
- [ ] 数据量是否在容量限制内,或已使用窗口 / 采样 / 聚合
- [ ] 是否避免了高频 option 更新
- [ ] 不可见图表是否使用
v-if卸载 - [ ] 页面销毁前是否调用了
dispose() - [ ] 是否按需启用 bloom 后处理
- [ ] 是否使用了默认四点光照而非过多自定义光源
- [ ] 开发环境下是否用
globalMonitor验证过渲染耗时
常见问题
Q: 图表卡顿怎么办?
A: 按开销从大到小排查:
- 关闭 bloom(
postprocessing.bloom.enabled = false) - 减少数据量(采样 / 聚合 / 窗口)
- 降低材质
metalness与roughness的复杂度 - 降低更新频率
Q: 内存持续增长?
A:
- 确认图表是否被反复创建而未销毁(使用
v-if而非v-show) - 页面销毁时调用
dispose() - 避免创建过多图表实例,必要时复用实例更新数据
Q: 移动端性能差?
A:
- 大幅减少数据量
- 关闭 bloom
- 使用较简单的材质配置
- 使用较小的画布尺寸
性能优化案例
案例 1: 大数据量折线图
问题: 10000 个数据点导致卡顿
解决方案: 先采样,再交给图表渲染:
javascript
// 等间隔采样到 1000 个点
const simplifiedData = rawData.filter((_, i) => i % 10 === 0)
const option = {
series: [{
data: simplifiedData
}],
xAxis3D: { data: simplifiedData.map((_, i) => String(i)) }
}数据量依旧很大时,可配合 xAxis3D.window 只展示部分区间(见 坐标系统)。
案例 2: 多图表页面
问题: 页面同时存在多个图表,内存与 GPU 占用高
解决方案: 只渲染可见的图表,不可见时卸载:
vue
<template>
<div v-for="chart in visibleCharts" :key="chart.id">
<component :is="chart.component" v-if="chart.visible" :option="chart.option" />
</div>
</template>
<script setup>
import { RayBar, RayLine, RayPie } from 'raychart'
// 只渲染可见的图表
const visibleCharts = computed(() => {
return charts.value.filter(chart => isInViewport(chart))
})
</script>效果: 图表随滚动按需挂载/卸载,避免所有图表同时占用 GPU 资源。
