在 Composition API 里面返回动态构造的组件,是邪道做法吗?

2022-03-16 11:26:59 +08:00
 shintendo

标题可能说的不清楚,比如以下写法:

// hooks.js
function useList (apiUrl) {
  const loading = ref(false)
  const list = ref([])
  const getData = () => {
    loading.value = true
  	http.get(apiUrl).then(data => {
      list.value = data
      loading.value = false
    })
  }
  const MyList = {
    render () {
      return <a-list data-source={list.value} loading={loading.value} size="small" />
    }
  }
  const ReloadBtn = {
    render () {
      return <a-button type="primary" size="small" onClick={getData} disabled={loading.value}>刷新</a-button>
    }
  }
}
// App.vue
<template>
  <div>
    <Component :is="ReloadBtn" />
  </div>
  <Component :is="MyList" />
</template>
 
<script>
  import { useList } from 'hooks.js'
  export default {
    setup () {
      const { MyList, ReloadBtn } = useList('http://xxxxxx/xxx')
      return { MyList, ReloadBtn }
    }
  }
</script>
// or App.vue with script setup
<template>
  <div>
    <ReloadBtn />
  </div>
  <MyList />
</template>
 
<script setup>
  import { useList } from 'hooks.js'
  const { MyList, ReloadBtn } = useList('http://xxxxxx/xxx')
</script>

类似这样(把 UI 封装在 hook 函数里)的实践,算是邪道么?因为我似乎 Google 不到任何相关的讨论,而在 VueUse 这类的库中,我也看不到任何这样做的例子,所以有点好奇。这样做有什么重大的缺陷吗?

2201 次点击
所在节点    Vue.js
16 条回复
SongGG3
2022-03-16 11:48:45 +08:00
没毛病。
SongGG3
2022-03-16 11:50:36 +08:00
你甚至可以这样(编译会稍微变慢)
Component :is="MyList"
const MyList = () => import('./xxx.vue')
ccyu220
2022-03-16 11:53:53 +08:00
这并不是你说的「动态构造组件」 ,只是把 JSX 中的 if 判断代码抽离出来放在了 hooks 中。

从 setup return 中移到了 hook 中你就不认识它了?
equt
2022-03-16 11:54:39 +08:00
不是邪道,Vue Use 确实没有,但是 Vue Use 的 @vueuse/components 就像是另一面 —— 把 Hook 做成了 Abstract Component 。

不过到了后面,你可能会发现 Vue 没有范型组建这个概念,毕竟 TS 没 HKT (现在有个 RFC 不过是用 Class 做的) XD
agdhole
2022-03-16 12:09:00 +08:00
jsx 了属于是
ruoxie
2022-03-16 12:13:10 +08:00
很正常,我这是我喜欢用 jsx,不喜欢 template 的地方
shintendo
2022-03-16 13:18:49 +08:00
@SongGG3 这个就是普通的动态组件吧
shintendo
2022-03-16 13:24:32 +08:00
@ccyu220 重点不是在 hooks 还是在 setup 里,我提问的本意就是在 setup 里返回一个动态构造的组件的做法。因为我没有见过别人这么写的(不管是写在 setup 里还是单独 js 文件里),故有此一问。
我见到的“动态组件”的应用似乎都是指 import 好几个 vue 文件,然后根据条件选择其中一个的这种“动态”
ccyu220
2022-03-16 13:38:02 +08:00
@shintendo 这种做法在 「重载业务组件」 是可行的
ccyu220
2022-03-16 13:39:10 +08:00
@shintendo 只不过写法稍微不是那么优雅,JSX 、Template 、文件混用
fason1995
2022-03-16 13:47:44 +08:00
这个应该有点类似是动态 mixins 吧
yaphets666
2022-03-16 13:55:22 +08:00
vue 之所以在国内大流行的原因就是大家可以轻松的保持一致的写法,写 vue 最好保持这个优良传统。
summerLast
2022-03-16 15:06:57 +08:00
不是邪教做法,没有什么缺陷,只是会增加逻辑的层数,抽象并非越多越好,只要是方便理解就可; vue 的单组件本质就是一个生成代码的模板,Composition API 之前 好像是 vue2.6 之前,复用手端有组合 、混合但粒度比较粗,最细的粒度就是函数了,这也是 Composition API 出来的原因,至于 Composition API 是只有 js 还是 html css 都包含,我个人站前者,原因是后者那样其实又回到了组件了 逃 (但是只要利于理解也没关系,但是会混乱,越简单越好);所以 Composition API 也好,混合也好,继承也好,代码写出来是给人看的,只要容易理解易于维护就好。个人认为 Composition API 层数最好要控制在 7 层以内,最好是 3-4 层 往下。
shintendo
2022-03-16 17:14:17 +08:00
@summerLast 感谢,关于抽象程度、可读性 /可维护性、代码冗余 /DRY 之间,应该怎么把握平衡呢。比如对于页面高度同质化的后台管理系统,抽象得少了会觉得一大半代码是冗余的(其实也不好维护),抽象过头又会变成只有自己看得懂的 DSL ,这中间是否有某种最佳实践?有没有相关的文章讨论、样板项目可以看看的?
summerLast
2022-03-17 09:53:28 +08:00
@shintendo : 文章或项目还真没有留意到,个人的看法是控制好抽象的层数和命名,关于为什么是 7 层以内,最好是 3-4 层 往下,之前看过一篇刘未鹏老师的文章,讲到了一个工作记忆(其实我们意识的窗口很小,我们的工作记忆只能容纳寥寥数个条目),
```
人在思考一个问题的时候,就像是在黑暗中打着电筒往前走(事实上,我们的工作记忆资源是有限的,有研究证明我们只能在工作记忆里面持有 7 加减 2 个项目;此外认知负荷也是有极限的),每一步推导都将我们往前挪一小步,然而电筒的光亮能照到的范围是有限的,我们走了几步发现后面又黑了,想到后面就忘了前面的,想到某个分支上去就忘了另一个分支,我们常常想着想着就想岔了,想岔了也就罢了,问题是一旦想岔了太远,就很难回到当初岔开的地方了
```
还有另一点是用好聚合和管理好代码的正交性,前者聚合是一种代码关联性比较强的关系可以放在一个包下,名字如 Todo ,TodoItetm 放在 todo 包下然后放在业务模块包下,这里 TodoItem 的创建依赖 父节点 Todo 是否创建;后者正交性则是我们理论上能达到代码复用性的最好状态,不依赖具体的父节点,如 Input button 这种 ,也就是一个函数具体实现的改动对另一个函数造成的影响要尽可能的小最好是无,而强依赖性显然会增大影响,写代码没有完全正确的模式,写的过程中慢慢找到“对就是这个感觉”,祝渐入佳境。
shintendo
2022-03-17 10:58:21 +08:00
@summerLast 感谢

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/840706

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX