Es una situación común: tiene dos componentes que son bastante similares, comparten la misma funcionalidad básica, pero hay suficientes diferencias en cada uno de ellos que llega a una encrucijada: ¿divido este componente en dos componentes diferentes? ¿O me quedo con un componente, pero creo suficiente variación con los accesorios para poder modificar cada uno?
Ninguna de estas soluciones es perfecta: si lo divide en dos componentes, corre el riesgo de tener que actualizarlo en dos lugares si la funcionalidad cambia alguna vez, derrotando las premisas de DRY. Por otro lado, demasiados accesorios pueden ensuciarse muy rápidamente y obligar al mantenedor, incluso si es usted mismo, a comprender mucho contexto para usarlo, lo que puede ralentizarlo.
Introduzca mixins. Los mixins en Vue son útiles para escribir en un estilo funcional porque, en última instancia, la programación funcional se trata de hacer que el código sea comprensible al reducir las partes móviles. (Hay una gran cita de Michael Feathers sobre esto). Un mixin le permite encapsular una pieza de funcionalidad para que pueda usarla en diferentes componentes a lo largo de la aplicación. Si se escriben correctamente, son puros: no modifican ni cambian cosas fuera del alcance de la función, por lo que siempre recibirá de manera confiable el mismo valor con las mismas entradas en múltiples ejecuciones. Esto puede ser realmente poderoso.
Ejemplo basico
Digamos que tenemos un par de componentes diferentes cuyo trabajo es alternar un estado booleano, un modal y una información sobre herramientas. Estas descripciones emergentes y modales no tienen mucho en común excepto por esa funcionalidad: no tienen el mismo aspecto, no se usan igual, pero la lógica es similar.
//modal
const Modal = {
template: '#modal',
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
},
components: {
appChild: Child
}
}
//tooltip
const Tooltip = {
template: '#tooltip',
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
},
components: {
appChild: Child
}
}
Podríamos extraer la lógica aquí y crear algo que se pueda reutilizar:
const toggle = {
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
}
}
const Modal = {
template: '#modal',
mixins: [toggle],
components: {
appChild: Child
}
};
const Tooltip = {
template: '#tooltip',
mixins: [toggle],
components: {
appChild: Child
}
};
Vea el Pen Mixin de Sarah Drasner (@sdras) en CodePen.
Este ejemplo se mantuvo intencionalmente pequeño y simple para fines de legibilidad; se incluyen ejemplos de combinaciones que he encontrado útiles en aplicaciones de la vida real, pero no se limitan a: obtener dimensiones de la ventana gráfica y el componente, recopilar eventos específicos de mousemove y elementos base de gráficos . Paul Pflugradt tiene un buen repositorio de Vue Mixins, pero vale la pena mencionar que están escritos en coffeescript.
Uso
Este lápiz no muestra realmente cómo lo configuraríamos en una aplicación real, así que veamos eso a continuación.
Puede configurar la estructura de su directorio de la forma que desee, pero me gusta crear un directorio de mezcla para mantenerme organizado. El archivo que crearíamos tendría un .js
extensión (a diferencia de .vue
, como nuestros otros archivos), y exportaríamos un objeto para el mixin:
Y luego en Modal.vue ahora tendríamos acceso a él importando el conmutador de esta manera:
import Child from './Child'
import { toggle } from './mixins/toggle'
export default {
name: 'modal',
mixins: [toggle],
components: {
appChild: Child
}
}
Es importante comprender que, aunque estamos usando un objeto y no un componente, los métodos del ciclo de vida todavía están disponibles para nosotros. Podríamos engancharnos mounted()
aquí y se aplicaría al ciclo de vida del componente, lo que hace que esta forma de trabajar sea realmente flexible y potente.
Fusionando
Mirando el último ejemplo, podemos ver que no solo tenemos nuestra funcionalidad, sino también ganchos de ciclo de vida disponibles para nosotros desde el mixin, por lo que cuando lo aplicamos a un componente con procesos superpuestos, el pedido es importante. De forma predeterminada, los mixins se aplicarán primero y el componente se aplicará en segundo lugar para que podamos anularlo según sea necesario. El componente tiene la última palabra. Esto solo se vuelve realmente importante cuando hay un conflicto y el componente tiene que “decidir” cuál gana, de lo contrario, todo se colocará en una matriz para ejecutarse y el mixin se presionará primero y el componente en segundo lugar.
//mixin
const hi = {
mounted() {
console.log('hello from mixin!')
}
}
//vue instance or component
new Vue({
el: '#app',
mixins: [hi],
mounted() {
console.log('hello from Vue instance!')
}
});
//Output in console
> hello from mixin!
> hello from Vue instance!
Si los dos entran en conflicto, podemos ver cómo ganará la instancia o el componente de Vue:
//mixin
const hi = {
methods: {
sayHello: function() {
console.log('hello from mixin!')
}
},
mounted() {
this.sayHello()
}
}
//vue instance or component
new Vue({
el: '#app',
mixins: [hi],
methods: {
sayHello: function() {
console.log('hello from Vue instance!')
}
},
mounted() {
this.sayHello()
}
})
// Output in console
> hello from Vue instance!
> hello from Vue instance!
Puede notar que tenemos dos console.log
s para la cadena de instancia de Vue en lugar de una aquí, eso se debe a que la primera función que se llamó no se destruyó, se anuló. Todavía estamos llamando a los dos sayHello()
funciones aquí.
Mixins globales
Cuando usamos el término global en referencia a mixins, no nos referimos a poder acceder a ellos en todos los componentes, como lo hacemos con algo así como filtros. Ya podemos acceder a nuestros mixins en un componente con mixins: [toggle]
.
Los mixins globales se aplican literalmente a cada componente. Por esta razón, el caso de uso para ellos es extremadamente limitado y deben considerarse con gran precaución. Un uso en el que puedo pensar que tiene sentido es algo así como un complemento, donde es posible que necesite obtener acceso a todo. Pero nuevamente, incluso en este caso, desconfiaría de lo que está aplicando, especialmente cuando está extendiendo la funcionalidad a aplicaciones que podrían ser una caja negra para usted.
Para crear una instancia global, la colocaríamos encima de la instancia de Vue. En una compilación típica de Vue-cli, esto iría en su main.js
Archivo.
Vue.mixin({
mounted() {
console.log('hello from mixin!')
}
})
new Vue({
...
})
Nuevamente, ¡usa esto con precaución! Ese console.log
ahora aparecería en cada componente. Esto no es tan malo en este caso (aparte de todo el ruido en la consola) pero puede ver cuán potencialmente dañino podría ser si se usa incorrectamente.
Conclusión
Los mixins pueden ser útiles para encapsular una pequeña parte de la funcionalidad que le gustaría reutilizar. Ciertamente, no son la única opción disponible para usted: los componentes de orden superior, por ejemplo, le permiten componer una funcionalidad similar, esta es solo una forma de trabajar. Me gustan los mixins porque no tenemos que pasar el estado, pero este patrón ciertamente también se puede abusar, así que tenga cuidado de pensar qué opción tiene más sentido para su aplicación.