菜单
本页目录

贡献指南

了解如何为 shadcn/vue 贡献代码

简介

感谢您对 shadcn-vue.com 的支持。我们在此期待能与您合作。

在提交您的第一个 Pull Request 之前,请花一点时间阅读此文档。我们还强烈建议您查看已有的 issue 和 Pull Request,看看是否有人正在尝试类似的工作。

如果您需要任何帮助,欢迎通过 Discord 联系核心团队。

本指南将会为新的贡献者提供详细信息。

关于此仓库

此仓库为一个 monorepo(单仓库)。

项目结构

GitHub 仓库由多个文件夹组成,以下是简要概览:

shadcn-structure-light.svg

  1. packages - 源代码支持用 Nuxt 做为模块(module)以及通过 CLI添加新组件。
  2. apps/www - 主文件夹包含网站的源代码和所有 shadcn/vue 组件,以及包含重要的子文件夹,是一个包含独立的 package.json 的子项目。
  3. .vitepress - 包含 Vitepress 和 `shadcn/vue 网站的配置和源代码。
  4. src - 存放每个 shadcn/vue 组件或演示及其网站文档的主要源代码。
  5. __ registry __ - 存放由 scripts/build-registry.ts 生成的注册表,为 CLI 提供组件服务。此文件夹内容是自动生成的,不需要手动编辑。
  6. scripts - 包含各种辅助脚本,如 build-registry.ts,它会自动生成 __registry__ 文件夹。
  7. content - 包含所有 /docs 路由文档。每个组件都有一个 .md 文件,记录组件的安装和使用方法。
  8. examples - 包含所有不属于 /docs 的示例,如主页示例
  9. lib/registry - 主文件夹,为每个组件存放不同的样式源代码。此文件夹可能是你会修改的地方。

shadcn/vue 中,我们为每个组件都配备了两种样式:

  • Default(默认)
  • New York

添加到存储库中的每个组件都必须支持这两个版本,包括主源代码和相关的演示(demo)。

添加或修改组件时,请确保以下条件:

  1. 您为每种样式都做了相应的更改。
  2. 更新文档。
  3. 运行 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>

如您所见,AccordionRootEmitsAccordionRootProps 类型是从 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 的浏览器