Créer une Bibliothèque de Composants Vue.js Simplement
TL;DR: 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 est plus difficile qu'il n'y paraît en raison des Single File Components (SFC). Dans un projet TypeScript standard, vous transpilez généralement en JavaScript et regroupez vos fichiers avec un outil comme tsup ou Vite.
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 :
<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 :
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 :
import User from './components/User.vue'
export { User }
export { useUser } from './composables/useUser'
De plus, le package.json
inclut quelques exports
:
{
"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 ou tsc 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, 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! 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 provenant de l'écosystème UnJS.
Unbuild est un outil simple mais hautement configurable car il est construit sur Rollup.
Pour notre petit projet, nous pouvons l'expérimenter directement :
npx unbuild
Malheureusement, cela échoue pour les mêmes raisons que précédemment :
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 :
export { useUser } from './composables/useUser'
Maintenant, la commande npx unbuild
fonctionne correctement.
➜ 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
.
cp -r src/components/ dist/components/
Ensuite, nous ajoutons un champ exports
dans le package.json
pour informer l'utilisateur où localiser les composants :
{
"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. 😔
Le dilemme du chemin d'importation
Dans notre composant Vue, le composable useUser
est importé comme ceci :
<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 ? 👀
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 :
- Regrouper les fichiers Vue en utilisant Vite, des plugins et une configuration extensive.
- Conserver la structure de répertoires et utiliser un outil comme 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 et Vite comprennent intrinsèquement les blocs de script TypeScript. Pour plus de détails, consultez issue mkdist#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.
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 :
➜ 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. 🥳
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.
Commencez par créer un fichier pnpm-workspace.yaml
à la racine du projet :
packages:
- .
- playground
Ensuite, créez un nouveau projet Vite dans un dossier playground
:
npx create-vite playground --template vue-ts
Puisque nous utilisons un pnpm workspace, installez les dépendances à partir de la racine du projet :
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
:
<script setup lang="ts">
import { User } from '../../src'
</script>
<template>
<User />
</template>
Et exécutez le projet :
cd playground && pnpm dev
Vous verrez le composant User
s'afficher dans le navigateur. 🎉 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 et connectez-vous en utilisant la commande npm login
.
Puis, installez changelogen 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.
pnpm i -D changelogen
Ensuite, ajoutez deux scripts à votre package.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 :
npm run release
Et c'est tout ! Vous venez de publier votre première bibliothèque de composants Vue.js sur npm. 🚀
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 : 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).