问题记录-前端开发避坑(6)-- el-tree 开发相关

问题1

问题描述

创建一个外部函数,在基础组件中混入,并在业务组件中导入。产生以下报错(每种错误信息都有N条):

[Vue warn]: Error in render: "TypeError: Cannot read properties of undefined (reading 'props')".
[Vue warn]: You may have an infinite update loop in a component render function.

问题分析

混入语法写错,正确语法是将需要混入的函数,作为“混入对象” methods 属性的一个方法,而不是直接作为函数导入到组件中。如下所示:

// 定义外部函数
export function yourFunc() { ... }

export cosnt yourMixins = {
  methods: { yourFunc },
}
// 混入到基础组件;正确写法
import { yourMixins } from '***'
...
  mixins: [yourmixins],
...
// 错误写法 
// import { yourFunc } from '***'
// ...
//   mixins: [yourFunc ],
// ...

// 导入到业务组件
import { yourFunc } from '***'

问题2

问题描述

我注意到很多 element 的组件都提供了传参数不带值的方式,但之前一直没想到可以这么优化组件。这次想起来就搜了一下,发现这是 Vue 自带的功能。

Vue 官方文档中有说明,当自定义的 prop 为布尔类型时,只要在组件上提供了该 prop,即使没有提供值,也会视作 true 。这个机制可以帮助减少传参时输入的字符,对于这类配置参数而言,只要传入属性就视为传入了 :yourProp="true"

严格来说,这一点不算问题,而是因为没有注意到文档中的一些细节,导致没有想到优化的方式,即使没有也不会影响运行。

问题3

问题描述及分析

为了给 el-tree 增加编辑功能,修改节点文本,尝试向 data 对象添加状态属性,并通过插槽获取每个节点的状态数据,在节点变为编辑状态时显示输入框。

一开始发现插槽的内容无法响应式更新,查了文档也确实是这么写的。在此基础上:

  1. 尝试直接修改传入的 node.data.status.editing 为 true 无效
  2. 尝试过强制更新组件 this.$forceUpdate(),也是无效。
  3. 之后,尝试将绑定到 el-tree 的 data 数组同时作为参数传入,并且通过递归判断 node 对象和 data 数组中元素的 label 值,确定是哪个节点需要修改。但还是存问题。

因此尝试分解过程:发现直接一开始就设置为编辑状态也获取不到数据,然后检查发现是参数写错了,node 对象和 data 对象没分辨清楚,导致方法没有获取到数据,修改之后插槽就能正确获取修改后的状态数据了。

但方法3明显过于复杂。又尝试优化:
4. API 中 \(set 方法支持响应式修改对象属性和数组的子元素,优化为 `this.\)set(node.data.status, 'editing', true)`
5. 之后再次尝试直接修改 node.data.status.editing,经过验证可以反馈到树的节点上。所以其实关键的问题还是一开始写错了参数

因此,最终还是使用方法5直接修改状态数据。后续如果遇到类似的无法响应式更新的情况,且方法5失效了,可以再尝试其他方法。

初始状态

编辑状态

此外,由于按钮是添加到节点组件内部,每次点击按钮都会同时触发节点的 click 事件,因此需要给所有按钮添加 .stop 修饰符,这样就能在保留单击节点时展开子节点,同时又保证单击按钮不会触发展开或收缩。同时,输入框需要给 click 事件添加 .native.stop 修饰符,只有 .stop 修饰符时实测是无效的。

感悟

目前不太确定对于插槽非响应式是否理解正确,因为按照我上面的方法,插槽内容其实是随着 node 的值而改变了,因此已经可以算是支持响应式更新。个人理解是插槽依赖的数据源如果是静态数据,比如某个直接绑定在组件或插槽上的对象/数组字面量,那么插槽数据也是非响应式;而如果插槽依赖的数据源是 props 或 data 等响应式数据,则插槽内容的变化可以被观测到。

另外,根据控制台反馈信息,el-tree 的 node 对象并不是注册在 vue 上的观测对象,观测对象通常是这样的 ▶[{…}, ob: Observer]。而 node 对象是 Node 类型,且与一般的观测对象不同,因此不太确定是否属于观测对象,但按照上面验证的结果来看,应该也算是观测对象的一种;从下图的最下面一个内部属性或许可以得到验证。

问题4

问题描述及分析

基于问题3的需求,向 el-tree 的节点添加编辑功能,开启编辑状态时,显示输入框。但 el-input 的高度无法更改,尝试了一些方法,包括:

  1. 直接修改 input.el-input__inner 的高度,
  2. 增加嵌套层级增加权重
  3. 设置为 !important
  4. 使用深度选择器 >>> (别名 ::v-deep或者/deep/),使用时要注意,一定要在最外层一个子组件的选择器之外,比如下面 .custom-tree-node 是当前组件中存在的类,而 input.el-input__inner 是 el-input 内置的类,因此需要在 input.el-input__inner 外层使用 /deep/ ,否则样式还是会失效。
.custom-tree-node {
    /deep/ & input.el-input__inner {
      height: 28px;
  }
}

后来发现还有一种更简单的方法,可以设置 size="mini",此时输入框高度正好是 28px,不会超出节点的行高。

问题5

问题描述及分析

文字按钮过于冗长,设置为圆形图标的按钮,此时仍然是每个节点都展示按钮,会导致按钮的上下边缘存在重叠。因此尝试设置 :hover 伪类,控制按钮组的显示与隐藏,但是实际表现很差,离开的节点的按钮没有立即消失,进入新的节点后还有很短的时间内继续显示。

因此尝试通过鼠标事件 onmouseleave 和 onmouseenter 修改状态中的 hover 。效果好很多,离开和进入节点时,按钮的显示和隐藏没有明显延迟或停留。

同上问题4,设置成圆形按钮后,也会出现与组件高度相关的问题:当前 hover 的节点为第一个或最后一个子节点时(相对于某一层而言),按钮的上面或下面会被遮挡一部分:

通过检查元素发现,是中间的某个元素设置了 overflow: hidden 属性,因此可以设置全局属性,将该属性修改为 visible 。或者可以缩小圆形按钮的直径(宽和高)。

问题6

问题描述及分析

当点击编辑按钮时,自动聚焦当前节点的输入框。由于使用了 v-if 指令动态生成输入框,通过 this.\(refs 直接获取时组件可能尚未创建完毕,因此获取到 undefined。网上有说将 v-if 改成 v-show,但实测并不能解决我的问题。因此尝试通过设置定时器,循环查询 this.\)refs 中是否已包含指定的 input 组件,并在判定为真时调用 focus()。经验证确实该方案确实可以。

问题7

问题描述及分析

给编辑状态下的输入框添加 enter 事件回调,设置完 .enter 修饰符还是无效。这是因为我使用了自定义组件,此时需要额外绑定 .native 修饰符,也就是 .enter.native 才能使 el-input 监听 enter 按键。

问题8

问题描述及分析

将右键菜单升级为全局组件,封装好相关变量,当执行特定操作时,执行 vuex 的 actions,并将当前组件的菜单数据和位置信息传递过去,由于一开始使用了多参数,导致第二个参数一直是 undefined。这是因为 dispacth 方法的只有两个参数,而第二个参数是支持对象类型的,因此需要将多个参数作为对象的属性。commit 方法同理。

问题9(未解决)

问题描述

没找到原因,突然出现以下报错:

backend.js:748 Error: Missing module "" for path "[vdt]".

有人分析是开启了 vuex 的命名空间时有几率报错。我重启服务后本来没有消失,但过了一会刷新页面又不见了。

问题10

问题描述

由于 vuex 很多方法都是通过硬编码的命名空间名字符串、方法名字符串作为参数调用的,使用起来不是很方便,因此尝试将各命名空间包装成全局属性,然后在各业务组件中调用。

经过验证,commit/dispatch 方法是允许传入变量或表达式的。但 mapState 方法并不支持,使用实例变量(通过vue组件声明)传递时会导致 this 指向错误(原理未知,可能是 mapState 的执行比此处的 this 赋值更早,导致此时的 this 还没值就被传入方法,也可能是其他原因),可以通过定义一个局部变量或常量作为替代,避免 this 问题。

补充

可以直接通过 vuex 提供的 mapState/mapGetters/mapMutations/mapActions 批量导入到组件中,然后调用本地属性或方法,这样至少每个组件只需要写一遍相应的命名空间、属性、方法。

问题11(未解决)

问题描述

原先给 el-tree 添加右键菜单后,当点击空白位置时,需要关闭菜单,但此时有明显延迟。原因未知。

后续经过调整,将右键菜单升级为全局组件,所以将控制菜单显示/隐藏的变量放到了 store 中,这时候再控制该变量为 false,菜单很快就隐藏了。

问题12

问题描述及分析

给树节点添加额外的 click 监听器,使得打开右键菜单后,点击节点时也能正确关闭菜单。一开始直接给 el-tree 中 template 内的顶层元素添加监听器,可以满足需求。后续尝试将监听器移动到 el-tree 组件上就失效了,原因是节点自身绑定的 click 事件在内部被阻止了冒泡(已验证过源码),因此点击节点时,树组件无法捕获事件。所以类似这种场景下,必须给对应的在内部阻止了冒泡的组件或其子元素,添加额外的事件监听器。

posted @ 2022-07-12 23:49  CJc_3103  阅读(1769)  评论(0)    收藏  举报