我在使用 Element-ui 的 el-popconfirm 气泡确认框插件时,使用了以下的代码:
<el-popconfirm
style="margin-right:10px;"
confirm-button-text='确认'
cancel-button-text='取消'
icon="el-icon-info"
icon-color="red"
title="该操作仅能执行一次且不可逆,建议冻结团队后操作。确认执行吗?"
@confirm="handleAddGitGroupMembers()"
>
<el-button v-if="gitGroup().groupId!=null" slot="reference" size="small">
添加项目组成员
</el-button>
</el-popconfirm>
这是一段真实的代码,我想通过 gitGroup().groupId 是否绑定来控制 button 的存亡,但却错把 v-if 放在了子组件中。我们从一个正常人的视角来看,大概都会知道,按理来说 v-if 应该放在嵌套在外层父组件 popconfirm 中,否则会导致一些问题。
这种问题表现出来就非常的玄学:当我更改代码后编译运行打开这个页面时,一切都很正常,点击 button 也能够正常显示气泡确认框。但运行一段时间后,再去点击 button,则很有可能无法显示气泡确认框了。
具体是什么原因呢?我们还是得从 Vue 组件的生命周期说起。
我们都知道,在生命周期中,挂载阶段会将源 html 代码生成真实的 DOM 节点,而后随着数据的更新,相应的组件也会重新编译渲染并再次挂载,因此实际上,挂载阶段是整个文件第一次且完整的编译挂载。问题就出在这,由于 v-if 的渲染是惰性的,因此,若 v-if 的初始值为false,那么在页面的挂载阶段,v-if 所修饰的组件不会被渲染执行。而与此同时,父组件 popconfirm 已经渲染完毕了,但它表示很困惑,因为根本找不到自己服务的 button。后来发生了某些数据更新,v-if 的值变为 true,子组件重新得以渲染挂载了,但此时已经为时已晚。子组件比父组件仅仅晚到了一步,从表面上看来,具象的 DOM 结构是没有任何异常的,但实际上,父组件 popconfirm 早已经丢失了子组件的指针,因此绑定失败了。
<!-- 好的代码 -->
<el-popconfirm
v-if="gitGroup().groupId!=null"
style="margin-right:10px;"
confirm-button-text='确认'
cancel-button-text='取消'
icon="el-icon-info"
icon-color="red"
title="该操作仅能执行一次且不可逆,建议冻结团队后操作。确认执行吗?"
@confirm="handleAddGitGroupMembers()"
>
<el-button slot="reference" size="small">
添加工蜂项目组成员
</el-button>
</el-popconfirm>
其实如果非要放在里面的话,使用 v-show 吧!这种非惰性的显隐控制,无论什么情况,子组件都会在挂载阶段渲染挂载,这样也就不会出现丢了儿子的情况了。
<!-- 使用 v-show 的代码 -->
<el-popconfirm
style="margin-right:10px;"
confirm-button-text='确认'
cancel-button-text='取消'
icon="el-icon-info"
icon-color="red"
title="该操作仅能执行一次且不可逆,建议冻结团队后操作。确认执行吗?"
@confirm="handleAddGitGroupMembers()"
>
<el-button v-show="gitGroup().groupId!=null" slot="reference" size="small">
添加项目组成员
</el-button>
</el-popconfirm>