Error in beforeDestroy hook: “Error: [ElementForm]unpected width “

[复制链接]
发表于 2025-5-24 19:26:50 | 显示全部楼层 |阅读模式
利用 element 的 form 时候报错:
vue.runtime.esm.js:3065  Error: [ElementForm]unpected width 
    at VueComponent.getLabelWidthIndex (element-ui.common.js:23268:1)
    at VueComponent.deregisterLabelWidth (element-ui.common.js:23281:1)
    at VueComponent.updateLabelWidth (element-ui.common.js:23483:1)
    at VueComponent.beforeDestroy (element-ui.common.js:23510:1)
原因:el-form-item 的 label 组件有个 beforeDestroy 生命同期,el-form 组件的 display: none 获取labelWidth 有问题导致的
办理办法:el-form 上加 v-if,用 el-dialog 的 :visible.sync="dialogVisible" 的话,也加 v-if="dialogVisible"
由于 el-form 标签 label-width 设为 "auto" ,而 el-form-item 会继续这个属性,在某种情况下页面销毁时获取这个 el-form-item 的 label 的 width 为 'auto',然后用 parseFloat 转换后为 NaN 就报错了。
这个方法是在 element-ui 共通方法里写的 node_modules\element-ui\lib\element-ui.common.js


或是


computedWidth 为 “auto" 或 "" 都会返回 NaN
网上说是由于代码中利用了 v-show 以及 el-form 标签中利用了label-width="auto",导致离开页面后产生报错
办理方法一:label-width 换成固定值
办理方法二: v-show 换成  v-if
但我的原因是由于利用了 keep-alive 用下面代码可以再现出来
src\router\index.js
  1. import Vue from 'vue';
  2. import Router from 'vue-router';
  3. import Home from '../views/Home.vue';
  4. import Layout from '../views/management/Layout.vue';
  5. Vue.use(Router);
  6. const router = new Router({
  7.   mode: 'history',
  8.   routes: [
  9.     {
  10.       path: '/',
  11.       name: 'home',
  12.       component: Home
  13.     },
  14.     {
  15.       path: '/management',
  16.       component: Layout,
  17.       children: [
  18.         {
  19.           path: '/management/audit',
  20.           component: () => import("@/views/management/audit/index.vue"),
  21.           name: "memberAudit",
  22.         },
  23.         {
  24.           path: '/management/detail',
  25.           component: () => import("@/views/management/detail/index.vue"),
  26.           name: "memberAudit",
  27.         }
  28.       ]
  29.     }
  30.   ]
  31. });
  32. export default router;
复制代码
src\views\management\Layout.vue
  1. <template>
  2.   <div class="main">
  3.     <el-scrollbar>
  4.       <div class="left">
  5.         <el-menu
  6.           default-active="$route.path"
  7.           background-color="#545c64"
  8.           text-color="#fff"
  9.           router
  10.         >
  11.           <el-menu-item :router="true" index="/management/audit">
  12.             <i class="el-icon-menu"></i>
  13.             <span slot="title">审核</span>
  14.           </el-menu-item>
  15.           <el-menu-item :router="true" index="/management/detail?label=menu">
  16.             <i class="el-icon-menu"></i>
  17.             <span slot="title">详情</span>
  18.           </el-menu-item>
  19.           <el-menu-item :router="true" index="/management/about">
  20.             <i class="el-icon-menu"></i>
  21.             <span slot="title">About</span>
  22.           </el-menu-item>
  23.         </el-menu>
  24.       </div>
  25.       <div class="right">
  26.         <keep-alive>
  27.           <router-view />
  28.         </keep-alive>
  29.       </div>
  30.     </el-scrollbar>
  31.   </div>
  32. </template>
  33. <style lang="scss">
  34. .left {
  35.   float: left;
  36.   width: 200px;
  37. }
  38. .right {
  39.   float: left;
  40.   width: 80%;
  41. }
  42. </style>
复制代码
src\views\management\audit\index.vue
  1. <template>
  2.   <div class="hello1" style="margin-top: 100px">
  3.     audit
  4.     <span @click="detail">click</span>
  5.   </div>
  6. </template>
  7. <script>
  8. export default {
  9.   methods: {
  10.     detail() {
  11.       this.$router.push("/management/detail?label=测试");
  12.     },
  13.   },
  14. };
  15. </script>
复制代码
src\views\management\detail\index.vue
  1. <template>
  2.   <div>
  3.     <el-form
  4.       :model="ruleForm"
  5.       status-icon
  6.       ref="ruleForm"
  7.       label-width="auto"
  8.       class="demo-ruleForm"
  9.     >
  10.       <el-form-item label="年龄" prop="age">
  11.         <el-input v-model.number="ruleForm.age"></el-input>
  12.       </el-form-item>
  13.       <el-form-item :label="$route.query.label" prop="name">
  14.         <el-input v-model.number="ruleForm.name"></el-input>
  15.       </el-form-item>
  16.       <el-form-item>
  17.         <el-button @click="resetForm()">重置</el-button>
  18.       </el-form-item>
  19.     </el-form>
  20.   </div>
  21. </template>
  22. <script>
  23. export default {
  24.   name: "Home",
  25.   components: {},
  26.   data() {
  27.     return {
  28.       ruleForm: {
  29.         age: "",
  30.         name: "",
  31.       },
  32.     };
  33.   },
  34.   methods: {
  35.     resetForm() {
  36.       this.$router.push("/management/audit");
  37.     },
  38.   },
  39. };
  40. </script>
复制代码

点击详情

点击重置

再点击 about

报错了,这里留意报错的主要写法
1、src\views\management\Layout.vue
利用 keep-alive
2、src\router\index.js
这里我没设置 about 页面路由,可能如许会触发 VueComponent.beforeDestroy
3、src\views\management\detail\index.vue
el-form-item 必须两个以上,而且其中一个的 label 利用动态值 :label="$route.query.label"
我的这种情况即使设置 label-width 为固定值也不行,由于最后页面消失,获取  label-width 为空
parseFloat(label-width) 为 NaN 就报错了
去掉 keep-alive 就不会报错

去掉后就不会走 'update' 分支,由于页面切换时由于有个 label 是动态赋值的,以是即使页面销毁由于是 keep-alive 且 label 动态值变没了,导致 el-form 以为是 label 值 update ,走的这个方法,而这个时候页面找不到元素, label-width 就是空,以是报错了。
着实是由于 el-form-item 的 label 设为响应式的了,而且在页面销毁时响应式值还更新,可以换一种响应式使它在页面销毁时不更新值
<el-form-item :label="qLabel" prop="name">
  created() {
    this.qLabel = this.$route.query.label;
  },
  1. <template>
  2.   <div>
  3.     <el-form
  4.       :model="ruleForm"
  5.       status-icon
  6.       ref="ruleForm"
  7.       label-width="auto"
  8.       class="demo-ruleForm"
  9.     >
  10.       <el-form-item label="年龄" prop="age">
  11.         <el-input v-model.number="ruleForm.age"></el-input>
  12.       </el-form-item>
  13.       <el-form-item :label="qLabel" prop="name">
  14.         <el-input v-model.number="ruleForm.name"></el-input>
  15.       </el-form-item>
  16.       <el-form-item>
  17.         <el-button @click="resetForm()">重置</el-button>
  18.       </el-form-item>
  19.     </el-form>
  20.   </div>
  21. </template>
  22. <script>
  23. export default {
  24.   name: "Home",
  25.   components: {},
  26.   created() {
  27.     this.qLabel = this.$route.query.label;
  28.   },
  29.   data() {
  30.     return {
  31.       qLabel: "",
  32.       ruleForm: {
  33.         age: "",
  34.         name: "",
  35.       },
  36.     };
  37.   },
  38.   methods: {
  39.     resetForm() {
  40.       this.$router.push("/management/audit");
  41.     },
  42.   },
  43. };
  44. </script>
复制代码
这么写不会报错,算是完美避开了这个 bug
但是公司的是另一个问题,是用 el-dialog 组件包裹 el-form 也出这个 bug了
  1. <el-dialog
  2.       title="提示"
  3.       :visible.sync="dialogVisible"
  4.       width="30%"
  5.       :before-close="handleClose"
  6.       :destroy-on-close="true"
  7.     >
  8.       <el-form
  9.         :model="ruleForm"
  10.         status-icon
  11.         ref="ruleForm"
  12.         label-width="auto"
  13.         class="demo-ruleForm"
  14.       >
  15.         <el-form-item :span="24" label="年龄" prop="age">
  16.           <el-input v-model.number="ruleForm.age"></el-input>
  17.         </el-form-item>
  18.         <el-form-item>
  19.           <el-button @click="resetForm()">重置</el-button>
  20.         </el-form-item>
  21.       </el-form>
  22.     </el-dialog>
  23.     resetForm() {
  24.       this.dialogVisible = false;
  25.       this.$router.push("/management/detail");
  26.     },
复制代码
查看源码仔细分析发现,虽然报错在 element-ui.common.js 但着实报错是在源码里 label-wrap.vue
 

  1. <script>
  2. export default {
  3.   props: {
  4.     isAutoWidth: Boolean,
  5.     updateAll: Boolean
  6.   },
  7.   inject: ['elForm', 'elFormItem'],
  8.   render() {
  9.     const slots = this.$slots.default;
  10.     if (!slots) return null;
  11.     if (this.isAutoWidth) {
  12.       const autoLabelWidth = this.elForm.autoLabelWidth;
  13.       const style = {};
  14.       if (autoLabelWidth && autoLabelWidth !== 'auto') {
  15.         const marginLeft = parseInt(autoLabelWidth, 10) - this.computedWidth;
  16.         if (marginLeft) {
  17.           style.marginLeft = marginLeft + 'px';
  18.         }
  19.       }
  20.       return (<div class="el-form-item__label-wrap" style={style}>
  21.         { slots }
  22.       </div>);
  23.     } else {
  24.       return slots[0];
  25.     }
  26.   },
  27.   methods: {
  28.     getLabelWidth() {
  29.       if (this.$el && this.$el.firstElementChild) {
  30.         const computedWidth = window.getComputedStyle(this.$el.firstElementChild).width;
  31.         return Math.ceil(parseFloat(computedWidth));
  32.       } else {
  33.         return 0;
  34.       }
  35.     },
  36.     updateLabelWidth(action = 'update') {
  37.       if (this.$slots.default && this.isAutoWidth && this.$el.firstElementChild) {
  38.         if (action === 'update') {
  39.           this.computedWidth = this.getLabelWidth();
  40.         } else if (action === 'remove') {
  41.           this.elForm.deregisterLabelWidth(this.computedWidth);
  42.         }
  43.       }
  44.     }
  45.   },
  46.   watch: {
  47.     computedWidth(val, oldVal) {
  48.       if (this.updateAll) {
  49.         this.elForm.registerLabelWidth(val, oldVal);
  50.         this.elFormItem.updateComputedLabelWidth(val);
  51.       }
  52.     }
  53.   },
  54.   data() {
  55.     return {
  56.       computedWidth: 0
  57.     };
  58.   },
  59.   mounted() {
  60.     this.updateLabelWidth('update');
  61.   },
  62.   updated() {
  63.     this.updateLabelWidth('update');
  64.   },
  65.   beforeDestroy() {
  66.     this.updateLabelWidth('remove');
  67.   }
  68. };
  69. </script>
复制代码
由于 form-item 组件用了 label 组件,而 label 组件是用上面的 label-wrap 包裹的,以是 el-dialog 关闭并跳转到新页面时就调用了 label-wrap beforeDestroy生命周期函数,这个时候由于 el-dialog 是用的 v-show 以是 display 为 none,这个时候获取 label 的 width 
if (this.$el && this.$el.firstElementChild) 这个判定条件是获取 label 标签由于用了 <keep-alive>,以是照旧能找到的,但是window.getComputedStyle(this.$el.firstElementChild).width 获取的值就是 空大概 ’auto'
办理办法 v-show 改为 v-if 就是让 this.$el.firstElementChild 根本查询不到 label 标签,就不会走这个条件分支了,就不会报错了,对功能也没影响。
以是可以在 el-dialog 上加 v-if 和 visible 用一个变量即可,或是在 el-form 上加,或是 el-form-item上加 v-if 但是这种情况得所有 el-form-item 都加上
测试办理了

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

© 2001-2025 Discuz! Team. Powered by Discuz! X3.5

GMT+8, 2025-7-10 02:45 , Processed in 0.085582 second(s), 30 queries 手机版|qidao123.com技术社区-IT企服评测▪应用市场 ( 浙ICP备20004199 )|网站地图

快速回复 返回顶部 返回列表