Soporte para múltiples marcos en Monorepo
Su tarea, si decide tomarla, es construir un componente Botón en cuatro marcos usando solo uno button.css
¡documento!
Esta idea es muy importante para mí. Estaba trabajando en una biblioteca de componentes llamada Interfaz de usuario agnóstica Su propósito es crear componentes de interfaz de usuario que no dependan de un marco de JavaScript específico. AgnosticUI funciona con React, Vue 3, Angular y Svelte. Esto es exactamente lo que haremos en este artículo de hoy: Cree un componente de botón que funcione con todos estos marcos.
El código fuente de este artículo es Disponible en GitHub existe the-little-button-that-could-series
clon.
contenido
¿Por qué un repositorio?
Construiremos un micro monorepo basado en el espacio de trabajo de hilo. por qué Chris en realidad dio una buena descripción general de los beneficios en otro artículo. Pero aquí está mi propia lista sesgada de beneficios para nuestros esfuerzos de botón pequeño:
conector
Estamos tratando de construir un componente que use solo un botón button.css
Archivos en varios marcos. Entonces, esencialmente, hay algunos específicos conector Entre diferentes implementaciones del marco y un archivo CSS de verdad. El ajuste monorepo proporciona una estructura conveniente para tocar nuestro single button.css
componentes en varios proyectos marco.
proceso de trabajo
Supongamos que el botón debe ajustarse, como una implementación de anillo de enfoque o la cagamos. aria
en la plantilla del componente. Idealmente, nos gustaría solucionar los problemas en un solo lugar, no arreglos individuales en repositorios separados.
prueba
Queríamos ejecutar convenientemente las cuatro implementaciones de botones simultáneamente para realizar pruebas. Con el desarrollo de proyectos de este tipo, es seguro asumir que habrá pruebas más apropiadas. Por ejemplo, en AgnosticUI actualmente uso Storybooks y, a menudo, ejecuto todos los marcos de Storybooks o ejecuto pruebas de instantáneas de todo el monorepo.
lo que me gusta Leonardo Losovitz Debo decir el método de monorepo. (Y sucede que corresponde a todo lo que hemos discutido hasta ahora).
Creo que monorepo es especialmente útil cuando todos los paquetes están codificados en el mismo lenguaje de programación, estrechamente relacionados y dependientes de las mismas herramientas.
configurar
Es hora de profundizar en el código: primero cree un directorio de nivel superior en la línea de comando para contener el proyecto, luego cd
Adelante. (¿No se te ocurre un nombre? mkdir buttons && cd buttons
funcionará bien )
Primero, inicialicemos el proyecto:
$ yarn init
yarn init v1.22.15
question name (articles): littlebutton
question version (1.0.0):
question description: my little button project
question entry point (index.js):
question repository url:
question author (Rob Levin):
question license (MIT):
question private:
success Saved package.json
Esto nos da una package.json
El archivo es el siguiente:
{
"name": "littlebutton",
"version": "1.0.0",
"description": "my little button project",
"main": "index.js",
"author": "Rob Levin",
"license": "MIT"
}
Crear un espacio de trabajo básico
Podemos configurar el primero con:
mkdir -p ./littlebutton-css
Luego, debemos agregar las siguientes dos filas al nivel superior del monorepo package.json
archivo para que podamos mantener el monorepo en privado. También declara nuestro espacio de trabajo:
// ...
"private": true,
"workspaces": ["littlebutton-react", "littlebutton-vue", "littlebutton-svelte", "littlebutton-angular", "littlebutton-css"]
ahora hasta littlebutton-css
contenido. De nuevo querremos generar un package.json
y yarn init
. porque nombramos nuestro directorio littlebutton-css
(con nosotros en nuestro workspaces
existe package.json
) podemos simplemente hacer clic Return
Ingrese y acepte todas las indicaciones:
$ cd ./littlebutton-css && yarn init
yarn init v1.22.15
question name (littlebutton-css):
question version (1.0.0):
question description:
question entry point (index.js):
question repository url:
question author (Rob Levin):
question license (MIT):
question private:
success Saved package.json
En este punto, la estructura del directorio debería verse así:
├── littlebutton-css
│ └── package.json
└── package.json
En este punto, solo creamos el espacio de trabajo de CSS, ya que usaremos una herramienta similar para generar la implementación de nuestro marco. vite
como resultado de un package.json
y un directorio del proyecto para Ud. Debemos recordar que los nombres que elijamos para estos elementos generados deben corresponder a lo que tenemos package.json
por nuestros anteriores workspaces
Me voy a trabajar.
HTML básico y CSS
Vamos a quedarnos ./littlebutton-css
Workspace y cree nuestro componente de botón simple usando archivos HTML y CSS normales.
touch index.html ./css/button.css
Ahora el directorio de nuestro proyecto debería verse así:
littlebutton-css
├── css
│ └── button.css
├── index.html
└── package.json
Continuemos y conectemos algunos puntos con alguna plantilla HTML ./index.html
:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>The Little Button That Could</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://css-tricks.com/make-a-component-multiple-frameworks-in-a-monorepo/css/button.css">
</head>
<body>
<main>
<button class="btn">Go</button>
</main>
</body>
</html>
Y para darnos una pequeña prueba visual, podemos agregar un poco de color ./css/button.css
:
.btn {
color: hotpink;
}
ábrelo ahora index.html
página en el navegador Si ve un botón compartido feo hotpink
Texto... ¡éxito!
Espacios de trabajo específicos de fotogramas
Entonces, lo que acabamos de terminar es la línea principal de nuestro componente de botón. Lo que haremos ahora es abstraerlo un poco para poder extenderlo a otros marcos, y así sucesivamente. Por ejemplo, ¿qué pasa si queremos usar botones en nuestro proyecto React? Necesitaremos tener espacios de trabajo para cada espacio de trabajo en nuestro monorepo. Comenzaremos con React y luego con Vue 3, Angular y Svelte.
reacción
Generaremos nuestro proyecto React con la ayuda de Rápido, constructor muy ligero y extremadamente rápido. Tenga en cuenta que si intenta usar create-react-app
, es probable que encuentre conflictos más adelante react-scripts
y configuraciones conflictivas de paquetes web o Babel de otros marcos como Angular.
Para activar nuestro espacio de trabajo React, volvamos a la terminal y cd
Archivar en un directorio del más alto nivel, de ahí usaremos vite
Inicializar un nuevo proyecto - llamémoslo littlebutton-react
"Por su puesto que lo hare." nosotros elegimos react
Como marco y opción en el aviso:
$ yarn create vite
yarn create v1.22.15
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
success Installed "[email protected]" with binaries:
- create-vite
- cva
✔ Project name: … littlebutton-react
✔ Select a framework: › react
✔ Select a variant: › react
Scaffolding project in /Users/roblevin/workspace/opensource/guest-posts/articles/littlebutton-react...
Done. Now run:
cd littlebutton-react
yarn
yarn dev
✨ Done in 17.90s.
Luego inicializamos la aplicación React con el siguiente comando:
cd littlebutton-react
yarn
yarn dev
Con React instalado y probado, reemplácelo src/App.jsx
Usa el siguiente código para colocar nuestro botón:
import "./App.css";
const Button = () => {
return <button>Go</button>;
};
function App() {
return (
<div className="App">
<Button />
</div>
);
}
export default App;
Ahora escribiremos un pequeño script de nodo para replicar el nuestro. littlebutton-css/css/button.css
Directamente en nuestra aplicación React. Este paso es probablemente el más divertido para mí, porque es tanto mágico como feo. Esto es sorprendente porque significa que nuestro componente de botón React realmente deriva sus estilos del mismo CSS escrito en el proyecto principal. Esto es feo porque llegamos desde un espacio de trabajo y tomamos archivos de otro. ¯ _ (ツ) _ / (
Agregue el siguiente script de nodo pequeño a littlebutton-react/copystyles.js
:
const fs = require("fs");
let css = fs.readFileSync("../littlebutton-css/css/button.css", "utf8");
fs.writeFileSync("./src/button.css", css, "utf8");
pongamos un node
comandos package.json
un escenario que sucedió antes dev
guion en littlebutton-react/package.json
Añadiremos un syncStyles
y actualizar dev
Llamar syncStyles
adelante vite
:
"syncStyles": "node copystyles.js",
"dev": "yarn syncStyles && vite",
Ahora que estamos lanzando nuestra aplicación React yarn dev
, comenzaremos copiando el archivo CSS. En esencia, estamos "obligados" a no desviarnos de los paquetes CSS. button.css
en nuestro botón Reaccionar.
Pero también queremos usar Módulo CSS Para evitar conflictos de nombres y fugas globales de CSS, debemos dar otro paso para conectarlo (desde el mismo littlebutton-react
contenido):
touch src/button.module.css
Luego agregue lo siguiente al nuevo src/button.module.css
documento:
.btn {
composes: btn from './button.css';
}
encontré composes
(también conocido como trabajo) se ha convertido en una de las mejores características de los módulos CSS. En resumen, copiamos nuestra versión HTML/CSS button.css
Venta al por mayor y luego maquillaje de uno de los nuestros. .btn
reglas de estilo
Con esto podemos volver a lo nuestro. src/App.jsx
e importar el módulo CSS styles
En nuestro componente React:
import "./App.css";
import styles from "./button.module.css";
const Button = () => {
return <button className={styles.btn}>Go</button>;
};
function App() {
return (
<div className="App">
<Button />
</div>
);
}
export default App;
¡Vamos! Hagamos una pausa e intentemos ejecutar nuestra aplicación React nuevamente:
yarn dev
Si todo va bien, debería ver el mismo botón común, pero con hotpink
texto. Antes de pasar al siguiente cuadro, regresemos a nuestro directorio monorepo de nivel superior y actualicemos su package.json
:
{
"name": "littlebutton",
"version": "1.0.0",
"description": "toy project",
"main": "index.js",
"author": "Rob Levin",
"license": "MIT",
"private": true,
"workspaces": ["littlebutton-react", "littlebutton-vue", "littlebutton-svelte", "littlebutton-angular"],
"scripts": {
"start:react": "yarn workspace littlebutton-react dev"
}
}
atropellar yarn
Instale amplificadores monorepo desde el comando de directorio de nivel superior.
El único cambio que hicimos en ese package.json
es nuevo scripts
La parte que inicia una aplicación React con un script start:react
ahora podemos correr yarn start:react
Desde nuestro directorio de nivel superior, lanzará el proyecto que acabamos de construir ./littlebutton-react
No hay necesidad cd
'ing - ¡muy conveniente!
Luego hablaremos de Vue y Svelte. Resulta que podemos aplicarles un enfoque muy similar, ya que ambos usan componentes de un solo archivo (SFC). En principio, podemos mezclar HTML, CSS y JavaScript en un solo archivo. Ya sea que le guste o no el enfoque SFC, es lo suficientemente bueno para crear presentaciones o componentes de interfaz de usuario sin formato.
vista
De acuerdo a Documentación de scaffolding de invitación Ejecutaremos los siguientes comandos desde el directorio de nivel superior de monorepo para inicializar la aplicación Vue:
yarn create vite littlebutton-vue --template vue
Esto genera un andamio con algunas instrucciones proporcionadas para ejecutar la aplicación de inicio de Vue:
cd littlebutton-vue
yarn
yarn dev
Esto debería abrir una página de inicio del navegador con un título como "Hello Vue 3 + Vite". Desde aquí podemos actualizar src/App.vue
llega:
<template>
<div id="app">
<Button class="btn">Go</Button>
</div>
</template>
<script>
import Button from './components/Button.vue'
export default {
name: 'App',
components: {
Button
}
}
</script>
reemplazaremos cualquiera src/components/*
y src/components/Button.vue
:
<template>
<button :class="classes"><slot /></button>
</template>
<script>
export default {
name: 'Button',
computed: {
classes() {
return {
[this.$style.btn]: true,
}
}
}
}
</script>
<style module>
.btn {
color: slateblue;
}
</style>
Vamos a desglosarlo:
:class="classes"
utiliza los enlaces de Vue para llamar al cálculoclasses
método.- Esta
classes
los métodos a su vez son explotados Módulos CSS en Vue ythis.$style.btn
utilizará la sintaxis de los estilos contenidos en un<style module>
Etiqueta.
Actualmente estamos codificando duro color: slateblue
Solo para probar si las cosas dentro del componente funcionan correctamente. Intente ejecutar la aplicación nuevamente. yarn dev
Si ve un botón con el color de prueba que declaramos, ¡entonces funciona!
Ahora escribiremos un script de nodo para replicar el nuestro. littlebutton-css/css/button.css
en nuestro Button.vue
Los archivos son similares a lo que hicimos para la implementación de React, como mencionamos, este componente es SFC, por lo que tendremos que usar un simple expresión regular.
Agregue el siguiente script pequeño de Node.js a littlebutton-vue/copystyles.js
:
const fs = require("fs");
let css = fs.readFileSync("../littlebutton-css/css/button.css", "utf8");
const vue = fs.readFileSync("./src/components/Button.vue", "utf8");
// Take everything between the starting and closing style tag and replace
const styleRegex = /<style module>([sS]*?)</style>/;
let withSynchronizedStyles = vue.replace(styleRegex, `<style module>n${css}n</style>`);
fs.writeFileSync("./src/components/Button.vue", withSynchronizedStyles, "utf8");
Este script es un poco complicado, pero se usa replace
Copiar texto entre abrir y cerrar style
Las etiquetas de expresiones regulares no son malas.
Ahora agreguemos los siguientes dos scripts a scripts
en las condiciones littlebutton-vue/package.json
documento:
"syncStyles": "node copystyles.js",
"dev": "yarn syncStyles && vite",
corre ahora yarn syncStyles
Mira ./src/components/Button.vue
de nuevo. Debería ver nuestro módulo de estilo reemplazado por:
<style module>
.btn {
color: hotpink;
}
</style>
Inicie la aplicación Vue nuevamente yarn dev
Y asegúrese de obtener el resultado esperado: sí, un botón de texto de colores vivos. Si es así, ¡estamos emocionados de pasar al siguiente espacio de trabajo en el marco!
Delgado
De acuerdo a documento delgado, tenemos que empezar nuestro littlebutton-svelte
Espacio de trabajo con el siguiente contenido, comenzando desde el directorio de nivel superior de monorepo:
npx degit sveltejs/template littlebutton-svelte
cd littlebutton-svelte
yarn && yarn dev
Confirme que puede hacer clic en la página de inicio "Hello World". http://localhost:5000
Entonces actualiza littlebutton-svelte/src/App.svelte
:
<script>
import Button from './Button.svelte';
</script>
<main>
<Button>Go</Button>
</main>
Además, c. littlebutton-svelte/src/main.js
, queremos eliminar name
accesorios, por lo que se ve así:
import App from './App.svelte';
const app = new App({
target: document.body
});
export default app;
Finalmente agregar littlebutton-svelte/src/Button.svelte
tiene lo siguiente:
<button class="btn">
<slot></slot>
</button>
<script>
</script>
<style>
.btn {
color: saddlebrown;
}
</style>
Una última cosa: Svelte parece estar nombrando el nuestro. solicitud: "name": "svelte-app"
dentro package.json
cambiarlo a "name": "littlebutton-svelte"
Entonces es lo mismo que workspaces
llamado a nuestro más alto nivel package.json
documento.
Podemos repetir nuestra línea de partida de nuevo littlebutton-css/css/button.css
en nuestro Button.svelte
Como mencionamos, este componente es SFC, por lo que tendremos que usarlo expresión regularAgregue el siguiente script de nodo a littlebutton-svelte/copystyles.js
:
const fs = require("fs");
let css = fs.readFileSync("../littlebutton-css/css/button.css", "utf8");
const svelte = fs.readFileSync("./src/Button.svelte", "utf8");
const styleRegex = /<style>([sS]*?)</style>/;
let withSynchronizedStyles = svelte.replace(styleRegex, `<style>n${css}n</style>`);
fs.writeFileSync("./src/Button.svelte", withSynchronizedStyles, "utf8");
Esto es muy similar al script de copia que usamos en Vue, ¿verdad? Agregaremos un script similar a nuestro package.json
texto:
"dev": "yarn syncStyles && rollup -c -w",
"syncStyles": "node copystyles.js",
corre ahora yarn syncStyles && yarn dev
Si todo está bien, necesitamos ver el botón de nuevo. hotpink
texto.
Si esto vuelve a suceder, todo lo que tengo que decir es Bienvenido a mi mundoEl proceso que les muestro aquí es esencialmente el mismo que usé para construir mi propio Interfaz de usuario agnóstica ¡proyecto!
bocina
Probablemente ya conoces el ejercicio, desde el directorio de nivel superior de monorepo instala Angular y Crear una aplicación AngularSi estuviéramos creando una biblioteca de interfaz de usuario completa, podríamos usarla ng generate library
incluso nx
Pero para hacer las cosas lo más simples posible, configuraremos una aplicación Angular estándar como esta:
npm install -g @angular/cli ### unless you already have installed
ng new littlebutton-angular ### choose no for routing and CSS
? Would you like to add Angular routing? (y/N) N
❯ CSS
SCSS [ https://sass-lang.com/documentation/syntax#scss ]
Sass [ https://sass-lang.com/documentation/syntax#the-indented-syntax ]
Less [ http://lesscss.org ]
cd littlebutton-angular && ng serve --open
Después de confirmar la configuración de Angular, actualicemos algunos archivos. cd littlebutton-angular
, Borrar src/app/app.component.spec.ts
archivo y agregarle un componente de botón src/components/button.component.ts
como esto:
import { Component } from '@angular/core';
@Component({
selector: 'little-button',
templateUrl: './button.component.html',
styleUrls: ['./button.component.css'],
})
export class ButtonComponent {}
Agregue lo siguiente a src/components/button.component.html
:
<button class="btn">Go</button>
y ponlo src/components/button.component.css
Archivo de prueba:
.btn {
color: fuchsia;
}
existe src/app/app.module.ts
:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { ButtonComponent } from '../components/button.component';
@NgModule({
declarations: [AppComponent, ButtonComponent],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
Luego reemplaza src/app/app.component.ts
y:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {}
Entonces cambia src/app/app.component.html
y:
<main>
<little-button>Go</little-button>
</main>
Corramos yarn start
y confirma nuestro botón fuchsia
El texto se muestra como se esperaba.
Nuevamente, queremos copiar CSS del espacio de trabajo principal. Podemos hacerlo agregándolo a littlebutton-angular/copystyles.js
:
const fs = require("fs");
let css = fs.readFileSync("../littlebutton-css/css/button.css", "utf8");
fs.writeFileSync("./src/components/button.component.css", css, "utf8");
Angular es bueno porque usa ViewEncapsulation
es el predeterminado emulate
imitar Según la documentación,
[…] El comportamiento de Shadow DOM se logra preprocesando (y renombrando) el código CSS para limitar efectivamente el CSS a la apariencia del componente.
Esto básicamente significa que podemos copiar literalmente button.css
y úsalo como está.
la última actualización package.json
Al agregar estas dos líneas al archivo scripts
parte:
"start": "yarn syncStyles && ng serve",
"syncStyles": "node copystyles.js",
Podemos huir con él ahora. yarn start
Vuelva a comprobar el color del texto de nuestro botón (esto es fuchsia
) Ahora es hotpink
.
¿Qué acabamos de hacer?
Tomemos un descanso de la codificación y miremos el panorama general y lo que acabamos de hacer. Básicamente, configuramos un sistema para realizar cualquier cambio en nuestro paquete CSS. button.css
Se replicará en todas las implementaciones del marco por nuestras razones. copystyles.js
Guión de nodo. Además, hemos incluido convenciones idiomáticas para cada marco:
SFC
Funciona con Vue y SvelteCSS Modules
para React (y Vue en SFC)<style module>
ajustar)ViewEncapsulation
por una esquina
Por supuesto, dejo en claro que estas no son las únicas formas de imponer CSS en cada uno de los marcos anteriores (por ejemplo, CSS-in-JS es una opción popular), pero ciertamente también son prácticas aceptadas para nuestros más antiguos. El objetivo es tener una única fuente de datos CSS para administrar la implementación de todos los marcos.
Por ejemplo, si se usa nuestro botón y nuestro equipo de diseño decide que queremos comenzar 4px
ellos llegan 3px
border-radius
, podemos actualizar el archivo y todas las conversiones individuales permanecerán sincronizadas.
Si tiene un equipo de desarrollo políglota al que le gusta trabajar en varios marcos, o un equipo extranjero (3 veces más productivo en Angular que en Angular) encargado de crear una aplicación de back-end, pero sus productos principales están integrados, entonces esto será muy convincente para responder. O tal vez esté creando una consola de administración temporal y desee probar Vue o Svelte. te dan la imagen.
trabajos de acabado
Bien, entonces tenemos arquitectura monorepo en un lugar realmente agradable. Sin embargo, hay algunas cosas que podemos hacer para que sea más útil en términos de experiencia del desarrollador.
mejores scripts de inicio
Volvamos a nuestro directorio monorepo de nivel superior y actualícelo package.json
scripts
sección con lo siguiente para que podamos prescindir cd
'En g:
// ...
"scripts": {
"start:react": "yarn workspace littlebutton-react dev",
"start:vue": "yarn workspace littlebutton-vue dev ",
"start:svelte": "yarn workspace littlebutton-svelte dev",
"start:angular": "yarn workspace littlebutton-angular start"
},
mejor estilo básico
También podríamos darle al botón un mejor conjunto de estilos básicos para que comience en una posición agradable y neutral. littlebutton-css/css/button.css
documento.
Ver el clip completo
.btn {
--button-dark: #333;
--button-line-height: 1.25rem;
--button-font-size: 1rem;
--button-light: #e9e9e9;
--button-transition-duration: 200ms;
--button-font-stack:
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
Ubuntu,
"Helvetica Neue",
sans-serif;
display: inline-flex;
align-items: center;
justify-content: center;
white-space: nowrap;
user-select: none;
appearance: none;
cursor: pointer;
box-sizing: border-box;
transition-property: all;
transition-duration: var(--button-transition-duration);
color: var(--button-dark);
background-color: var(--button-light);
border-color: var(--button-light);
border-style: solid;
border-width: 1px;
font-family: var(--button-font-stack);
font-weight: 400;
font-size: var(--button-font-size);
line-height: var(--button-line-height);
padding-block-start: 0.5rem;
padding-block-end: 0.5rem;
padding-inline-start: 0.75rem;
padding-inline-end: 0.75rem;
text-decoration: none;
text-align: center;
}
/* Respect users reduced motion preferences */
@media (prefers-reduced-motion) {
.btn {
transition-duration: 0.001ms !important;
}
}
¡Vamos a probarlo! Inicie cada una de las cuatro implementaciones de cuadros con los scripts de inicio nuevos y mejorados y confirme que los cambios de estilo surtieron efecto.
Una actualización de un archivo CSS se expande a cuatro cuadros, ¡genial, eh! ?
establecer el modo básico
agregaremos un mode
Apoya cada uno de nuestros botones y herramientas primary
el próximo modelo. El botón principal puede ser de cualquier color, pero usaremos verde para el fondo y texto blanco. Del mismo modo en la hoja de estilo principal:
.btn {
--button-primary: #14775d;
--button-primary-color: #fff;
/* ... */
}
entonces, justo antes @media (prefers-reduced-motion)
solicitud, agregue lo siguiente btn-primary
a la misma hoja de estilo principal:
.btn-primary {
background-color: var(--button-primary);
border-color: var(--button-primary);
color: var(--button-primary-color);
}
¡vamos! Cierta comodidad para los desarrolladores y una mejor base ¡estilo!
Actualice cada componente para obtener mode
Propiedad
Ahora hemos añadido otros nuevos. primary
modo presentado .btn-primary
class, queremos sincronizar los estilos implementados por los cuatro marcos. Entonces, agreguemos más package.json
Guión a nuestro más alto nivel scripts
:
"sync:react": "yarn workspace littlebutton-react syncStyles",
"sync:vue": "yarn workspace littlebutton-vue syncStyles",
"sync:svelte": "yarn workspace littlebutton-svelte syncStyles",
"sync:angular": "yarn workspace littlebutton-angular syncStyles"
¡Recuerda seguir las reglas de comas de JSON! Depende de dónde coloques estas líneas en tu scripts: {...}
, debe asegurarse de que no falten ni comas finales.
Continúe y ejecute el siguiente comando para sincronizar completamente los estilos:
yarn sync:angular && yarn sync:react && yarn sync:vue && yarn sync:svelte
Ejecutarlo no cambiará nada, ya que aún no hemos implementado la clase base, pero si observa el componente CSS del botón de marco, al menos debería ver que CSS está copiado.
reacción
Si aún no lo ha hecho, verifique nuevamente para ver si el CSS actualizado se ha copiado en littlebutton-react/src/button.css
Si no, puedes ejecutar yarn syncStyles
Tenga en cuenta que si olvida ejecutar yarn syncStyles
nuestro dev
De todos modos, la próxima vez que inicie la aplicación, el script lo hará por nosotros:
"dev": "yarn syncStyles && vite",
Para nuestra implementación de React también necesitamos agregar un composición Las clases de módulos CSS están en littlebutton-react/src/button.module.css
Esto es nuevo .btn-primary
:
.btnPrimary {
composes: btn-primary from './button.css';
}
también actualizaremos littlebutton-react/src/App.jsx
:
import "./App.css";
import styles from "./button.module.css";
const Button = ({ mode }) => {
const primaryClass = mode ? styles[`btn${mode.charAt(0).toUpperCase()}${mode.slice(1)}`] : '';
const classes = primaryClass ? `${styles.btn} ${primaryClass}` : styles.btn;
return <button className={classes}>Go</button>;
};
function App() {
return (
<div className="App">
<Button mode="primary" />
</div>
);
}
export default App;
Inicie la aplicación Reaccionar yarn start:react
del directorio de nivel superior. Si todo salió bien, ahora debería ver su botón verde de inicio.
Como ilustración, mantengo el componente Button dentro App.jsx
En breve. Si esto le preocupa, no dude en organizar el componente Botón en su propio archivo.
vista
Verifique nuevamente si el estilo del botón está copiado, si no, ejecute yarn syncStyles
.
A continuación, sí <script>
parte littlebutton-vue/src/components/Button.vue
:
<script>
export default {
name: 'Button',
props: {
mode: {
type: String,
required: false,
default: '',
validator: (value) => {
const isValid = ['primary'].includes(value);
if (!isValid) {
console.warn(`Allowed types for Button are primary`);
}
return isValid;
},
}
},
computed: {
classes() {
return {
[this.$style.btn]: true,
[this.$style['btn-primary']]: this.mode === 'primary',
}
}
}
}
</script>
Ahora podemos actualizar la etiqueta. littlebutton-vue/src/App.vue
usa uno nuevo mode
pilar:
<Button mode="primary">Go</Button>
ahora usted puede yarn start:vue
Marque el mismo botón verde en el directorio de nivel superior.
Delgado
Nos deja cd
Ingresar littlebutton-svelte
y validar los estilos en littlebutton-svelte/src/Button.svelte
Nuevo .btn-primary
la clase se copia y yarn syncStyles
si necesitas. de nuevo, dev
Si lo olvida accidentalmente, el script lo hará por nosotros la próxima vez que lo carguemos.
Luego actualice la plantilla Svelte para cambiar mode
sobre primary
existe src/App.svelte
:
<script>
import Button from './Button.svelte';
</script>
<main>
<Button mode="primary">Go</Button>
</main>
También necesitamos actualizar nuestro top src/Button.svelte
El componente mismo acepta mode
Mantener e implementar clases de módulos CSS:
<button class="{classes}">
<slot></slot>
</button>
<script>
export let mode = "";
const classes = [
"btn",
mode ? `btn-${mode}` : "",
].filter(cls => cls.length).join(" ");
</script>
Tenga en cuenta, <styles>
La parte del componente Svelte no debe tocarse en este paso.
y ahora puedes yarn dev
desde littlebutton-svelte
(o yarn start:svelte
desde un directorio superior) ¡confirme el éxito del botón verde!
bocina
Lo mismo, marco diferente: asegúrese de que los estilos se copien y ejecuten yarn syncStyles
si necesario.
agreguemos mode
apoya littlebutton-angular/src/app/app.component.html
documento:
<main>
<little-button mode="primary">Go</little-button>
</main>
Ahora tenemos que configurar el enlace a classes
rendimientos compute
La clase correcta se basa en si mode
Se pasa al componente Agregue esto a littlebutton-angular/src/components/button.component.html
(y tenga en cuenta que el enlace se realiza con corchetes):
<button [class]="classes">Go</button>
Entonces en realidad tenemos que crear classes
Conéctese a nuestro componente littlebutton-angular/src/components/button.component.ts
:
import { Component, Input } from '@angular/core';
@Component({
selector: 'little-button',
templateUrl: './button.component.html',
styleUrls: ['./button.component.css'],
})
export class ButtonComponent {
@Input() mode: 'primary' | undefined = undefined;
public get classes(): string {
const modeClass = this.mode ? `btn-${this.mode}` : '';
return [
'btn',
modeClass,
].filter(cl => cl.length).join(' ');
}
}
Usamos Input
se acepta la instruccion mode
props, luego creamos un classes
El accesor, si se transmite, agrega la clase al esquema.
¡Enciéndelo y busca el botón verde!
finalización del código
Si has llegado hasta aquí, ¡felicidades, has terminado con el código! Si algo sale mal, te recomiendo que lo reenvíes. Código fuente de GitHub existe the-little-button-that-could-series
clon. Debido a que los paquetes tienden a cambiar repentinamente, es posible que desee adjuntar la versión del paquete a la versión en esta rama si encuentra problemas de dependencia.
Tómese un momento para dar un paso atrás y comparar las cuatro implementaciones basadas en marcos de los componentes de botones que acabamos de crear. Todavía son lo suficientemente pequeños como para detectar rápidamente algunas diferencias interesantes. accesorios vamos como vamos unir a los accesorios y cómo usar CSS choque de nombres Bloqueado en otros tonos A medida que sigo agregando componentes Interfaz de usuario agnóstica (Admite los cuatro marcos idénticos) y siempre pienso cuál proporciona la mejor experiencia para los desarrolladores. ¿Qué piensas?
Trabajar desde casa
Si eres del tipo al que le gusta resolver problemas por su cuenta o le gusta profundizar más, aquí hay algunas ideas.
estado del botón
El estilo del botón actual no tiene en cuenta los diferentes estados, como :hover
Creo que esta es una buena primera práctica.
/* You should really implement the following states
but I will leave it as an exercise for you to
decide how to and what values to use.
*/
.btn:focus {
/* If you elect to remove the outline, replace it
with another proper affordance and research how
to use transparent outlines to support windows
high contrast
*/
}
.btn:hover { }
.btn:visited { }
.btn:active { }
.btn:disabled { }
Opciones
La mayoría de las bibliotecas de botones admiten muchas variaciones de botones, como el tamaño, la forma y el color. Prueba a crear más primary
El modelo que ya tenemos, tal vez uno secondary
La diversidad es una warning
o success
? Quizás filled
y outline
• Puede ver AgnosticUI de manera similar página de botones ocurrencia.
Propiedades personalizadas de CSS
Si no ha comenzado a usar propiedades CSS personalizadas, lo recomiendo encarecidamente. Puede consultar AgnosticUI primero común estiloConfío mucho en las propiedades personalizadas allí. Aquí hay algunos artículos excelentes sobre qué son los atributos personalizados y cómo aprovecharlos:
Escribe
No... yo no escribo, pero <button>
elemental type
Atributos. No cubrimos esto en nuestro componente, pero es posible extender el componente a otros usos con tipos válidos, como button
, submit
, y reset
Esto es fácil de implementar y mejorará mucho la API del botón.
más ideas
Dios, hay tantas cosas que puedes hacer: agregar un enlace, convertirlo a TypeScript, auditar la accesibilidad, etc.
La implementación actual de Svelte tiene algunos supuestos muy libres, porque si es válido primary
el modo no se transmite; esto crea una clase CSS basura:
mode ? `btn-${mode}` : "",
Puedes decir: "Bueno, .btn-garbage
Porque la clase no es precisamente dañina. "Pero puede ser una buena idea tener un estilo de defensa cuando y donde sea posible.
peligros potenciales
Antes de continuar con este enfoque, debe tener en cuenta algunas cosas:
- El posicionamiento de CSS basado en una estructura de marcado no funciona con la técnica basada en módulos CSS que se usa aquí.
- Angular dificulta las técnicas de posicionamiento durante la construcción
:host
elemento Representa una vista de cada componente. Esto significa que tiene estos elementos adicionales entre sus plantillas o estructuras de etiquetas. Tienes que arreglar esto. - Copiar estilos en paquetes de espacio de trabajo es un poco antimodelo para algunas personas. Lo justifiqué porque creo que los beneficios superan los costos; además, no me siento tan mal con este enfoque cuando pienso en cómo los monorepositorios usan enlaces simbólicos y (menos resistente a fallas) levantamiento.
- Debe suscribirse a la técnica de separación utilizada aquí, por lo que no hay CSS-in-JS.
Creo que todos los métodos de desarrollo de software tienen sus pros y sus contras y, en última instancia, debe decidir si compartir un archivo CSS es apropiado para usted o su proyecto en particular. Por supuesto, hay otras formas de hacerlo (como usar littlebuttons-css
dependiendo del paquete npm) si es necesario.
en conclusión
Espero haber despertado su interés y ahora realmente desea crear una biblioteca con componentes independientes de la interfaz de usuario y/o un sistema de diseño. Tal vez tenga mejores ideas sobre cómo lograr esto. ¡Estaré encantado de escuchar sus pensamientos en los comentarios!
Estoy seguro de que has visto al venerable TodoMVC proyecto y cuántas implementaciones de marco se han creado para él. Además, ¿no sería bueno tener una biblioteca de componentes de interfaz de usuario disponibles para muchos marcos? Abra la interfaz de usuario Se han hecho grandes avances en la estandarización adecuada de la configuración predeterminada para los componentes de nuestra propia interfaz de usuario, pero creo que siempre tendremos que involucrarnos en algún momento. Por supuesto, pasar un año construyendo un sistema de diseño personalizado rápidamente cae en desgracia. y las empresas están cuestionando seriamente el reySe necesita algún tipo de andamiaje para hacer este trabajo.
Visión Interfaz de usuario agnóstica Existe una forma relativamente agnóstica de construir rápidamente sistemas de diseño que no estén limitados por un marco frontal específico. El proyecto es muy temprano y accesible, si se ve obligado a involucrarse, ¡estaré encantado de ayudarlo! Además, después de completar este tutorial, ¡ya está bastante familiarizado con el funcionamiento del proyecto!
Deja una respuesta