贡献指南
了解如何为 shadcn/vue 贡献代码
简介
感谢您对 shadcn-vue.com 的支持。我们在此期待能与您合作。
在提交您的第一个 Pull Request 之前,请花一点时间阅读此文档。我们还强烈建议您查看已有的 issue 和 Pull Request,看看是否有人正在尝试类似的工作。
如果您需要任何帮助,欢迎通过 Discord 联系核心团队。
本指南将会为新的贡献者提供详细信息。
关于此仓库
此仓库为一个 monorepo(单仓库)。
- 我们使用 pnpm 和 workspaces 进行开发。
项目结构
GitHub 仓库由多个文件夹组成,以下是简要概览:
- packages - 源代码支持用
Nuxt
做为模块(module)以及通过CLI
添加新组件。 - apps/www - 主文件夹包含网站的源代码和所有
shadcn/vue
组件,以及包含重要的子文件夹,是一个包含独立的package.json
的子项目。 - .vitepress - 包含
Vitepress
和 `shadcn/vue 网站的配置和源代码。 - src - 存放每个 shadcn/vue 组件或演示及其网站文档的主要源代码。
- __ registry __ - 存放由
scripts/build-registry.ts
生成的注册表,为 CLI 提供组件服务。此文件夹内容是自动生成的,不需要手动编辑。 - scripts - 包含各种辅助脚本,如
build-registry.ts
,它会自动生成__registry__
文件夹。 - content - 包含所有
/docs
路由文档。每个组件都有一个.md
文件,记录组件的安装和使用方法。 - examples - 包含所有不属于
/docs
的示例,如主页示例。 - lib/registry - 主文件夹,为每个组件存放不同的样式源代码。此文件夹可能是你会修改的地方。
在 shadcn/vue
中,我们为每个组件都配备了两种样式:
- Default(默认)
- New York
添加到存储库中的每个组件都必须支持这两个版本,包括主源代码和相关的演示(demo)。
添加或修改组件时,请确保以下条件:
- 您为每种样式都做了相应的更改。
- 更新文档。
- 运行
pnpm build:registry
来更新注册表。
开发
首先,克隆仓库:
git clone git@github.com:radix-vue/shadcn-vue.git
安装依赖
pnpm install
运行工作区
您可以使用 pnpm --filter=[WORKSPACE]
命令来启动某个工作区的开发流程,或使用我们设置的一些快捷命令。
示例
运行 shadcn-vue.com
网站:
pnpm dev
运行 shadcn-vue
CLI 包:
pnpm dev:cli
文档
此项目的文档位于 www
工作区。您可以通过运行以下命令在本地运行文档:
pnpm dev
文档是用 Markdown(.md
)编写的。您可以在 apps/www/src/content
目录中找到文档文件。
CLI
shadcn-vue
包是一个用于向您的项目添加组件的 CLI。您可以在这里找到 CLI 的文档。
任何对 CLI 的更改应在 packages/cli
目录中完成。如果可以的话,最好为你的更改添加相应的测试。
测试
测试由使用 Vitest 编写。您可以在仓库根目录运行所有测试:
pnpm test
提交 Pull Request 时,请确保测试通过。如果你在提交中添加了新功能,请务必包括测试。
提交规范
在创建 Pull Request 之前,请检查您的提交是否符合本仓库使用的提交规范。
创建提交时,我们建议你遵循 类别(范围或模块): 信息
的格式,并使用以下类别之一:
feat / feature
: 所有引入全新代码或新功能的更改fix
: 修复 bug 的更改(理想情况下,您应引用相关 issue)refactor
: 与代码相关的更改,但不是修复或新功能docs
: 更改现有文档或创建新文档(如 README、lib 或 CLI 使用文档)build
: 与软件构建、依赖项或新依赖项添加相关的所有更改test
: 与测试相关的所有更改(添加新测试或更改现有测试)ci
: 与持续集成配置相关的所有更改(如 GitHub Actions、CI 系统)chore
: 所有不属于上述类别的仓库更改
例如,feat(组件): 为头像组件添加新的属性
。
如果您对详细的规范感兴趣,可以访问 Conventional Commits。
单文件组件(SFC)
在 shadcn/ui(React 版本的 shadcn)中,多个组件整合在一个文件中,而 Vue 只支持每个文件一个组件,因此称为单文件组件(SFC)。在这种情况下,您需要为每个组件部分创建单独的文件,然后在 index.ts
文件中导出它们。
请参阅 Accordion 源代码作为示例。
包装 Radix-Vue 组件
Radix-Vue 提供了许多用于构建可重用组件的低级 UI 组件。在许多情况下,您需要包装 Radix-Vue 组件。
Props 和事件
所有 Radix-Vue 组件都暴露了它们的 prop 和 emit 类型。我们需要将从外部传入的 props/事件转发到 Radix-Vue 组件。
为此,我们有一个名为 useForwardPropsEmits
的辅助函数,它将必须绑定到子 Radix 组件的 props 和事件组合起来。
更清楚地说,useForwardPropsEmits
函数接受 props 和一个可选的 emit 函数,返回一个组合后的对象,作为 props 传递。
以下是 Accordion 根组件的示例:
<script setup lang="ts">
import { AccordionRoot, type AccordionRootEmits, type AccordionRootProps, useForwardPropsEmits } from 'radix-vue'
const props = defineProps<AccordionRootProps>()
const emits = defineEmits<AccordionRootEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<AccordionRoot v-bind="forwarded">
<slot />
</AccordionRoot>
</template>
如您所见,AccordionRootEmits
和 AccordionRootProps
类型是从 radix 中导入的,与 useForwardPropsEmits
组合后通过 v-bind
绑定。
CSS 类
在某些情况下,我们希望在 shadcn/vue 组件中接受 class
作为 prop,然后将其与 Radix-Vue 组件上的默认 Tailwind 类结合在一起,使用 cn
实用函数。
在这些情况下,不能使用 v-bind
,因为这会导致双重类绑定。
来看一下 DrawerDescription.vue
的代码。
<script lang="ts" setup>
import type { DrawerDescriptionProps } from 'vaul-vue'
import { DrawerDescription } from 'vaul-vue'
import { type HtmlHTMLAttributes, computed } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<DrawerDescriptionProps & { class?: HtmlHTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<DrawerDescription v-bind="delegatedProps" :class="cn('text-sm text-muted-foreground', props.class)">
<slot />
</DrawerDescription>
</template>
如您所见,我们创建了一个名为 delegatedProps
的计算属性,将 class
从 props 中移除,然后将返回的值绑定到我们的 Radix 组件(在本例中是 DrawerDescription
)。
对于我们的 class
,我们首先将其声明为 HtmlHTMLAttributes['class']
类型,然后使用 cn
合并从 class
prop 和我们自己的类中传递的 Tailwind 类。
这种模式仅在需要合并用户提供的类与默认 Tailwind 类时适用。如果没有默认 Tailwind 类需要与用户提供的类合并,则不需要此模式。一个好的例子是 SelectValue.vue
组件。
<script setup lang="ts">
import { SelectValue, type SelectValueProps } from 'radix-vue'
const props = defineProps<SelectValueProps>()
</script>
<template>
<SelectValue v-bind="props">
<slot />
</SelectValue>
</template>
布尔型 Props
当构建组件的包装器时,在某些情况下,您可能希望忽略 Vue Props 的布尔值转换。您可以为所有布尔字段设置默认值为 undefined
,或者可以使用 useForwardProps
可组合函数。
来看一下 AccordionItem.vue
的代码:
<script setup lang="ts">
import { type HTMLAttributes
, computed } from 'vue'
import { AccordionItem, type AccordionItemProps, useForwardProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<AccordionItemProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<AccordionItem v-bind="forwardedProps" :class="cn('border-b', props.class)">
<slot />
</AccordionItem>
</template>
由于 AccordionItemProps
类型至少有一个布尔属性,我们需要对整个 props
对象使用 useForwardProps
。
请注意,useForwardPropsEmits
在底层使用了 useForwardProps
。
组件作为根
当您的根组件是 Vue 的原始组件(Primitive)时,使用 Primitive
会更容易。
来看一下 Button.vue
的代码:
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { Primitive, type PrimitiveProps } from 'radix-vue'
import { type ButtonVariants, buttonVariants } from '.'
import { cn } from '@/lib/utils'
interface Props extends PrimitiveProps {
variant?: ButtonVariants['variant']
size?: ButtonVariants['size']
class?: HTMLAttributes['class']
}
const props = withDefaults(defineProps<Props>(), {
as: 'button',
})
</script>
<template>
<Primitive :as="as" :as-child="asChild" :class="cn(buttonVariants({ variant, size }), props.class)">
<slot />
</Primitive>
</template>
您需要在 props 中扩展 PrimitiveProps
以支持 Primitive
组件。在大多数情况下,您还需要为 as
属性设置默认值。
同步 shadcn/ui
shadcn/vue 是一个社区驱动的 shadcn/ui 的 Vue 端口,随着时间推移,二者可能会不同步。
截至目前,我们与 shadcn/ui 的此 提交 同步。
点击以下链接检查我们是否应该同步较新的提交:
- 没有更改 - 如果您看到 “There isn’t anything to compare”,则无需执行任何操作,我们与最新版本同步。
- 有更改 - 如果有更改,您应该审查这些更改,并尝试将其应用到 shadcn/vue 代码库中并创建 PR,记得同时更新
latestSyncCommitTag
。
调试
以下是一些工具和技术,能够帮助您更有效地调试 shadcn/vue 或开发自己的项目。
安装 Vue 开发工具
为了轻松检查组件的 props、属性、事件等,您可以使用浏览器的 Vue DevTools 插件。此插件为调试 Vue 组件提供了用户友好的界面,并能提高您的开发体验。
启用自定义格式化程序
Vue 会将存储在 ref
中的值包装成一个对象,记录日志时会导致嵌套对象,需手动检查才能访问其中存储的值。
您可以在浏览器中启用自定义格式化程序,自动化这一过程。
- Firefox
- Chrome、Edge、Brave 及其他基于 Chromium 的浏览器