---
contentId: 75df6bff-de40-4056-af23-fae2401cd21a
title: Créer une Bibliothèque de Composants Vue.js Simplement
description: Gérer les complexités des SFC de Vue avec des outils puissants comme unbuild et mkdist pour créer une bibliothèque de composants.
date: 2024-09-24
---

TL;DR: <GitHubLink repo="barbapapazes/vue-library" /> _Continuez à lire pour comprendre le processus derrière cela et les raisons de mes choix._

Une bibliothèque de composants est une collection de morceaux réutilisables qui peuvent être utilisés dans divers projets. Elle facilite le partage de ressources entre différents projets et équipes. Ces composants peuvent être basiques et génériques comme des boutons, des champs de saisie et des modales, ou des modules plus spécifiques au métier. _En fin de compte, c'est une méthode pour partager du code entre les projets, une compétence cruciale qui peut faire économiser beaucoup de temps._

Cependant, construire une bibliothèque de composants avec [Vue.js](https://vuejs.org) est plus difficile qu'il n'y paraît en raison des [Single File Components](https://vuejs.org/guide/scaling-up/sfc.html) (SFC). Dans un projet TypeScript standard, vous transpilez généralement en JavaScript et regroupez vos fichiers avec un outil comme [tsup](https://tsup.egoist.dev/) ou [Vite](https://vitejs.dev/).

<CalloutReadMore id="1fafed86-05bb-4de4-b7d0-bb0bd6abc9d2" />

> [!NOTE]
> N'oubliez pas de **livrer le code du paquet aussi natif que possible** et de laisser les outils de l'utilisateur gérer la transpilation et l'optimisation. C'est un principe que je garde toujours à l'esprit lorsque je crée des bibliothèques, ce qui simplifie considérablement le processus. _Chaque cas est unique, mais c'est une bonne ligne directrice à suivre._

L'utilisation des SFC de Vue.js complique ce processus. Examinons pourquoi en explorant les défis que vous rencontrerez lors de la construction d'une bibliothèque de composants Vue.js.

Considérons le composant suivant :

```vue
<script lang="ts" setup>
import { useUser } from '../composables'

const { user } = useUser()
</script>

<template>
  <div v-if="user">
    {{ user.name }}
  </div>
</template>
```

Le composable est le suivant :

```ts
import { ref } from 'vue'

export function useUser() {
  const user = ref({ name: 'John Doe' })

  return {
    user
  }
}
```

Le composant utilise un composable importé d'un autre fichier. _Cela peut sembler trivial, mais cela pose un problème significatif._

Tout cela suit l'architecture ci-dessous :

```
src/
  components/
    User.vue
  composables/
    useUser.ts
  index.ts
```

Le fichier `index.ts` sert de point d'entrée de la bibliothèque et ressemble à ceci :

```ts
import User from './components/User.vue'

export { User }

export { useUser } from './composables/useUser'
```

De plus, le `package.json` inclut quelques `exports` :

```json
{
  "type": "module",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs"
    }
  },
  "main": "dist/index.mjs",
  "types": "dist/index.d.ts"
}
```

## Similaire à un projet TypeScript standard

> [!NOTE]
> Un bundle est un seul fichier encapsulant tout le code de votre projet. Il optimise le chargement du projet en minimisant le nombre de fichiers à charger. Ce bundle n'est pas transpile (sauf TypeScript vers JavaScript) ni minifié lors de la création d'un package npm.

Maintenant, si nous tentons de regrouper ce projet, notre dossier `dist` apparaîtra comme ceci :

```
dist/
  index.cjs
```

En inspectant `index.cjs`, vous trouverez une surprise : le composable `useUser` est intégré, mais le composant est complètement ignoré. _S'il fonctionne, l'outil pourrait déclencher une erreur du type `No loader is configured for ".vue" files`. Essentiellement, des outils comme [esbuild](https://esbuild.github.io) ou [tsc](https://www.typescriptlang.org/docs/handbook/compiler-options.html) ne savent pas comment gérer les fichiers Vue. C'est logique, car les fichiers Vue ne sont pas des fichiers JavaScript._

Le problème devient évident. Nous avons besoin d'une configuration pour gérer les fichiers Vue. Jetons un coup d'œil rapide sur Internet pour quelque chose comme `vue loader`, car nous utilisons des fichiers Vue et devons les transpiler en JavaScript.

Le premier résultat est [Vue Loader](https://www.npmjs.com/package/vue-loader), qui est un loader Webpack. _Non. Je refuse d'utiliser Webpack. Vite est maintenant un standard, et il n'y a aucune justification pour employer Webpack dans ce contexte._

Mais avant de plonger plus loin, rappelons-nous notre mantra : **livrer le code du paquet aussi natif que possible** _et [keep it simple, stupid!](https://en.wikipedia.org/wiki/KISS_principle)_ La transpilation des fichiers SFC Vue avec Webpack semble contraire à ce principe.

## Ignorer les fichiers Vue

Pour livrer des fichiers `.vue` sans les transpiler, nous demandons à notre outil de les ignorer. Cela est réalisé à l'aide d'un outil appelé [unbuild](https://unbuild.unjs.io) provenant de l'écosystème [UnJS](https://unjs.io).

Unbuild est un outil simple mais hautement configurable car il est construit sur Rollup.

Pour notre petit projet, nous pouvons l'expérimenter directement :

```sh
npx unbuild
```

Malheureusement, cela échoue pour les mêmes raisons que précédemment :

```txt
src/components/ShowGitHubUser.vue (1:0): Expression expected (Note that you need plugins to import files that are not JavaScript)
```

_Mais unbuild a le potentiel pour bien plus._

Pour résoudre ce problème, essayons la solution évidente. Dans le fichier `index.ts`, supprimons l'exportation du composant et exportons uniquement les fichiers TypeScript :

```ts
export { useUser } from './composables/useUser'
```

Maintenant, la commande `npx unbuild` fonctionne correctement.

```sh
➜ npx unbuild
ℹ Automatically detected entries: src/index [esm] [dts]
ℹ Building vue-library
ℹ Cleaning dist directory: ./dist
✔ Build succeeded for vue-library
  dist/index.mjs (total size: 139 B, chunk size: 139 B, exports: useUser)

Σ Total dist size (byte size): 531 B
```

C'est un bon début, mais nos composants restent dans le dossier `src` et peu importe combien vous cherchez ardemment, ils sont absents du dossier `dist`.

## Copier les fichiers Vue

Maintenant que les fichiers `.ts` sont traités correctement, nous pouvons tenter de copier les fichiers `.vue` dans le dossier `dist` avec une simple commande `cp`.

```sh
cp -r src/components/ dist/components/
```

Ensuite, nous ajoutons un champ `exports` dans le `package.json` pour informer l'utilisateur où localiser les composants :

```json
{
  "type": "module",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs"
    },
    "./components/*": {
      "import": "./dist/components/*.vue"
    }
  },
  "main": "dist/index.mjs",
  "types": "dist/index.d.ts"
}
```

Ça a l'air prometteur, n'est-ce pas ? _Mais ce n'est pas le cas._ :pensive:

## Le dilemme du chemin d'importation

Dans notre composant Vue, le composable `useUser` est importé comme ceci :

```vue
<script lang="ts" setup>
import { useUser } from '../composables'
</script>
```

Pendant ce temps, notre dossier `dist` est structuré comme suit :

```
dist/
  components/
    User.vue
  index.d.mts
  index.d.ts
  index.mjs
```

Pouvez-vous identifier le problème ? :eyes:

Le composant est inutilisable et produira une erreur du type `Cannot find module '../composables' or its corresponding type declarations`, car le chemin mène nulle part. Nous avons regroupé tous nos fichiers TypeScript dans un seul fichier `index.mjs`, perdant complètement la structure du projet.

## Regrouper ou ne pas regrouper ?

À partir de là, nous avons deux choix :

1. **Regrouper les fichiers Vue** en utilisant [Vite](https://vitejs.dev), des plugins et une configuration extensive.
2. **Conserver la structure de répertoires** et utiliser un outil comme [mkdist](https://github.com/unjs/mkdist) pour une transpilation fichier à fichier (construction sans bundle) sur les fichiers TypeScript.

La décision dépendra largement de vos besoins spécifiques, mais je suis convaincu que la simplicité est la meilleure approche. Donc, explorons la deuxième option.

L'objectif est de transformer les fichiers TypeScript en fichiers JavaScript, tout en préservant la structure d'origine du dossier `src` dans le dossier `dist` et en _ignorant_ les fichiers Vue.

> [!NOTE]
> En utilisant un bloc `<script setup lang="ts">`, mkdist ignorera les fichiers Vue pour permettre au compilateur Vue de générer des props d'exécution. Sinon, il générera un fichier `.vue.d.ts` pour offrir des types pour le composant Vue. Rappelez-vous que le [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue) et Vite comprennent intrinsèquement les blocs de script TypeScript. Pour plus de détails, consultez [issue mkdist#14](https://github.com/unjs/mkdist/issues/14).

## Configurer Unbuild

Oui, nous allons configurer unbuild car il intègre mkdist, ce qui le rend aggréable à utiliser.

Commencez par créer un fichier `build.config.ts` à la racine du projet. Unbuild lira ce fichier de configuration pour comprendre comment traiter le projet. Il suppose des valeurs par défaut et infère de nombreux aspects à partir du `package.json`, mais nous devons lui dire d'utiliser mkdist.

```ts
import { defineBuildConfig } from 'unbuild'

export default defineBuildConfig({
  entries: ['./src/'],
  declaration: true,
})
```

C'est un fichier de configuration très simple. _Et je l'adore._ La clé `entries` informe unbuild du fichier initial à commencer à transpiler. Cependant, `./src/` est un répertoire, indiqué par le `/` à la fin. Avec cet indice, unbuild contourne le regroupement par défaut `rollup` en faveur de `mkdist`. La clé `declaration` incite unbuild à créer des fichiers de déclaration TypeScript (`.d.ts`).

Nous pouvons également supprimer le champ `exports` dans le `package.json` puisque nous ne regrouperons plus les fichiers et restaurer l'exportation du composant dans le fichier `index.ts`.

Relaçons la commande `npx unbuild` et magie :

```sh
➜ npx unbuild
ℹ Building vue-library
ℹ Cleaning dist directory: ./dist
✔ Build succeeded for vue-library
  dist (total size: 600 B)
  └─ dist/index.d.ts (108 B)
  └─ dist/index.mjs (112 B)
  └─ dist/composables/useUser.d.ts (79 B)
  └─ dist/composables/useUser.mjs (124 B)
  └─ dist/components/User.vue (177 B)
Σ Total dist size (byte size): 600 B
```

Maintenant, le dossier `dist` apparaît comme ceci :

```
dist/
  components/
    User.vue
  composables/
    useUser.d.ts
    useUser.mjs
  index.d.ts
  index.mjs
```

Cela reflète la structure du dossier `src`, et le composant `User.vue` est maintenant utilisable dans le dossier `dist`. Le chemin d'importation relatif reste intact, et le composant peut être utilisé dans n'importe quel projet Vue. :partying_face:

## Développement local

De nombreux tutoriels s'arrêtent ici, suggérant que vous êtes prêt à publier votre package sur npm. Mais comment pouvez-vous créer une bibliothèque complexe si vous ne pouvez pas **voir le résultat de votre travail** ? Si vous ne pouvez pas la tester tout en la construisant ?

Vous ne pouvez pas.

Examinons comment utiliser la bibliothèque dans un projet local, au sein du même dépôt pour plus de simplicité.

Je vais expliquer la méthode la plus simple en utilisant un [pnpm workspace](https://pnpm.io/).

Commencez par créer un fichier `pnpm-workspace.yaml` à la racine du projet :

```yaml
packages:
  - .
  - playground
```

Ensuite, créez un nouveau projet Vite dans un dossier `playground` :

```sh
npx create-vite playground --template vue-ts
```

Puisque nous utilisons un pnpm workspace, installez les dépendances à partir de la racine du projet :

```sh
pnpm install
```

> [!NOTE]
> Cette étape n'est pas obligatoire. Vous pouvez créer un nouveau projet Vite et installer les dépendances à l'intérieur. Pnpm simplifie cela en installant les dépendances pour tous les espaces de travail à la racine. Une simple commande `pnpm install` installera les dépendances de tous les espaces de travail.

Maintenant, nous pouvons utiliser cette bibliothèque `playground` dans un projet "réel" pour tester notre bibliothèque.

Par exemple, ouvrez le fichier `src/App.vue` et importez le composant `User` :

```vue
<script setup lang="ts">
import { User } from '../../src'
</script>

<template>
  <User />
</template>
```

Et exécutez le projet :

```sh
cd playground && pnpm dev
```

Vous verrez le composant `User` s'afficher dans le navigateur. :tada: Si simple, mais si puissant. _J'utilise cette méthode pour presque toutes les bibliothèques que je construis, et cela fonctionne parfaitement._

## Publication sur npm

Cette partie est simple une fois que vous avez compris le flux de travail.

Commencez par nommer votre bibliothèque. J'utiliserai `@barbapapazes/vue-library` pour cet exemple.

Ensuite, créez un compte sur [npm](https://www.npmjs.com) et connectez-vous en utilisant la commande `npm login`.

Puis, installez [changelogen](https://changelogen.unjs.io) pour générer un changelog, mettre à jour le numéro de version selon la gestion sémantique des versions et les _conventional commits_, et pré-remplir la version sur GitHub.

```sh
pnpm i -D changelogen
```

Ensuite, ajoutez deux scripts à votre `package.json` :

```json
{
  "scripts": {
    "prepack": "unbuild",
    "release": "changelogen --release && npm publish --access public && git push --follow-tags"
  }
}
```

Le script `prepack` exécute la commande `unbuild` avant de publier le package sur npm. Le script `release` génère un changelog, publie le package sur npm, et pousse les tags sur GitHub.

> [!NOTE]
> Votre projet doit être sur GitHub pour que l'option `--release` fonctionne. Sinon, omettez-là et créez la version manuellement sur votre dépôt.

Maintenant, publiez votre package sur npm en utilisant la commande suivante :

```sh
npm run release
```

Et c'est tout ! Vous venez de publier votre première bibliothèque de composants Vue.js sur npm. :rocket:

## Tout est prêt

Nous avons terminé pour aujourd'hui. Nous avons couvert de nombreux sujets :

- Comment construire une bibliothèque de composants Vue.js avec TypeScript.
- Comment gérer les fichiers Vue dans un projet TypeScript.
- Comment utiliser unbuild pour transpiler les fichiers TypeScript sans regrouper les fichiers Vue.
- Comment tirer parti de mkdist pour maintenir la structure du projet.
- Comment utiliser un pnpm workspace pour tester la bibliothèque dans un projet local.
- Comment publier la bibliothèque sur npm.

Pour un exemple plus détaillé et complexe, consultez GitHub : <GitHubLink repo="barbapapazes/vue-library" /> mais tout ce qui a été expliqué ici provient de **mon expérience dans le monde réel**.

J'espère que vous avez apprécié ce tutoriel et qu'il vous aidera à construire votre propre bibliothèque de composants Vue.js. Si vous avez des questions, n'hésitez pas à me contacter sur [X (Twitter)](https://x.com/soubiran_).

_Vous pouvez aller encore plus loin en utilisant Vite pour construire votre bibliothèque de composants :_

<CalloutReadMore id="1fafed86-05bb-4de4-b7d0-bb0bd6abc9d2" />
