给三方组件包“打补丁” (解决el-form 设置label-width="auto"时,组件销毁时控制台报错的问题)

138 阅读2分钟

!24 背景!

我们在使用第三方依赖包时如果遇到了bug,通常解决的方式都是绕过这个问题,使用其他方式解决,较为麻烦。或者给作者提个issue,然后等待作者的修复,等待的时间不可控。

!24 解决方案:“打补丁” - patch-package!

我们可以使用patch-package(patch-package - npm)给我们的三方包进行打补丁 大致步骤如下: 1.在node_modules中找到我们要修复问题或者要新增特性的三方包的文件夹(如:node_modules\element-ui)

2.修改文件夹下有问题的代码的文件中的代码,或者新增新特性

3.测试代码符合预期

4.安装patch-package包(npm i patch-package)

5.运行:npx patch-package "文件夹的名字" (如:element-ui)

6.在package.json中添加scripts脚本:"postinstall": "patch-package"

7.把代码提交到仓库

!24 案例!

解决el-form 设置label-width="auto"时,组件销毁时控制台报错的问题

最近在使用vue2+elementUI 2.15的前端框架时开发项目时,时不时总会发现控制台会报如下图的错误

image.png 最终排查下来是 el-form 设置了 label-width="auto" 然后又处于不展示的情况下(display: none,项目里是 el-tab产生的)在离开页面的时候就会有问题。

el-form 在挂载时(display:none也会挂载),会初始化一个 el-label 的 width 的数组,在销毁的时候时候会依次清除 该数组 。问题原因在此获取宽度函数上

// https://github.com/ElemeFE/element/blob/dev/packages/form/src/label-wrap.vue#L32
methods: {
    getLabelWidth: function getLabelWidth() {
      if (this.$el && this.$el.firstElementChild) {
       var computedWidth = window.getComputedStyle(this.$el.firstElementChild).width;
       // return computedWidth == 'auto' ? 'auto' : Math.ceil(parseFloat(computedWidth));
        return Math.ceil(parseFloat(computedWidth));
      } else {
        return 0;
      }
    },
    //...
}

在注册的时候取宽度(getLabelWidth)会拿到 NaN,因为 getComputedStyle 在元素未实际渲染时(父元素display:none)会取到 auto, parseFloat('auto') 就会NaN,不会被注册到 数组 potentialLabelWidthArr

registerLabelWidth(val, oldVal) {
  if (val && oldVal) {
     const index = this.getLabelWidthIndex(oldVal);
     this.potentialLabelWidthArr.splice(index, 1, val);
   } else if (val) {
     this.potentialLabelWidthArr.push(val);
   }
}

在销毁的时候自然取不到 就抛错了 [ElementForm]unpected width

// https://github.com/ElemeFE/element/blob/dev/packages/form/src/form.vue#L160
getLabelWidthIndex(width) {
  const index = this.potentialLabelWidthArr.indexOf(width);
    // it's impossible
    if (index === -1) {
    throw new Error('[ElementForm]unpected width ', width);
  }
  return index;
},

原因找到了,更改也比较简单, getLabelWidth 函数中 针对auto做个处理

// lib/element-ui.common.js
getLabelWidth: function getLabelWidth() {
   if (this.$el && this.$el.firstElementChild) {
     var computedWidth = window.getComputedStyle(this.$el.firstElementChild).width;
     return computedWidth == 'auto' ? 'auto' : Math.ceil(parseFloat(computedWidth));
     // return Math.ceil(parseFloat(computedWidth));
   } else {
     return 0;
   }
 },

我们在node_modules中找到报错的代码并修改,运行测试,报错解决了

但是我们的代码是在node_modules中修改的只会在本地才能生效,要让它在线上的环境也能正确执行,我们就得打补丁了 首先我们安装下patch-package这个包

npm i patch-package

然后确保我们代码修改完成之后

运行npx patch-package element-ui

image.png

这样我们就会看到项目的根目录下出现一个patchs的文件夹了 ,这个文件夹就是存放我们补丁的位置了

image.png

接下来就是在package.json文件中添加"postinstall": "patch-package"的脚本,这个命令会在npm install之后自动执行patch-package的命令

image.png

这样在项目上线打包的时候在执行npm install的命令的时候最新的补丁代码就会出现在包中了

image.png

image.png

最后我们把代码提交到远程仓库就可以共享本次补丁修改了