# 云析上图流程汇总

# 流程总览

云析流程

# Chart.js

首先,云析模块的主页面位于tieshangongzhu仓库的analyticGraph.vue文件,当打开云析页面时,会调用Chart.jscreateTemporaryChart静态方法用来新建一个临时图表,其中接收8个参数,具体含义如下:

Chart

在执行上述类的静态方法的同时,内部执行了Chart的构造函数以及ChartMetadata构造函数。ChartMetadata类是用来存储一些元信息,可通过Chart的实例对象调用getChartMetadata()方法来获取这些元信息。 Chart类接收4个参数,具体含义如下:

newChart

在实例化Chart的时候,会创建空的各个图层,首先创建OriginalGraph,然后根据ChartMetadata里存储的是否进行叶子结点合并以及是否进行链接合并,来决定是否创建EntityMergingGraphLinkMergingGraph。最后创建FinalGraph并将上述实例化对象传入。 然后,Chart内部会实例化CommandManager,该类用来记录管理前进后退操作,默认最多记录5条。

此外,在ChartOriginalGraphEntityMergingGraphLinkMergingGraphFinalGraph以及CommandManager里都存在着execute方法,如果在当前类里找不到相应的方法,那么就会去传入的参数里面找,可以把Chart创建各个图层理解为入栈操作,最先入栈的是OriginalGraph,最后入栈的是CommandManager。把执行execute理解为出栈操作,因此最先执行到CommandManager,最后执行OriginalGraph

下图演示了Chart初始化与调用execute时的操作: 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,并接收一个参数,该参数可以是OriginalGraphEntityMergingGraphLinkMergingGraph,传入什么参数取决于是否进行叶子结点合并以及是否进行链接合并。具体来说,如果needLinkMergetrue,则传入LinkMergingGraph的实例对象,如果needEntityMergetrue,则传入EntityMergingGraph的实例对象,都为false则传入OriginalGraph的实例对象。

在该类中,主要操作包括监听了由Graphemit出的changed事件,执行对实体与链接的增删改查、显示隐藏、设置属性等等操作。中转了elp-changedcollection-addcollection-remove等事件到Chart中。将大部分对图数据的操作包裹为Promise,方便进行异步调用。

# LinkMergingGraph.js

LinkMergingGraph类继承自基类Graph,并接收三个参数,具体参数含义如下: LinkMergingGraph 在该类中,主要操作包括链接合并规则的设置与读取、链接样式的设置、链接合并与取消合并,同样也监听了由Graphemit出的事件,对链接进行增删改查,但是最终都是调用基类Graph的方法,将数据存储在Graph里。

# EntityMergingGraph.js

EntityMergingGraph类继承自基类Graph,并接收两个参数,具体含义如下: EntityMergingGraph 该类的作用与LinkMergingGraph的作用类似。

# OriginalGraph.js

OriginalGraph类继承自基类Graph,并接收三个参数,具体含义如下: OriginalGraph 在该类中,会执行添加、删除、隐藏、展示子图的操作,以及设置实体与链接的样式。

# 图层总结

经过上面的概述可以发现,对实体链接的许多操作在很多图层里同时存在,那么真实调用的顺序是怎么样的?以全部显示showAll方法为例: 首先分为两个阶段,分别是图层初始化阶段与方法调用阶段。 initRender

可以看到,在第二步与第三步的时候,都监听了graph实例对象的changed事件。而在Graph.js中,会监听FinalGraph实例上的changed方法,同时触发回调函数,在回调函数内会广播graph实例上的changed事件,所有监听了graph实例对象changed事件的地方都会响应。

在代码执行阶段,首先在analyticGraph.vue里会调用this.chart.execute('showAll'),然后会经由CommandManagerFinalGraphOriginalGraph,在OriginalGraph里触发showAll,在Graph.js里去执行showLink方法。接下来广播changed事件,在FinalGraph里会捕获该事件并广播changed事件。由上图第五步可知,渲染层的Graph.js会去监听FinalGraph实例上changed事件,因此最终会触发第二、三步里的回调,执行onGraphChanged事件,进行webGL的渲染。

# 两个Graph的区别

注意这里跟Chart打交道的Graph与渲染使用的Graph是两个不同的文件,与Chart相关的Graph主要是存储实体与链接并对其进行操作,与渲染相关的Graph主要是监听事件,并通知到各个图层去执行具体的方法。

# 上图时间分析

# 页面初始化

首先,云析页面初始化的时候,WebGL已初始化完毕,包括NodeContainerLinkContainer已经把实体和链接所需的纹理与顶点缓存至pixiRenderer当中,使用的是pixi提供的pixi-gl-core库。初始化webGL的时间在100ms左右。

# 上图中

然后在上图的过程中,以1w实体,2w链接为例,整体的时间为4s。其中耗时较多的执行方法包括,

  1. OriginalGraph里的添加子图方法addSubGraph耗时160ms
  2. ForceLayoutInNGraph.js里监听graph变化的回调函数onGraphChanged耗时146ms
  3. ForceLayoutInNGraph.js里与默认布局有关的方法step耗时312ms
  4. pixiRenderer.js里的onGraphChanged函数耗时2.2s
  5. pixi.js里内置的渲染webGL的方法WebGLRenderer.js耗时848ms
  6. 云析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的基础上,对性能进行优化。