# 云析上图流程汇总
# 流程总览
# Chart.js
首先,云析模块的主页面位于tieshangongzhu
仓库的analyticGraph.vue
文件,当打开云析页面时,会调用Chart.js
的createTemporaryChart
静态方法用来新建一个临时图表,其中接收8个参数,具体含义如下:
在执行上述类的静态方法的同时,内部执行了Chart
的构造函数以及ChartMetadata
构造函数。ChartMetadata
类是用来存储一些元信息,可通过Chart
的实例对象调用getChartMetadata()
方法来获取这些元信息。
Chart
类接收4个参数,具体含义如下:
在实例化Chart
的时候,会创建空的各个图层,首先创建OriginalGraph
,然后根据ChartMetadata
里存储的是否进行叶子结点合并以及是否进行链接合并,来决定是否创建EntityMergingGraph
和LinkMergingGraph
。最后创建FinalGraph
并将上述实例化对象传入。
然后,Chart
内部会实例化CommandManager
,该类用来记录管理前进后退操作,默认最多记录5条。
此外,在Chart
、OriginalGraph
、EntityMergingGraph
、LinkMergingGraph
、FinalGraph
以及CommandManager
里都存在着execute
方法,如果在当前类里找不到相应的方法,那么就会去传入的参数里面找,可以把Chart
创建各个图层理解为入栈操作,最先入栈的是OriginalGraph
,最后入栈的是CommandManager
。把执行execute
理解为出栈操作,因此最先执行到CommandManager
,最后执行OriginalGraph
。
下图演示了Chart
初始化与调用execute
时的操作:
# CommandManager.js
CommandManager
类接收一个参数,Chart
的上下文this
,可以更方便的操作Chart
。主要是用来记录前进与撤销操作,默认可以记录的操作有:
- addSubGraph/removeSubGraph:添加与删除子图操作;
- hideSubGraph/showAll:隐藏与展示子图操作;
- setLayoutType:布局操作;
- fullLinkMerge/linkUnmerge:链接合并与取消合并操作;
- linkEliminate:链接消元操作;
- setEntityBorder:设置实体边框操作;
- setEntityScale:设置实体缩放操作;
- setLinkColor:设置链接颜色操作;
- setLinkWidth:设置链接宽度操作;
- clearStyle:清空上述四种样式操作;
- lock:设置实体锁定状态操作;
- unLock:设置实体解锁状态操作;
# Graph.js
该类继承自EventEmitter
,可以实现事件监听。内部实现了对实体与链接的增删改查、显示隐藏、设置属性等等操作,同时存储了elpData
。所有图层的事件都会经过该类进行触发,然后由该类里的方法修改最终生成的实体与链接。
# FinalGraph.js
FinalGraph
类继承自基类Graph
,并接收一个参数,该参数可以是OriginalGraph
、EntityMergingGraph
、LinkMergingGraph
,传入什么参数取决于是否进行叶子结点合并以及是否进行链接合并。具体来说,如果needLinkMerge
为true
,则传入LinkMergingGraph
的实例对象,如果needEntityMerge
为true
,则传入EntityMergingGraph
的实例对象,都为false
则传入OriginalGraph
的实例对象。
在该类中,主要操作包括监听了由Graph
emit出的changed
事件,执行对实体与链接的增删改查、显示隐藏、设置属性等等操作。中转了elp-changed
、collection-add
、collection-remove
等事件到Chart
中。将大部分对图数据的操作包裹为Promise
,方便进行异步调用。
# LinkMergingGraph.js
LinkMergingGraph
类继承自基类Graph
,并接收三个参数,具体参数含义如下:
在该类中,主要操作包括链接合并规则的设置与读取、链接样式的设置、链接合并与取消合并,同样也监听了由Graph
emit出的事件,对链接进行增删改查,但是最终都是调用基类Graph
的方法,将数据存储在Graph
里。
# EntityMergingGraph.js
EntityMergingGraph
类继承自基类Graph
,并接收两个参数,具体含义如下:
该类的作用与LinkMergingGraph
的作用类似。
# OriginalGraph.js
OriginalGraph
类继承自基类Graph
,并接收三个参数,具体含义如下:
在该类中,会执行添加、删除、隐藏、展示子图的操作,以及设置实体与链接的样式。
# 图层总结
经过上面的概述可以发现,对实体链接的许多操作在很多图层里同时存在,那么真实调用的顺序是怎么样的?以全部显示showAll
方法为例:
首先分为两个阶段,分别是图层初始化阶段与方法调用阶段。
可以看到,在第二步与第三步的时候,都监听了graph
实例对象的changed
事件。而在Graph.js
中,会监听FinalGraph
实例上的changed
方法,同时触发回调函数,在回调函数内会广播graph
实例上的changed
事件,所有监听了graph
实例对象changed
事件的地方都会响应。
在代码执行阶段,首先在analyticGraph.vue
里会调用this.chart.execute('showAll')
,然后会经由CommandManager
、FinalGraph
、OriginalGraph
,在OriginalGraph
里触发showAll
,在Graph.js
里去执行showLink
方法。接下来广播changed
事件,在FinalGraph
里会捕获该事件并广播changed
事件。由上图第五步可知,渲染层的Graph.js
会去监听FinalGraph
实例上changed
事件,因此最终会触发第二、三步里的回调,执行onGraphChanged
事件,进行webGL
的渲染。
# 两个Graph的区别
注意这里跟Chart
打交道的Graph
与渲染使用的Graph
是两个不同的文件,与Chart
相关的Graph
主要是存储实体与链接并对其进行操作,与渲染相关的Graph
主要是监听事件,并通知到各个图层去执行具体的方法。
# 上图时间分析
# 页面初始化
首先,云析页面初始化的时候,WebGL已初始化完毕,包括NodeContainer
和LinkContainer
已经把实体和链接所需的纹理与顶点缓存至pixiRenderer
当中,使用的是pixi
提供的pixi-gl-core
库。初始化webGL的时间在100ms左右。
# 上图中
然后在上图的过程中,以1w实体,2w链接为例,整体的时间为4s。其中耗时较多的执行方法包括,
OriginalGraph
里的添加子图方法addSubGraph
耗时160msForceLayoutInNGraph.js
里监听graph
变化的回调函数onGraphChanged
耗时146msForceLayoutInNGraph.js
里与默认布局有关的方法step
耗时312mspixiRenderer.js
里的onGraphChanged
函数耗时2.2spixi.js
里内置的渲染webGL的方法WebGLRenderer.js
耗时848ms- 云析UI页面
analyticGraph.vue
里监听graph变化的函数graphChangeListener
耗时150ms
优化方向:
1、优化默认布局相关代码,缩减2、3的耗时; 2、
pixiRenderer.js
里的onGraphChanged
占总耗时的一半以上,是因为针对上图的每个实体和链接都进行了初始化的操作,考虑将初始化的操作放入js原生提供的web worker
内,采用额外的线程执行代码,防止阻塞主线程,该方案需要进一步研究;
# 上图后
上完图后,当数据量较多的时候,针对实体的拖拽等操作会有明显的卡顿,经查看浏览器的火焰图发现,是因为pixi.js
的核心库InteractionManager
会不断的监听鼠标的事件,包括鼠标移动、单击落下、单击抬起的操作,当鼠标移动的时候,会不断的触发onPointerMove
事件,该事件每次执行时间在70-100ms之间,阻塞了前端动画事件的执行,由此产生卡顿。
优化方向:
1、尝试修改自定义事件文件
customizedEventHandling.js
的逻辑,优化事件的监听,避免频繁触发,产生阻塞。
# 总结
总体来看,优化方向不涉及到webGL的优化,主要还是以修改业务代码为主;在pixijs
的基础上,对性能进行优化。