Gadzan

Element UI form 的 label-width="auto" 所引发错误的分析与临时解决方法

错误 Error

使用 Element UI 时, 在 el-diag 里嵌入了 el-form 并设置了label-width="auto"属性, 发现一旦发生 tab 之间的跳转, 就会报错:

vue.runtime.esm.js?2b0e:1888 Error: [ElementForm]unpected width 
    at VueComponent.getLabelWidthIndex (element-ui.common.js?5c96:22872)
    at VueComponent.deregisterLabelWidth (element-ui.common.js?5c96:22885)
    at VueComponent.updateLabelWidth (element-ui.common.js?5c96:23084)
    at VueComponent.beforeDestroy (element-ui.common.js?5c96:23111)
    at invokeWithErrorHandling (vue.runtime.esm.js?2b0e:1854)
    at callHook (vue.runtime.esm.js?2b0e:4219)
    at VueComponent.Vue.$destroy (vue.runtime.esm.js?2b0e:3978)
    at destroy (vue.runtime.esm.js?2b0e:3159)
    at invokeDestroyHook (vue.runtime.esm.js?2b0e:6114)
    at invokeDestroyHook (vue.runtime.esm.js?2b0e:6119)

找了一下 Element UI 的 github 发现有同样的问题存在issues/17617. 目前还没有修复.

既然没有修复, 那么先分析看看能不能自己解决, 看调用栈, beforeDestroy -> updateLabelWidth -> deregisterLabelWidth -> getLabelWidthIndex

源码分析

在源码里搜一下getLabelWidthIndex方法, 找到 form 组件, 对比了一下调用栈, 好像不对.
再看看getLabelWidthIndex方法的上一个方法, 搜一下deregisterLabelWidth, 找到 label-wrap 组件, 再对比一下调用栈, 没错了.

来, 看看 Element UI 的 label-wrap 组件源码, 位置在\node_modules\element-ui\packages\form\src\label-wrap.vue.

找到getLabelWidth方法:

getLabelWidth() {
  if (this.$el && this.$el.firstElementChild) {
    const computedWidth = window.getComputedStyle(this.$el.firstElementChild).width;
    return Math.ceil(parseFloat(computedWidth));
  } else {
    return 0;
  }
},

模拟一下出错的操作, 关闭 el-diag 后,

window.getComputedStyle(document.querySelector('.el-form-item__label-wrap')).width
// "auto"

输出为auto, 那么使用parseFloat就会出现NaN的错误.

解决办法:

一. 用 v-if 取消渲染 form

这个方法比较简单, 因为 el-dialog 会有:visible.sync="show"属性, 只要在 el-form 里加上v-if="show"使之随 el-dialog 组件隐藏而删除, 显示而渲染即可.

既然在 el-dialog 关闭时已经没有 el-form, 那么要注意重置方法this.$refs.elForm.resetFields();也没有必要了, 删除或注释掉即可.

二. 用 patch-package 插件方法为源码打补丁

patch-package 使用文档

  1. 安装 patch-package:
npm i patch-package --save-dev
  1. 修改package.json,新增命令postinstall
"scripts": {
   "postinstall": "patch-package"
}
  1. 修改\node_modules\element-ui\packages\form\src\label-wrap.vue里面的代码

把返回修改成:return Math.ceil(parseFloat(computedWidth) || 0);

  1. 执行命令:
npx patch-package element-ui

第一次使用 patch-package 会在项目根目录生成 patches 文件夹并创建一个名为 package-name+version.patch 的文件(这里是 element-ui+2.13.2.patch), 里面有修改过的文件diff记录.

将该 patch 文件提交至版本控制中, 在后续运行npm install或是yarn install命令更新或安装依赖时, 它会在 npm 的生命周期 postinstall 自动为依赖包根据 patch 文件的 diff 打上我们编写的补丁.

这样就可以避免更新或者安装依赖时源码更新导致我们的修改被还原了.


打赏码

知识共享许可协议 本作品采用知识共享署名 4.0 国际许可协议进行许可。

评论