echart在Angular里面使用,会持续占用cpu,这是性能图:
主要是触发了AnimationFrame:
通过callstack查询到这个函数会持续调用:
对应的echart里的代码:
主要是Animation对象里的_startLoop
函数里有个递归调用step
,并且self._running
一直是true
,这使得step
一直会被调用。这在Angular的zone会触发变更检查,导致占用cpu。一般一张图会占用8、9个点。
页面无操作时,不应该占用cpu
option = {
}
Related issue #2886
@hpyou 所以跟 Angular 环境没什么关系是吧?我后面会看下
@Ovilia 是的,我在一个新的html里面引用echart也会有这样的问题,只是在Angular的zone里面性能消耗更明显一点。
请 @hpyou 把你测试的html文件给贴到这里,麻烦 @Ovilia 看看。
这个问题和angular没有任何关系。
@Ovilia 当页面上只有少数几个图的时候,看不出区别,但是当一个页面上很多图(我们的一个页面有30个echart图)的时候,浏览器进程就把cpu吃满了。
@rdkmaster 图片类型有什么特殊的吗?还是每种都会有?
@Ovilia 我用是饼图,
echart代码用npm包里的echarts\dist\echarts.js ,版本是3.5.4。
这是html页面:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="height:400px"></div>
<!-- ECharts单文件引入 -->
<script src="echarts.js"></script>
<script type="text/javascript">
var myChart = echarts.init(document.getElementById('main'));
var option = {
title : {
text: '某站点用户访问来源',
subtext: '纯属虚构',
x:'center'
},
tooltip : {
trigger: 'item',
formatter: "{a} <br/>{b} : {c} ({d}%)"
},
legend: {
orient : 'vertical',
x : 'left',
data:['直接访问','邮件营销','联盟广告','视频广告','搜索引擎']
},
toolbox: {
show : true,
feature : {
mark : {show: true},
dataView : {show: true, readOnly: false},
magicType : {
show: true,
type: ['pie', 'funnel'],
option: {
funnel: {
x: '25%',
width: '50%',
funnelAlign: 'left',
max: 1548
}
}
},
restore : {show: true},
saveAsImage : {show: true}
}
},
calculable : true,
series : [
{
name:'访问来源',
type:'pie',
radius : '55%',
center: ['50%', '60%'],
data:[
{value:335, name:'直接访问'},
{value:310, name:'邮件营销'},
{value:234, name:'联盟广告'},
{value:135, name:'视频广告'},
{value:1548, name:'搜索引擎'}
]
}
]
};
// 为echarts对象加载数据
myChart.setOption(option);
</script>
</body>
</html>
@Ovilia 在Angular中有个变更检查机制,会在每个异步操作和事件触发后检查组件树。在echart中如果不停的触发Animation Frame,就会导致Angular不停的检查组件树。如果组件树很庞大,就会占用很多cpu。
Angular每次执行变更检查,会在组件中执行ngDoCheck
钩子:
在控制台中可以看到不停的打印:
@Ovilia Hi~, my friend
我研究了requestAnimationFrame
的相关问题。requestAnimationFrame
相对于setTimeout
是不一样的,不管执行多少requestAnimationFrame
,网页的AnimationFrame
事件永远只会有一个,也就是不管有多少张图,页面的cpu占用只会有1、2个点,这对性能的影响可以忽略不计的。
我们这边也用了一个方法,将echarts运行到了Angular Zone
之外,AnimationFrame
触发变更检查的问题得到了解决。
谢谢,麻烦了。
这个问题是另一个典型的angular的change detection机制与传统前端库的冲突例子,建议echart可以将这个解决方法放到你们的faq里,让在angular中使用echart的其他同学不掉这个坑。
类似的问题Jigsaw已经碰到过个了
@rdkmaster 谢谢建议
@rdkmaster 扯,还典型例子
@hpyou 你好,有个问题想和你交流一下,希望你有空的时候回复一下。请问你将echart置于zone之外后,那生成的图表是不受zone监控的。那么仅仅是展示的话应该是没问题的。若是有数据交互的话,那不是很麻烦了?
@yy7054wyq5 有数据交互,用zone.run放到zone里面跑,确保echarts.init在zone之外就行
@hpyou 谢谢解惑。
在没有任何操作的情况下无限调用requestAnimationFrame是出于什么原因。。。
用angular初始化echarts时需要放到监测zone外面:
this.ngZone.runOutsideAngular(() => {
echarts.init(nativeElement);
})
我在使用 this.ngZone.runOutsideAngular(() => { echarts.init(nativeElement); }) 后,在 Performance 火焰图里看还是由 AnimationFrame Fired -> zone.js -> (zrender) step。
但如果在 zone.js 加载前,设置标志 __zone_symbol__requestAnimationFrame = true ,就不会有 zone.js 了。看这个
Most helpful comment
@yy7054wyq5 有数据交互,用zone.run放到zone里面跑,确保echarts.init在zone之外就行