技术栈
技术栈仍然是 html ,css ,js ,vue3
问题描述
数据变化之后,节点未正确渲染到真实DOM树,导致该元素不显示。
场景
这里小编啰嗦一句:在开发中任何问题都不应脱离场景讨论。
本问题的具体场景如下:
web端开发中
页面元素:div(包含icon图标和文本两部分)、Element-Plus库中的抽屉组件 以及 下拉列表组件
步骤:
1.打开抽屉通过下拉列表修改了icon图标后保存,抽屉关闭修改成功。
2.再次修改 将选择图标元素的下拉列表清除(清除后会显示默认图标),点击保存之后默认图标未正常显示。下面附代码:
//按逻辑 这里若没有选择图标即userIcon为'',理应显示el-icon,但未达预期
<div style="width: 100%">
<el-row>
<el-col
:span="9"
style="display: flex; align-items: center; justify-content: right"
>
<el-icon size="20" v-if="item.info.userIcon == ''"
><***ponent :is="item.info.icon" />
</el-icon>
<img
v-else
:src="`/icon_svg/${item.info.userIcon}.svg`"
:style="{
width: 30 + 'px',
height: 30 + 'px',
backgroundColor: node?.info.color,
objectFit: 'contain',
borderRadius: '10px',
}"
/>
</el-col>
<el-col :span="1" />
<el-col
:span="14"
style="display: flex; align-items: center; justify-content: left"
>
<span>{{ item.info.name }}</span>
</el-col>
</el-row>
</div>
问题分析
出现这样的问题首先会想到机制方面的问题 逻辑判断v-if出错 、 数据未正确传递 、 异步导致的数据处理在dom渲染之后完成(echarts图表常见)、vue响应式未更新等
但经排查发现数据均已正确传递并保存到了pinia中触发了响应式更新而且此处并无异步处理,所以下一步就该考虑逻辑方面的问题,具体深入到代码中进行判断,经过查找发现了如下代码:
const drawerConfirm = (data) => {
...
const index = selectedNodeId.value.split('_')[1];
const newData = JSON.parse(JSON.stringify(store.getDoms()[index]));
...
store.updateDomInfo(index, newData);//最后把修改的新内容保存到pinia中
}
这段代码来自于画布组件,用于接收抽屉组件传递的数据,由于store.getDoms()有引用数据类型的属性,所以为了防止数据污染,我进行了深拷贝,而恰恰就是因为这个深拷贝操作导致了这个问题的产生!
我们知道,使用JSON进行深拷贝具有一定的局限性,其中之一就是不能拷贝函数,而在这getDoms()中的info属性如下:
import { Bug ...} from '@icon-park/vue-next';
{
id: 'input-1',
name: '变量',
icon: Bug,
userIcon: '', //用户自定义的图标
color: '#3593ffff',
nodebgColor: '#76acfcff',
nodeColor: '#ffffff',
node: ['right'],
returnNum: 1,
variable: {
applyId: null, //应用id
key: null, //变量key
},
}
有代码可知其中的icon的属性值为引入的第三方库中的图标,而关键就在于这个图标本质是一个vue文件,而vue文件最后在构建的时候会被构建为一个函数!于是原因便清晰明了了
解决方案
那么原因知晓之后我们可以针对此原因做出如下修改:
const drawerConfirm = (data) => {
...
const index = selectedNodeId.value.split('_')[1];
const newData = JSON.parse(JSON.stringify(store.getDoms()[index]));
newData.info.icon = store.getDoms()[index].info.icon;
...
store.updateDomInfo(index, newData);//最后把修改的新内容保存到pinia中
}
在修改之前,pinia中的内容可是一切正常的,所以只要把原本的内容拿过来就可以了!
vue文件本质
template板块
是vue文件的模板语法,构建的时候会被编译成一个渲染函数,并返回虚拟DOM节点
script板块
用于逻辑代码编写,其本质就是一个标准的es模块
style板块
本质就是css代码