Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

所有组件的 render 函数都删掉吧 #3363

Closed
yetrun opened this issue Jul 27, 2022 · 18 comments
Closed

所有组件的 render 函数都删掉吧 #3363

yetrun opened this issue Jul 27, 2022 · 18 comments
Labels
feature request New feature or request

Comments

@yetrun
Copy link

yetrun commented Jul 27, 2022

这个功能解决的问题

非常难用

<template>
  <n-menu :options="options" />
</template>

<script setup>
const options = ref([
  { label: '跳转到路由', key: 'gotoRoute' },
  { label: '打开一个弹出框', key: 'openDialog' },
  { label: '选择一个文件', key: 'openFileDialog' }
])
const renderLabel = (option) => {
  switch(option.key) {
    case 'gotoRoute': return h(
      // 不知道怎么写了
    )
    case 'openDialog': return h(
      // 不知道怎么写了
    )
    case 'openFileDialog': return h(
       // 不知道怎么写了
    )
  }
}
</script>

期望的 API

建议用 slot:

<template>
  <n-menu>
    <n-menu-option @click="$router.push('/about')">跳转到路由</n-menu-option>
    <n-menu-option @click="Dialog.open(...)">打开一个弹出框</n-menu-option>
    <n-menu-option @click="FileChooser.open(...)">选择一个文件</n-menu-option>
  </n-menu>
</template>
@github-actions github-actions bot added the feature request New feature or request label Jul 27, 2022
@XieZongChen
Copy link
Collaborator

#106

@yetrun
Copy link
Author

yetrun commented Jul 30, 2022

#106

这个问题提到的是动态 slot name,但我这里的例子 slot name 是 "default",是静态的啊。

并不清楚你要表达的是动态的 slot name 的 TypeScript 支持不够好,还是所有的 slot 对 TypeScript 的支持都不够好。可以给个代码示例描述一下吗?

@07akioni
Copy link
Collaborator

从 Slot 里面吐出来的 props 里面行数据的类型只能是 any 或者 unknown。

根本原因是 Vue 不支持泛型组件。

@yetrun
Copy link
Author

yetrun commented Aug 8, 2022

从 Slot 里面吐出来的 props 里面行数据的类型只能是 any 或者 unknown。

根本原因是 Vue 不支持泛型组件。

我自己写了个例子,好像并不是这样啊:
截屏2022-08-08 15 36 04

从 Slot 中吐出来的属性 toggleView() => void,调用时 toggleView('3232') 提示错误。

@yetrun
Copy link
Author

yetrun commented Aug 8, 2022

前面的回复似乎都在讨论表格组件中是否该使用 Slot 语法的问题,我还没有遇到表格的用法,暂且不讨论。我在讨论的是普遍的情况,整个 API 设计中都倾向于 render 设计而不是采用 Slot. 像我用菜单组件举例子,每个菜单项不一定是一致的数据,采用 Slot 写法更友好:

<template>
  <n-menu>
    <n-menu-option @click="$router.push('/about')">跳转到路由</n-menu-option>
    <n-menu-option @click="Dialog.open(...)">打开一个弹出框</n-menu-option>
    <n-menu-option @click="FileChooser.open(...)">选择一个文件</n-menu-option>
  </n-menu>
</template>

否则就要写一堆 if...else if. 另外,已经有采用 Slot 语法的组件,参考 NButtonGroup.

@07akioni
Copy link
Collaborator

07akioni commented Aug 9, 2022

类型检查要从根源来看,Vue 的 defineComponent 是没有提供这种机制的,并且依赖 vue-tsc 的长期可靠性是难以判断的

这个考量涉及到很多方面,在之前的 issue 也解释了多次了,不会在这方面投入精力是可以确定的事情

@yetrun
Copy link
Author

yetrun commented Aug 10, 2022

Mark 一下:#216

@tobiasdiez
Copy link

Could this feature please be reconsidered? Specifying menu entries is really inconvenient at the moment, and sometimes even impossible. For example, one cannot use vue.runtime.esm-bundler.js which requires templates to be pre-compiled (and which is what is used by nuxt3).

I don't quite understand the discussion about typescript. Is the problem that one cannot restrict the type of the slot of n-menu to be an array of n-menu-option? But that's also a problem for say the layout control, right? Other than that, the official docs recommend vue-tsc: https://vuejs.org/guide/typescript/overview.html#overview

The proposed design is nice and also allows for dynamic generation based on data, e.g. something like the following makes renderMenuLabel and renderMenuIcon obsolete (maybe except for in very complex scenarios):

  <n-menu>
    <n-menu-option v-for="item in items">
        <a :href="item.href">{{item.label}}</a>
        <template #icon><img :src="item.src"></img></template>
     </n-menu-option>
  </n-menu>

@lisonge
Copy link

lisonge commented Mar 3, 2023

也许你应该使用 jsx @vitejs/plugin-vue-jsx

// <script setup lang="tsx">
const options = ref([
  { label: '跳转到路由', key: 'gotoRoute' },
  { label: '打开一个弹出框', key: 'openDialog' },
  { label: '选择一个文件', key: 'openFileDialog' }
])
const renderLabel = (option) => {
  switch(option.key) {
    case 'gotoRoute': return <div>gotoRoute</div>
    case 'openDialog': return  <div>openDialog </div>
    case 'openFileDialog': return  <div>openFileDialog</div>
  }
}

另外示例文档里大量使用原生的h函数而不是 jsx 也确实给人 api 难用的感觉

比如我之前就不知道可以使用 jsx,看到这个 api 要手写 h 函数,完全没有使用的欲望

@lisonge
Copy link

lisonge commented May 18, 2023

从 Slot 里面吐出来的 props 里面行数据的类型只能是 any 或者 unknown。

根本原因是 Vue 不支持泛型组件。

@amadeus711 现在 vue3.3 支持泛型组件了

image

@Mrcxt
Copy link

Mrcxt commented May 18, 2023

是的,其实使用这个库就是应该用jsx来写才是最合理的,因为这个库就是按照jsx的模式来开发的,对template很不友好,h函数写起来真不舒服。原先我也是觉得很难用,现在我用vue3基本都是写jsx了,然后这个库的实用性瞬间提升了好多

@fudiwei
Copy link

fudiwei commented Aug 23, 2023

毛遂自荐 @skit/x.naive-ui,基于 Naive-UI 二次封装了 Menu 等组件,支持插槽式的写法。

@yetrun 的代码在此扩展下可以写为:

<x-n-menu>
  <x-n-menu-item @click="() => $router.push('/about')">跳转到路由</x-n-menu-item>
  <x-n-menu-item @click="() => Dialog.open(...)">打开一个弹出框</x-n-menu-item>
  <x-n-menu-item @click="() => FileChooser.open(...)">选择一个文件</x-n-menu-item>
</x-n-menu>

@lisonge
Copy link

lisonge commented Aug 23, 2023

@fudiwei

依赖规则看起来写错了,vue 和 naive-ui 应该放在 peerDependencies 和 devDependencies 而不是 dependencies

https://github.com/fudiwei/x.naive-ui/blob/1f4d601841238ab32a211184ace877920f167b71/package.json#L48-L51

image

使用 "dependencies": { "@skit/x.naive-ui": "0.4.0", "vue": "3.2.0" }npm i 安装后发现存在 node_modules/vue@3.2.0@skit/x.naive-ui/node_modules/vue@3.3.4 两个版本的 vue

@fudiwei
Copy link

fudiwei commented Aug 23, 2023

@fudiwei

依赖规则看起来写错了,vue 和 naive-ui 应该放在 peerDependencies 和 devDependencies 而不是 dependencies

https://github.com/fudiwei/x.naive-ui/blob/1f4d601841238ab32a211184ace877920f167b71/package.json#L48-L51

image

@lisonge

感谢提醒 🙏 安装的时候确实疏忽了忘记加 --save-dev 了。peerDependencies 已经包含。


Fixed on v0.4.1.

@lukaifang
Copy link

无力吐槽,难用至极

@leookun
Copy link

leookun commented Dec 14, 2023

相反,我觉得这个设计非常好,render中访问响应式变量会自动获得响应式追踪,很灵活
官网例子中大量使用了h函数,把它替换jsx会更好。
naiveui的几乎所有组件的所有props都支持传入vnode(h函数的结果,或者说一段jsx),这意味着你可以在任何地方书写无限的slot。吐槽的人甚至没有搞清楚vue的template中的slot到底是什么。

不过需要注意一点,如果将vnode作为props传递,需要确保vnode是在render函数中创建的,或者使用watchSyncEffect保证vnode与state同步,或者computed,否则vnode将会失去响应性

@Cat7373
Copy link

Cat7373 commented Apr 17, 2024

相反,我觉得这个设计非常好,render中访问响应式变量会自动获得响应式追踪,这非常非常的灵活 官网例子中大量使用了h函数,请一定把它替换jsx。 naiveui的几乎所有组件的所有props都支持传入vnode(render函数的结果,或者说一段jsx),这意味着你可以在任何地方书写无限的slot,或者诸如hoc等更灵活的组件设计模式也可以变得更加轻松。吐槽的人甚至没有搞清楚vue的template中的slot到底是什么。

不过需要注意一点,如果将vnode作为props传递,请确保vnode是在render函数中创建的,或者使用watchSyncEffect保证vnode与state同步,否则vnode将会失去响应式

@leookun 难道组件库甚至 Vue 本身的作用不都是帮助屏蔽复杂性么?为什么开发网页的人要搞清楚 slot 到底是什么呢?那是不是可以讲,用组件库的人甚至没搞清楚 input 到底有多少原生属性,它写的这些代码到底在原生上是怎么实现的?

@jahnli
Copy link
Collaborator

jahnli commented Apr 17, 2024

你可以使用 tsx jsx 来实现模板效果

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request New feature or request
Projects
None yet
Development

No branches or pull requests