为什么我感觉结构清晰了 但是逻辑变复杂了

来源:4-11 useClickOutside 第一个自定义函数

慕莱坞0998854

2020-10-09

老师,这节的逻辑比较简单,总结起来就是:
1> 用isOpen这个变量控制dropdown-item的显示与否
2> 当点击到页面中的其他部分,隐藏dropdown-item

前一节的代码时这样的:

	const dropdownRef = ref<null | HTMLElement>(null);

    const handler = (e: MouseEvent) => {
      if (dropdownRef.value) {
        if (
          !dropdownRef.value.contains(e.target as HTMLElement) &&
          isOpen.value
        ) {
          isOpen.value = false;
        }
      }
    };

    onMounted(() => {
      document.addEventListener("click", handler);
    });
    onUnmounted(() => {
      document.removeEventListener("click", handler);
    });

请注意,这里只有isOpen这一个变量。dropdown-item的显示与否只与isOpen相关,缺点也显而易见,setup这个函数容易变得很长很长。

老师把点击外面的区域这段代码抽象出来,这节的代码变成了:

// useClickOutside.ts
   const  useClickOutside = (
     elementRef: Ref<null | HTMLElement>
   ): Ref<boolean> => {
     const isClickOutside = ref(false);

     const handler = (e: MouseEvent) => {
      if (elementRef.value) {
        if (elementRef.value.contains(e.target as HTMLElement)) {
          isClickOutside.value = false;
          // 下面的else我在自己写的时候就没有注意要重置为true
        } else {
          isClickOutside.value = true;
        }
      }
   };

  onMounted(() => {
    document.body.addEventListener("click", handler);
  });

  onUnmounted(() => {
    document.body.removeEventListener("click", handler);
  });

  return isClickOutside;
};

//  Dropdown.vue

    const dropdownRef = ref<null | HTMLElement>(null);

    const isClickOutside = useClickOutside(dropdownRef);

    watch(isClickOutside, () => {
      if (isClickOutside.value && isOpen.value) {
        isOpen.value = false;
      }
    });

改写之后在Dropdown.vue中,代码少了一些。但是在这里我感到疑惑, 现在是两个变量了,其中isClickOutside可以通过改变isOpen的值的方式来间接影响dropdown-item的显示与否,就好像以前有只有一个变量A(true 或者 false),现在则是A(true 和 false)和B(同样也是true和false)两个变量,那么排列组合一下,就有四种情况了。

以第一次点击那个切换显示与否的button为例,我照着本节的代码捋捋流程就是这样的:
1 > 执行toggleOpen函数 isOpen由false变成true
2 > 点击的这个button是属于DropDown组件的,所以isClickOutside的值保持false不变
3 > 因为isClickOutside没有发生改变,所以watch里面的回调函数不会执行
4 > 由于isOpen值为true 故显示dropdown-item

但是如果按照上一节的代码来捋流程的话 似乎就简单了很多
1> 执行toggleOpen函数 isOpen由false变成true
2> 点击的这个button是属于DropDown组件的,不满足handler里面的if判断 什么也不做
3> 由于isOpen值为true 故显示dropdown-item

这只是一个例子 假如再想搞清楚"点击外部区域"这个情况 那么在脑中过一遍流程的话, 还是上一节的代码简单

按照本节的代码 我们需要关注isOpen和isClickOutside两个变量,我感觉多维护一个变量的话,总是容易想不清楚。

写回答

1回答

张轩

2020-10-09

同学你好 谢谢你这么长的文字 你思考的很认真 也带给我很多启示 我认为 代码抽象 很有可能代码复杂度的上升和更高的理解难度 其实 isClickOutside 这个抽象的函数,我们需要抛开 isOpen 来看,因为它完成的是一个单独的功能,有了这个值,我们可以满足更丰富的需求,带来更广阔的场景,比如说另外一个场景:点击某元素外面取消一个元素的选中(常见于图形编辑器中),点击Modal 的其他区域来关闭一个 Modal 等等。所以抽象是为了更好的服务更多的场景,复杂度上升有时候是没法避免的。

2
0

Vue3 + TS 仿知乎专栏企业级项目

带你完成前后端分离复杂项目,率先掌握 vue3 造轮子技能

3142 学习 · 2313 问题

查看课程