|
| 1 | +--- |
| 2 | +title: "Como crear una librería para Vue3" |
| 3 | +pubDate: "Thu Mar 21 2024" |
| 4 | +image: "https://github.com/CuCodersCommunity/cucoderscommunity.github.io/assets/50055316/06b16b54-012b-4d23-8334-e22b8ab5576a" |
| 5 | +username: "carlosjorger" |
| 6 | +categories: ["tutorials"] |
| 7 | +description: "En este artículo explicare paso a paso como crear un librería para Vue3 usando Vite** y **Typescript." |
| 8 | +canonicalUrl: "" |
| 9 | +--- |
| 10 | + |
| 11 | +# Como crear una librería para Vue3 |
| 12 | + |
| 13 | + |
| 14 | + |
| 15 | +Estas interesado en ir un paso más allá de crear un producto; estás frustrado de que el ecosistema de **Vue** no es tan rico como el de **React** en cuanto a librerías, frameworks, etc y quieres contribuir a este. Una buena idea podría ser crear tu propia librería donde podrías reutilizar tus propias componentes, composable o módulos js/ts. |
| 16 | + |
| 17 | +En este presente articulo plasmaré mis experiencia creando la librería de drad and drop [vue-fluid-dnd](https://github.com/carlosjorger/vue-fluid-dnd) para **Vue3** en la que actualmente estoy trabajando desde 0 usando **Vite** y **Typescript**. A diferencia de muchos artículos en internet voy a ir un paso más allá explicando un poco más algunos detalles de la configuración de nuestro proyecto que me hubiera gustado encontrarme cuando era un principiante. |
| 18 | + |
| 19 | +### Creando el proyecto |
| 20 | + |
| 21 | +Para crear el proyecto usando **Vite**, se necesita inicializar un proyecto de **Vite** con el siguiente comando: |
| 22 | + |
| 23 | +```bash |
| 24 | +npm create vite@latest |
| 25 | +``` |
| 26 | + |
| 27 | +Se debe seguir los pasos de la creación eligiendo un nombre (en este caso **`template-vue-component-lib`**) y después seleccionar **Vue** y **Typescript**. |
| 28 | + |
| 29 | +```bash |
| 30 | +❯ npm create vite@latest |
| 31 | +Need to install the following packages: |
| 32 | +create-vite@5.2.1 |
| 33 | +Ok to proceed? (y) y |
| 34 | +√ Project name: ... template-vue-component-lib |
| 35 | +√ Select a framework: » Vue |
| 36 | +√ Select a variant: » TypeScript |
| 37 | +``` |
| 38 | + |
| 39 | +Después ir al directorio correspondiente y instalar las dependencias: |
| 40 | + |
| 41 | +```bash |
| 42 | +cd template-vue-component-lib |
| 43 | +npm install |
| 44 | +``` |
| 45 | + |
| 46 | +Esta es la estructura del proyecto: |
| 47 | + |
| 48 | + |
| 49 | + |
| 50 | +Después se limpia el proyecto removiendo los archivos innecesarios (los archivos `index.html`, `App.vue`, `main.ts`, `stye.css` y las carpetas `public` y `assets`). |
| 51 | + |
| 52 | +## Creando la componente de la librería |
| 53 | + |
| 54 | +A continuación se crea una componente que es la funcionalidad que va a proveer nuestra librería. |
| 55 | +Creamos el archivo `MessageText.vue` en la carpeta `/src/components`. Este es el código de nuestra componente, note que se está usando sintaxis `script setup` de **Vue3** con **Typescript**: |
| 56 | + |
| 57 | +```vue |
| 58 | +<script setup lang="ts"> |
| 59 | +defineProps<{ msg: string }>(); |
| 60 | +</script> |
| 61 | +
|
| 62 | +<template> |
| 63 | + |
| 64 | + <h1 class="message">{{ msg }}</h1> |
| 65 | +</template> |
| 66 | +
|
| 67 | +<style scoped> |
| 68 | +.message { |
| 69 | + color: #bbb; |
| 70 | + background-color: #222; |
| 71 | +} |
| 72 | +</style> |
| 73 | +``` |
| 74 | + |
| 75 | +Después se crea el archivo `index.ts` en el directorio `/src`. Este archivo será el que exporte todas las herramientas que va a proveer nuestra librería, en nuestro caso la componente `MessageText`, pero de ser conveniente podemos exportar funciones, constantes, composables de **Vue**, todo lo que se necesite: |
| 76 | + |
| 77 | +```ts |
| 78 | +import MessageText from "./components/MessageText.vue"; |
| 79 | + |
| 80 | +export { MessageText }; |
| 81 | +``` |
| 82 | + |
| 83 | +## Configuración |
| 84 | + |
| 85 | +Con el código de nuestra librería listo, a continuación se procede a configurar **Vite**, `package.json` y el `tsconfig.json` de nuestro proyecto. |
| 86 | + |
| 87 | +### Configuración de Vite |
| 88 | + |
| 89 | +Para crear nuestra librería haremos uso del ["Modo librería"](https://vitejs.dev/guide/build.html#library-mode) usando la configuración [`build.lib`](https://vitejs.dev/config/build-options#build-lib) dentro de `defineConfig`. Dentro del código se explica cada sección a través de comentarios: |
| 90 | + |
| 91 | +```ts |
| 92 | +import { defineConfig } from "vite"; |
| 93 | +import vue from "@vitejs/plugin-vue"; |
| 94 | +import * as path from "path"; |
| 95 | +import { fileURLToPath } from "url"; |
| 96 | + |
| 97 | +// https://vitejs.dev/config/ |
| 98 | +export default defineConfig({ |
| 99 | + plugins: [ |
| 100 | + vue({ |
| 101 | + script: { |
| 102 | + defineModel: true, |
| 103 | + }, |
| 104 | + }), |
| 105 | + ], |
| 106 | + build: { |
| 107 | + lib: { |
| 108 | + // src/indext.ts es donde se expone el código que se va a usar de le librería |
| 109 | + entry: path.resolve(__dirname, "src/index.ts"), |
| 110 | + name: "TemplateVueComponentLib", |
| 111 | + // el nombre de los archivos de salida cuando se le hace build al proyecto |
| 112 | + fileName: "template-vue-component-lib", |
| 113 | + }, |
| 114 | + |
| 115 | + rollupOptions: { |
| 116 | + // asegúrate de externalizar las dependencias que no deben ser empaquetadas |
| 117 | + // dentro de tu biblioteca, en este caso es `vue` ya que se hará uso del |
| 118 | + // `vue` instalada por la aplicación que hace uso de esta librería. |
| 119 | + external: ["vue"], |
| 120 | + output: { |
| 121 | + globals: { |
| 122 | + vue: "Vue", |
| 123 | + }, |
| 124 | + }, |
| 125 | + }, |
| 126 | + }, |
| 127 | +}); |
| 128 | +``` |
| 129 | + |
| 130 | +### Configuración del archivo `tsconfig.json` |
| 131 | + |
| 132 | +A continuación se procede a modificar el archivo `tsconfig.json`, este contiene las opciones requeridas para compilar el código de **Typescript** de nuestro proyecto: |
| 133 | + |
| 134 | +```json |
| 135 | +{ |
| 136 | + "compilerOptions": { |
| 137 | + "target": "ES2020", |
| 138 | + "useDefineForClassFields": true, |
| 139 | + "module": "ESNext", |
| 140 | + "lib": ["ES2020", "DOM", "DOM.Iterable"], |
| 141 | + "skipLibCheck": true /* Bundler mode */, |
| 142 | + |
| 143 | + "moduleResolution": "Node", |
| 144 | + "resolveJsonModule": true, |
| 145 | + "isolatedModules": true, |
| 146 | + "jsx": "preserve", |
| 147 | + "esModuleInterop": true, |
| 148 | + "outDir": "dist", |
| 149 | + "declaration": true, |
| 150 | + /* Linting */ |
| 151 | + "strict": true, |
| 152 | + "noUnusedLocals": true, |
| 153 | + "noUnusedParameters": true, |
| 154 | + "noFallthroughCasesInSwitch": true |
| 155 | + }, |
| 156 | + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], |
| 157 | + "exclude": [], |
| 158 | + "references": [{ "path": "./tsconfig.node.json" }] |
| 159 | +} |
| 160 | +``` |
| 161 | + |
| 162 | +En breve se va a explicar los apartados más importantes de esta configuración, puede profundizar más en la [documentación oficial](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html): |
| 163 | + |
| 164 | +- **esModuleInterop** : Con este flag activado podemos importar módulos de **CommonJS** cumpliendo con la especificación de los módulos de **ES6** en js transpilado. |
| 165 | +- **outDir**: es el directorio en el cual se va a generar el js transpilado, por default se genera dicho js en la carpeta que contiene el código de **Typescript** compilado. |
| 166 | +- **declaration**: con `declaration` igual a `true` se genera los archivos `.d.ts` para cada archivo **TypeScript** o **JavaScript** dentro de tu proyecto. Dichos archivos de definición de tipos describen la API externa de tu módulo. Con archivos `.d.ts` permiten que herramientas como **Visual Studio** pueden proporcionar _intellisense_ y tipos precisos para código sin tipificar. |
| 167 | +- **include**: Especifica un array de nombres de ficheros o patrones de los ficheros que se van incluir en el programa. |
| 168 | +- **exclude**: Especifica un array de nombres de ficheros o patrones de los ficheros que no se van a incluir de los especificados en `include`. |
| 169 | + |
| 170 | +### Configuración del archivo `package.json` |
| 171 | + |
| 172 | +El último archivo a configurar pero no menos importante es el `package.json`. En este se puede encontrar detalles como la versión del proyecto, las dependencias necesarias, las compatibilidades, entre otros datos: |
| 173 | + |
| 174 | +```json |
| 175 | +{ |
| 176 | + "name": "template-vue-component-lib", |
| 177 | + "version": "0.0.0", |
| 178 | + "type": "module", |
| 179 | + "repository": { |
| 180 | + "type": "git", |
| 181 | + "url": "https://github.com/carlosjorger/template-component-lib.git" |
| 182 | + }, |
| 183 | + "files": ["dist"], |
| 184 | + "main": "./dist/template-vue-component-lib.cjs", |
| 185 | + "module": "./dist/template-vue-component-lib.js", |
| 186 | + "exports": { |
| 187 | + ".": { |
| 188 | + "types": "./dist/index.d.ts", |
| 189 | + "import": "./dist/template-vue-component-lib.js", |
| 190 | + "require": "./dist/template-vue-component-lib.umd.cjs" |
| 191 | + }, |
| 192 | + "./style.css": "./dist/style.css" |
| 193 | + }, |
| 194 | + "types": "./dist/index.d.ts", |
| 195 | + "scripts": { |
| 196 | + "dev": "vite", |
| 197 | + "build": "vite build && vue-tsc --emitDeclarationOnly", |
| 198 | + "preview": "vite preview" |
| 199 | + }, |
| 200 | + "peerDependencies": { |
| 201 | + "vue": ">=3.3.0" |
| 202 | + }, |
| 203 | + "devDependencies": { |
| 204 | + "@vitejs/plugin-vue": "^5.0.4", |
| 205 | + "typescript": "^5.2.2", |
| 206 | + "vite": "^5.1.4", |
| 207 | + "vue-tsc": "^1.8.27" |
| 208 | + } |
| 209 | +} |
| 210 | +``` |
| 211 | + |
| 212 | +A continuación voy a indagar en las propiedades del `package.json` que a efectos de nuestra librería son más importantes, sino no le interesa este apartado mucho más detallado y técnico puede continuar leyendo en la próxima sección: |
| 213 | + |
| 214 | +- **repository**: Aquí se concibe la información del repositorio y el sistema de control de versiones usado. |
| 215 | +- **files**: Este campo opcional es un array de patrones de archivo que describe el contenido que se va a incluir cuando tu paquete se instala como una dependencia. En este caso incluimos la carpeta **dist** que incluye todo el contenido compilado. |
| 216 | +- **main**: A este campo se le asigna el punto de entrada principal a tu programa, el cual es la raíz de la cual se va importar las funciones, objetos o componentes de `Vue`(como en este caso) que necesite el cliente. Aca sería el `javascript` compilado en formato **CommonJS** `./dist/template-vue-component-lib.cjs`. |
| 217 | +- **exports**: Esta propiedad permite declarar qué módulo se debe utilizar al realizar solicitudes de módulos como `import "package"` o `import "package/sub/path"`. Dentro se encuentra los siguientes campos anidados: |
| 218 | + - Se define los tipos (`types`) en caso de usar `Typescript`. |
| 219 | + - El campo`import` define el recurso que se emite desde una solicitud una sintaxis **ESM** o similar. |
| 220 | + - El campo `require` es similar al campo `import` pero en el caso de las solicitudes de una sintaxis de `CommonJs/AMD` o similar. |
| 221 | +- **types**: Aca se indica la ubicación del archivo que contiene la definición de los tipos de nuestra librería (los `.d.ts`). |
| 222 | +- **build**: Este sería el comando que se corre cuando corremos en la consola `npm build`, en este caso primero corremos corremos `vite build` para desplegar nuestro proyecto en la carpeta **dist** y a continuación con el comando `vue-tsc --emitDeclarationOnly` para crear los archivos `.d.ts`. |
| 223 | +- **peerDependencies**: Cuando una dependencia se enumera en un paquete como una `peerDependency`, no se instala automáticamente. En su lugar, el código que incluye el paquete debe ser incluido como su dependencia. `npm` lanzara un mensaje alarma sino encuentra este paquete. En este caso nos es muy útil ya que solo verificamos que el cliente tenga una versión de `vue` ya instalada al usar nuestra librería. Este [articulo](https://flaviocopes.com/npm-peer-dependencies/) lo explica de manera muy amena. |
| 224 | + |
| 225 | +## Publicación de la librería |
| 226 | + |
| 227 | +Para publicar nuestra librería, primero corremos el comando `npm run build` para preparar nuestra librería antes de ser publicada. |
| 228 | +Después sino estas registrado en **NPM** puedes hacerlo a través de la terminal con el comando `npm adduser` y finalmente publicarlo a **NPM** ejecutando `npm publish`. |
| 229 | + |
| 230 | +## Conclusiones |
| 231 | + |
| 232 | +Gracias a las potencialidades de **Vite** podemos crear fácilmente una librería enriquecida con el tipado de **Vite**. Además, podemos adaptar el conocimiento adquirido en este artículo para hacer una librería para **Vanilla**, **React**, **Svelte**, etc. Si tiene alguna duda me puede contactar en [X](https://twitter.com/carlosjorgerc) |
| 233 | +o en [linkedin](https://www.linkedin.com/in/carlosjorger/). |
| 234 | +El repositorio del código mostrado se encuentra aquí <https://github.com/carlosjorger/template-component-lib>. |
0 commit comments