Construire un premier composant avec Vue.js et Tailwind CSS

- Read in english
Ressources: huchet-vue

Avec notre bibliothèque de composants Vue.js configurée, il est temps de construire notre premier composant : un bouton simple. Cet exercice nous aidera à comprendre comment structurer nos composants.

Nous allons utiliser Tailwind CSS et Tailwind Variants pour le style. Tailwind Variants aide à organiser les classes Tailwind CSS de manière structurée pour créer et combiner des variantes.

Configuration du Package

Naviguez vers packages/huchet-vue et installez les dépendances nécessaires :

bash
cd packages/huchet-vue && pnpm add -D vue vite

Ensuite, créez un dossier src avec le composant Button/Button.vue :

bash
mkdir -p src/Button && touch src/Button/Button.vue

Ouvrez le fichier Button.vue et commençons à écrire notre composant.

Écriture du Composant Bouton

Pour notre exemple, nous allons créer un bouton avec deux variantes : solide et contour. La variante solide aura une couleur de fond, tandis que la variante contour inclura une bordure.

Pour assurer l'absence de conflit de classes et simplifier le refactoring, la maintenabilité, la lisibilité et la structure, nous utiliserons Tailwind Variants. Nous allons organiser nos classes dans un objet, et Tailwind Variants appliquera les classes appropriées en fonction des props.

Ajoutez une balise script en haut du fichier Button.vue :

vue
<script lang="ts">
import { tv, type VariantProps } from 'tailwind-variants'
</script>

Important

Ne pas ajouter l'attribut setup. Cette omission est volontaire.

La fonction tv est un helper pour organiser les classes. Elle accepte un objet avec des clés base et variants. Le base est une chaîne de classes partagées, tandis que variants est un enregistrement avec des clés de variante et des valeurs de classe.

vue
<script lang="ts">
// ...

const button = tv({
  base: 'border-2 px-2.5 py-1.5 text-sm font-semibold focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
  variants: {
    variant: {
      solid: 'border-transparent bg-blue-500 text-white hover:bg-blue-600 active:bg-blue-700',
      outline: 'border-blue-500 text-blue-500 hover:bg-blue-50 active:bg-blue-100',
    }
  }
})
</script>

Dans la clé variants, vous pourriez également inclure d'autres clés comme size avec des variantes sm et md.

Ensuite, extrayez les variantes pour les utiliser dans les props de notre composant :

vue
<script lang="ts">
// ...

type ButtonVariantProps = VariantProps<typeof button>

export interface ButtonProps {
  label?: string
  variant?: ButtonVariantProps['variant']
}
</script>

Ouvrez ensuite une nouvelle balise script avec l'attribut setup :

vue
<script setup lang="ts">
withDefaults(defineProps<ButtonProps>(), {
  variant: 'solid',
})
</script>

Nous avons divisé la balise script en deux parties pour permettre des exports nommés. L'interface ButtonProps est utilisée dans la fonction defineProps et est exportée afin que les développeurs puissent l'étendre pour créer un nouveau composant. Ce détail est crucial pour construire une bibliothèque de composants robuste.

Ajoutez le template du composant :

vue
<template>
  <button :class="button({ variant })">
    <slot>
      {{ label }}
    </slot>
  </button>
</template>

Notre composant est prêt à être utilisé :

html
<Button variant="outline" label="Cliquez moi" />

Cependant, il n'est pas utilisable tant que nous n'avons pas construit notre bibliothèque de composants.

Construction de la Bibliothèque de Composants

Ensuite, nous allons construire la bibliothèque de composants. Commencez par créer un fichier vite.config.ts à la racine du package huchet-vue :

bash
touch vite.config.ts

Cette configuration indique à Vite comment transpiler nos composants Vue.js en JavaScript pour être utilisé dans d'autres projets. Par défaut, Vite est conçu pour les applications web, pas pour les bibliothèques, donc nous allons l'ajuster à nos besoins.

Installez le plugin :

bash
pnpm add -D @vitejs/plugin-vue

Modifiez le fichier vite.config.ts avec la configuration suivante :

ts
import Vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [Vue()],
  build: {
    lib: {
      formats: ['es'],
      name: 'huchet-vue',
      fileName: (_, name) => `${name}.mjs`,
      entry: {
        index: resolve(__dirname, 'src/index.ts'),
      },
    },
    rollupOptions: {
      external: ['vue', 'tailwind-variants'],
    },
  },
})

Nous choisissons de ne pas inclure Vue.js et Tailwind Variants dans le bundle final pour plus de flexibilité, permettant aux utilisateurs d'utiliser leurs propres versions. Cela évite également la duplication dans le bundle final si l'utilisateur a déjà ces dépendances.

Ajoutez-les comme dépendances peer :

bash
pnpm add --save-peer vue tailwind-variants

Note

Une dépendance peer n'est pas installée par le package mais doit être présente dans le projet de l'utilisateur. Cette flexibilité est particulièrement utile lorsqu'un package peut fonctionner dans des contextes variés.

Créez le fichier src/index.ts, comme mentionné dans vite.config.ts :

bash
touch src/index.ts

Ce fichier exporte tous les composants de la bibliothèque :

ts
export * from './Button'

Créez un nouveau index.ts au niveau du composant :

bash
touch src/Button/index.ts

Ce fichier sert d'API publique, exposant des éléments sélectionnés à l'utilisateur tout en gardant la structure interne cachée :

ts
export {
  type ButtonProps,
  default as Button
} from './Button.vue'

Enfin, construisez la bibliothèque :

bash
pnpm run build

La bibliothèque se compile sous le répertoire dist sous le nom index.mjs.

Exposition des Composants

Notre bibliothèque de composants est construite, mais pour être utilisable comme un package npm, nous devons définir certains champs dans le fichier package.json. Ces champs fournissent des informations cruciales à Node sur la façon dont notre package doit être chargé :

json
{
  "exports": {
    ".": {
      "import": "./dist/index.mjs"
    }
  },
  "main": "./dist/index.mjs"
}

Simple, non ? Le champ exports informe Node des imports disponibles pour notre package. Dans notre cas, nous n'avons qu'un seul import—la racine de notre package—et c'est le fichier index.mjs dans le dossier dist. Le champ main spécifie le point d'entrée de notre package. Ici, le point d'entrée est le fichier index.mjs situé dans le dossier dist.

Note

Puisque nous avons seulement un point d'entrée, nous pourrions nous fier uniquement au champ main. Cependant, il est bon de définir le champ exports pour garantir la compatibilité avec les versions passées de Node et préparer le support de points d'entrée multiples. Le champ exports est plus moderne et puissant que main.

Dans le prochain article, nous ajouterons des définitions de type à notre bibliothèque de composants pour offrir une meilleure expérience aux développeurs.

Photo de profil d'Estéban

Merci de me lire ! Je m'appelle Estéban, et j'adore écrire sur le développement web.

Je code depuis plusieurs années maintenant, et j'apprends encore de nouvelles choses chaque jour. J'aime partager mes connaissances avec les autres, car j'aurais aimé avoir accès à des ressources aussi claires et complètes lorsque j'ai commencé à apprendre la programmation.

Si vous avez des questions ou souhaitez discuter, n'hésitez pas à commenter ci-dessous ou à me contacter sur Bluesky, X, et LinkedIn.

J'espère que vous avez apprécié cet article et appris quelque chose de nouveau. N'hésitez pas à le partager avec vos amis ou sur les réseaux sociaux, et laissez un commentaire ou une réaction ci-dessous—cela me ferait très plaisir ! Si vous souhaitez soutenir mon travail, vous pouvez me sponsoriser sur GitHub !

Continue readingUne meilleure expérience avec TypeScript et TSConfig