洪泽项目 是基于 qiankun 框架的微前端项目。主应用(portal),四个子应用:综合决策分析(turbo)、模型平台(model)、维护管理平台(smart)、知识平台。
1、前言
洪泽项目时间跨度大,轮到我接手的时候(8月底)项目已经开发进行了一年多了,加之需求反反复复的变化,且每次更改并无更改的文档记录(口述),所以增加了一定的开发难度;项目牵扯面广,多个系统间存在着相互依赖的关系,还涉及多个第三方的资源,如:飞度的三维,antv/x6故障树,数学公式;需求无规划,也没有版本控制迭代这一说,而这种现象貌似已经成了此项目中的常态......
-
使用
qiankun
时,多个子应用调试问题通常的方式是将每个子项目作为一个独立的应用进行开发和调试。每个子项目都可以在本地启动,并通过修改主应用的配置,让主应用去加载本地正在运行的子应用,这样就可以对子应用进行调试了。这种方式的好处是,子应用与主应用解耦,可以独立进行开发和调试,不会相互影响。还有另一种方式就是使用
npm-run-all
这个工具,可以并行或者串行多个npm脚本。 -
三维飞度的调试
飞度调试有自己的快捷调试入口:启动飞度的客户端,会有对应的服务启动(
10.218.108.163:8090
);把IP地址复制到浏览器中(http://10.218.108.163:8090/samples/locale_zh/index.html
);把index
改为main
进行回车就进入了快捷调试的界面。前端与飞度交互,一般都是通过操作API进行:每次启动客户端都会存在目前版本所对应的文档(http://10.218.108.163:8090/doc/api/
)。飞度的调试注定是坎坷的:客户端操作可行性;部署所在机器的性能;更多API细节还得问飞度开发者。
2、问题列表
2.1 静态资源
三维中很常见的操作:打点(参考地图上坐标打点)。测试环境与开发环境中发现,点位图片消失不见!
问题分析及解决:
- 常见反应:资源本地?是否丢失或者路径不对?
- 查看代码定位,找到打点的方法(一般都是通过与三维API交互实现,
tag.add()
)(截图) - 浏览器回车,图片获取不到。地址为何获取不到图片?(IP为三维服务器,证明资源在部署三维的机器上)
- 三维机器上查找资源或者路径(三维安装很混乱,原先卸载过也不删除文件),后面总结出来了一个规律,可以按照以下的步骤查看路径下是否有文件(截图)
- 开发环境OK了,就开始定位测试环境,经过一系列排查操作后,测试环境对应的路径下资源也正常。(能咋办?该做的也都操作了,重启吧)没想到重启好了!!!
- 总结:静态资源的导入最好三维重启
2.2 CFD-水流(三维中操作二维)
需求很简单,就是CFD下需要展示水流的效果。1-5号机组与调水模拟的场景下,都存在CFD。洪泽工况有三种情况:发电、调水跟其他,1-5号机组下的CFD展示只能在调水的情况下点击,其余不能点击;调水模式很难遇到,一般是到11月份才会调水,所以调水模拟就产生了,调水模拟下水泵、机组、声纹、CFD都可以切换点击。
- 快速切换水泵、机组、声纹,多切换几次,最后到CFD,就会看到所绘制的水流存在阴影
- CFD下展示八个换能器,每次出现的时机都早于水流,正常的逻辑应该是在水流产生之后出现
问题分析及解决:
-
快速切换,前端的指令是实时的,但是调用三维接口,三维拉近视角的这个过程却是异步的。水流的绘制呢是用
canvas
实现的(大致原理:截图快照,把多余的内容转化为透明,相当于挖了一块),就是截图快照的时候有时候三维还停留在别的视角。(问题照片,此处省略。)核心:三维视角结束后,再绘制水流。
(1)、调用视角方法的回调,查看文档支持回调,前端封装方法中也支持回调。但是尝试发现回调方法没生效(此刻版本5.3),故另辟途径。
jsexport const setCamera = (arr, flyTime, fn) => { const jsb = window.__g jsb && jsb.camera.set(arr, flyTime, fn) }
(2)、三维中有切换完视角的回调方法,是系统层面的回调。全局维护两个变量,点击事件与完成回调后的事件,再对比两个变量是否一致。
jscase 'CameraChanged': if (domId === 'screen-player1') { main_before_events.value = [] main_events.value = [] } else if (domId === 'screen-player2' && main_before_events.value && main_before_events.value.length) { const arr = main_events.value arr.push({ ...event }) main_events.value = arr console.log('CameraChanged 事件刚点击 main_before_events:', main_before_events.value) console.log('CameraChanged 事件完成后 main_events:', main_events.value) } break // 监听 main_events(val) { console.log('watch newIndex main_events:', val) console.log('watch newIndex main_events_before_events:', this.main_before_events) // 相机位置切换成功的回调,是否渲染水流,应判断点击完水流出现的条件最终的视角,且当前页面的队列全部执行完成 if (this.main_before_events && val.length && this.main_before_events.length === val.length) { console.log('watch newIndex main_events_before_events length ==:', this.main_before_events.length) this.isCameraDone = true } }
(3)、前端调整调水模拟的入口。效果还是不够理想,还是会存在偶先的上述情况。故向上反馈,跟飞度三维那边协商就直接让三维搞水流,后续由于种种原因没有推进下去,希望前端这边还是能再思索别的优化方案。 (截图 对象执行focus()或相机执行set()/lookAt()/lookAtBBox()方法时触发)
(4)、水泵机组声纹CFD切换也加入弹窗遮罩,限制用户频繁点击,规避上述问题产生,降低出现问题的概率。
(5)、1108号,在书写这份文档梳理总结的时候,突然就对(1)中方法产生了浓厚兴趣,就想研究下为何回调会没生效。结果这次试验,竟然成功了,回调中有打印!!!被催的当时头脑一定在发热,要是当初第一次尝试成功,就没有后续很多繁琐的事情了...(不过此刻版本是从5.3升到5.4的,不知是否跟版本有关系,5.3版本确实存在很多问题)
jssetCamera(VIEWPORT_MAP.xcj_cfd, 0, (done) => { console.log('8888888888888889999', done) setTimeout(() => { this.isCameraDone = true }, 1000) })
-
CFD模式下,出现水流与换能器的点,绘制水流的过程需要经过一系列算法来实现,这就导致了水流还未绘制完成,换能器的八个点已经出现。(截图)
(1)、核心:延迟换能器出现的时机。canvas完成事件回调?如何知道canvas绘制完成?设置固定时间延迟?
(2)、折中办法:监听canvas的dom属性变化。经发现,绘制水流的时候,会给canvas上设置style属性,证明此时下一步就进入算法阶段了。(截图)
jsVue.directive('listen-attr', { inserted(el, binding) { var callback = function(mutationsList) { for (var mutation of mutationsList) { if (mutation.attributeName === 'done') { var disabled = el.getAttribute('done') binding.value(disabled) } } } // 创建一个观察器实例并开始监听 var observer = new MutationObserver(callback) observer.observe(el, { attributes: true }) } }) <canvas id="flow_canvas" v-listen-attr="handleDisabledChange" class="video-container" /> // 监听dom中属性,添加了style的属性证明水流绘制完成了 handleDisabledChange(data) { if (data) { console.log('disabled属性的值变为:', data) this.$emit('change-canvas-status', true) } }
change-canvas-status
触发的方法再延迟1s,用于抵消canvas中计算的时间,故方法并不是很完美,万一计算很费时间...
2.3 故障树(antv/x6)
故障树的生成是在知识平台,每次让调整修改下,口头禅:我们是做的统一平台,不能做成单独定制化的项目。导致这边只能各个角度思考解决方案,为了解决而解决。
- 可视窗口放不下:你看人家UI,配置的树的节点跟UI一致,为啥人家就能一屏放下,这边就不行呢
- 边界范围:这边最多配置多少行多少列,不会多出来的
- 展示位置不对:标红异常的一列位置不对,不能在最左列,明显数据太假了...
- 开始结束或者多余节点删除:原先是咱这边自己的后台中间拿到数据,把开始结束手动去除(嗯,是有些厉害的),后续只是又发现了:知识平台给过来的树节点,还会多一个这边不要的节点,这...
- 下载PDF中包含的故障树,树的节点样式丢失
改动前的示例:(此处省略两张图片)
解决方案:
-
拓宽可视范围的宽高;一屏一定要放的下使用 graph.zoomToFit() 这个方法;数据量少会放大节点,数据量多会过度压缩节点(节点数量区分是否缩放?层级?是否能缩短连接线?); Dagre布局
-
边界问题,毕竟UI所画的是固定的一种情形,现实中是会存在多种场景的,数据量可能更大,产品项目层不考虑而开发不得不考虑,当然需求最后也没能确认后面可能存在的数据到底有多少(无形之中又是个坑?)。总归一句话,出了问题再看再思考解决方案
-
异常数据的位置不对,每个节点配置存在XY属性,然并没生效。
- Dagre布局在作妖,作妖即除妖,干掉此布局的使用,万事大吉。
- 事实证明并非如此简单,有的节点会四五个甚至更多完全重叠在一起,有的会相互遮挡一半,很明显XY的值有问题(知识平台:我们无法调XY的值;OS:......)。
- 节点拖拽,拖拽完生成的节点信息能否导出,并且包含对应的XY信息呢?导出的信息再次保存覆盖知识平台的数据 graph.toJSON()
- 保存导出的信息再次读取,前端节点会提示报错,并且再次渲染就不能拖拽及删除
jsconst old = this.graph.toJSON().cells || [] // 利用 析构赋值 此方法去除掉component const newArray = old.map(item => { const { component, ...rest } = item return rest }) console.log(newArray) const str = JSON.stringify(newArray)
-
多余节点的删除(示例),知识平台反馈说是多余的节点是结束的上一个节点,不会是中间节点(如果要是中间节点,那就得研究X6中节点如何连接了)
-
原先的下载是主要用 html2canvas 来实现的,后面涉及到故障树的下载是通过 dom-to-image 来实现的。(故障树有自己的导出图片,能否通过最后的拼凑实现最后效果?)
- html2canvas 通过遍历DOM树,将每一个HTML元素转化为Canvas对象,并叠加到一起形成一张完整的图片或者PDF文件
- dom-to-image 库可以帮你把dom节点转换为图片,它的核心原理就是利用 svg 的 foreignObject 标签能嵌入 html 的特性,然后通过img标签加载svg,最后再通过 canvas 绘制 img 实现导出
2.4 数学公式
需求很简单,就是让项目中能够展示数学公式,项目发布后也可以正常显示。
解决方案:
-
最简单又暴力的方式-图片
-
经过初步筛选,锁定了
MathJax
渲染工具,可以做到输入LaTeX,输出HTML,展示为数学公式- mathJax只要引入全局资源和全局配置,就能自动把全站的公式都识别出来(页面渲染好后,脚本开始执行,在网页中寻找符合条件的公式符号,比如被包裹在
$
中的内容,并把它编译) - 引入依赖,不推荐用npm安装,说是有存在莫名的报错
js<script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML"></script>
新建一个js文件(MathJax.js)
jslet isMathjaxConfig = false // 用于标识是否配置 // window.MathJax = MathJax; const initMathjaxConfig = () => { if (!window.MathJax) { return } window.MathJax.Hub.Config({ showProcessingMessages: false, // 关闭js加载过程信息 messageStyle: 'none', // 不显示信息 jax: ['input/TeX', 'output/HTML-CSS'], tex2jax: { inlineMath: [['$', '$'], ['\\(', '\\)']], // 行内公式选择符 displayMath: [['$$', '$$'], ['\\[', '\\]']], // 段内公式选择符 skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code', 'a'] // 避开某些标签 }, 'HTML-CSS': { availableFonts: ['STIX', 'TeX'], // 可选字体 showMathMenu: false // 关闭右击菜单显示 } }) isMathjaxConfig = true // 配置完成,改为true } const MathQueue = function(elementId) { if (!window.MathJax) { return } window.MathJax.Hub.Queue(['Typeset', window.MathJax.Hub, document.getElementById(elementId)]) } export default { isMathjaxConfig, initMathjaxConfig, MathQueue }
多处地方使用,可以提取到mixins文件中
jsimport MathJax from '@/utils/MathJax' export default { created() { this.formatMath() }, methods: { formatMath() { setTimeout(() => { this.$nextTick(function() { if (MathJax.isMathjaxConfig) { // 判断是否初始配置,若无则配置。 MathJax.initMathjaxConfig() } MathJax.MathQueue() }) }, 300) } } }
在vue文件中直接写入
LaTeX
,就会自动识别展示为数学公式。js<span v-html="formulaOne" /> import math from '@/mixins/math' data() { return { formulaOne: `$$L = D \\times f_{s} \\times f_{w} \\times R_{w} $$` } }
到此就算是成功了,在项目中也可以正常展示了。但洪泽项目是基于乾坤的微应用,洪泽会对全局变量window下的属性进行劫持转化,会添加一层
proxy
,即window.MathJax
会变成window.proxy.MathJax
。对MathJax.js
文件进行改造?全局引入的依赖中又如何更改? - mathJax只要引入全局资源和全局配置,就能自动把全站的公式都识别出来(页面渲染好后,脚本开始执行,在网页中寻找符合条件的公式符号,比如被包裹在
-
全局依赖的改造无从下手,再加上时间紧迫,转而寻找别的开发思路,换依赖!基于vue的 VaTex ,这种方式也比上一种使用更简单,最明显的是直接展示数学公式,而不需要像上面一种先展示一串字符串,在紧接着变化为数学公式。
jsnpm i vatex katex --save import { VueLatex } from 'vatex' components: { VueLatex } <vue-latex :expression="formula" :display-mode="false" :fontsize="14" /> formula: `R_{w}=1 - {\\Delta L \\times a_{i} \\over b_{1} - b_{0}}`
3、遗留的问题
-
url
每次回车对参数进行编码单个项目中路由中带参数多次刷新都正常;经过
qiankun
打开的项目路由带参数刷新就异常 截图 -
qiankun
切换项目,清除原先的所有请求?
4、总结
- 开发过程中遇到坑,大多数人第一反应并不是翻文档(我自己也是),而是去搜索有没有人遇到类似问题。
- 遇到棘手问题的可以及时沟通,多人不同的思路,也许其中一个建议就能解决燃眉之急。