程序报错不可怕,可怕的是出错了不报错,令人绝望。
今天一天思考和修复这个问题,比较尴尬的是,解决了后仍然不知道发生了什么,可能是运行环境的差异导致的现象吧。
现象如下图:
可以看到在 vue-element-admin 和 electron-vue-serialport 中的表现不一样,electron-vue-serialport 中侧边栏已经错位了。
起初按经验来说,我是先思考,应该是 css 样式布局出了问题,但简单的检查一下发现,electron 中不能使用审查元素,这就棘手了。
因为这是 vue 框架出来的代码,不像传统的 html + css 可以查阅相关性,很多隐性的 style 的 class 修改难以预料。
但 vue-element-admin 的部分是没问题的,那么我就手动展开开发者调试工具,去手动调整 HTML 的内容试图修复,第一次测试对比发现它掉标签了,所以有一个 Tooltip 的标签丢失了(经由 vue-element-admin 的对比发现),那么就看看代码的关联性。
vue 框架的代码关联性很难找,如果不是正向写出代码的人,在面对庞大的 vue 项目一般是会看懵的,甚至无从下手直接迷茫,但稍微理解几个点有利于去逆向理解整个项目代码,必须清楚知道 vue 组件 components 和视图 views 的区别与关系,了解 views 是如何调用和包含 components 的。
我在阅读和理解后,明白了自定义标签的由来,这个是一个难以搜索的函数链,因为 views 包含的 components 名称和使用时的标签并非统一,而是大写(AppLink)对小写(<app-link></app-link>
)的关系,所以在搜索代码的时候要稍微留意这层命名的区别,如果不知道这个事实就容易找不到要深入的代码。
在比对两者的代码后,发现应该是 views 的部分出了问题。
<template>
<div v-if="!item.hidden" class="menu-wrapper">
<!-- <div> {{ !item.hidden }} </div> -->
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
</el-menu-item>
</app-link>
</template>
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
<template slot="title">
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
</template>
<sidebar-item
v-for="child in item.children"
:key="child.path"
:is-nest="true"
:item="child"
:base-path="resolvePath(child.path)"
class="nest-menu"
/>
</el-submenu>
</div>
</template>
在不了解的情况下,我一般会先解决问题为主,最后再来思考为什么。
既然知道是侧边栏(Sidebar)出问题,根据原型 vue-element-admin 对照,先是试图 build:web 产生 electron 下的 web ,然后用 pm2 启动去判断,but 意料内的失败了,编译出现太多依赖问题,感觉去修复的话就会走偏,没有定位问题反而产生更多问题。
那么怎么做呢,先是对拷两边的 HTML 内容发现,确认了确实是缺少标签导致的,但是为什么会缺少呢?这个原因也不了解,那就试试旧代码,因为 electron-vue-admin 来的,也同样使用了 element-ui 且样式正常。
所以上述代码改成如下:
<template>
<div v-if="!item.hidden" class="menu-wrapper">
<!-- <div> {{ !item.hidden }} </div> -->
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
<svg-icon v-if="onlyOneChild.meta&&onlyOneChild.meta.icon" :icon-class="onlyOneChild.meta.icon"></svg-icon>
<span v-if="onlyOneChild.meta&&onlyOneChild.meta.title" slot="title">{{onlyOneChild.meta.title}}</span>
<!-- <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" /> -->
</el-menu-item>
</app-link>
</template>
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
<template slot="title">
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
</template>
<sidebar-item
v-for="child in item.children"
:key="child.path"
:is-nest="true"
:item="child"
:base-path="resolvePath(child.path)"
class="nest-menu"
/>
</el-submenu>
</div>
</template>
既然在调试器中测试通过,评估两者不同的地方就在于引用 vue 的 item 组件,那么就试图按以下步骤处理它。
- 复制旧代码到新框架,成功,但引出了其他问题,丢失了二级菜单列。
- 检查代码,将旧代码的变量替换,符合预期。
- 最后把这个封装退出来,还原回未封装的状态。
- 然后,就结束......了。
其实我也只是比较巧妙的解决了问题,因为我并没有在新代码上解决问题,因为我也不知道新代码上要如何解决问题,我只能根据一个既成事实去确认修复方案后解决问题。
那么本次事件到此结束,不管遇到什么困难!我们都要奥力给 XD。
- 解决问题不是学习,不需要以理解原理和正面对着干才是唯一的方法
- 曲线救国不一定好,但一定可以解决问题。
在解决了问题后再去思考为什么就好了,所以最后的个人分析是认为,两者的编译环境并不统一,而且也没有牛逼到所有组件的关系我都清清楚楚,这个项目已经不小了,没办法保证所有部件没有冲突,样式表冲突的可能性比较大,举个例子,现在版本中 item 标签已经存在了,如果不注意去使用这个标签,那么顺着更新的过程中,这个标签将会存在问题,而这个问题是前者组件先引入留下来的,所以后来载入的组件覆盖后,其他样式表 css 之间的关系就会冲突,想来这个项目有机会还是要退回 template 的方式后去解构,否则许多不清楚的组件冲突了只会让事情变得复杂和麻烦,如果不是特别熟悉这个项目的码农(例如我 XD),那就会破坏这个项目的架构稳定性了。
但个人的感觉来看,应该是环境差异,支持的部分没有统一,因为一个两年前的框架去兼容现在的项目,或多或少都会有一些编译链的问题,附带该项目 electron-vue-serialport 喔。