Incubator-echarts: 在Angular应用中使用echart,会持续占用cpu

Created on 15 Nov 2017  ·  18Comments  ·  Source: apache/incubator-echarts

One-line summary [问题简述]

echart在Angular里面使用,会持续占用cpu,这是性能图:

image

主要是触发了AnimationFrame:

image

通过callstack查询到这个函数会持续调用:

image

对应的echart里的代码:
image

主要是Animation对象里的_startLoop函数里有个递归调用step,并且self._running一直是true,这使得step一直会被调用。这在Angular的zone会触发变更检查,导致占用cpu。一般一张图会占用8、9个点。

image

Version & Environment [版本及环境]

  • ECharts version [ECharts 版本]: 3.5.4
  • Browser version [浏览器类型和版本]: chrome 60.0.3112.101(正式版本) (32 位)
  • OS Version [操作系统类型和版本]: win7

Expected behaviour [期望结果]

页面无操作时,不应该占用cpu

ECharts option [ECharts配置项]


option = {

}

Other comments [其他信息]


Most helpful comment

@yy7054wyq5 有数据交互,用zone.run放到zone里面跑,确保echarts.init在zone之外就行

All 18 comments

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钩子:
image

在控制台中可以看到不停的打印:
image

@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 了。看这个

Was this page helpful?
0 / 5 - 0 ratings