Las directivas export e import tienen varias formas de sintaxis.
En el artculo anterior vimos un uso simple, ahora exploremos ms ejemplos.
Export antes de las sentencias
Podemos exportar cualquier declaracin colocando antes export, ya sea a una variable, funcin o clase.
Por ejemplo, aqu todas las exportaciones son vlidas:
// exportar un array
export let months = ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
// exportar una constante
export const MODULES_BECAME_STANDARD_YEAR = 2015;
// exportar una clase
export class User {
constructor(name) {
this.name = name;
}
}
Tenga en cuenta que export antes de una clase o una funcin no la hace una expresin de funcin. Sigue siendo una declaracin de funcin, aunque exportada.
La mayora de las guas de estilos JavaScript no recomiendan punto y coma despus de declarar funciones y clases.
Es por esto que no hay necesidad de un punto y coma al final de export class y export function:
export function sayHi(user) {
alert(`Hello, ${user}!`);
} // sin ; al final
Export separado de la declaracin
Tambin podemos colocar export por separado.
Aqu primero declaramos y luego exportamos:
// say.js
function sayHi(user) {
alert(`Hello, ${user}!`);
}
function sayBye(user) {
alert(`Bye, ${user}!`);
}
export {sayHi, sayBye}; // una lista de variables exportadas
O, tcnicamente podemos colocar export arriba de las funciones tambin.
Import *
Generalmente, colocamos una lista de lo que queremos importar en llaves import {...}, de esta manera:
// main.js
import {sayHi, sayBye} from './say.js';
sayHi('John'); // Hello, John!
sayBye('John'); // Bye, John!
Pero si hay mucho para importar, podemos importar todo como un objeto utilizando import * as <obj>, por ejemplo:
// main.js
import * as say from './say.js';
say.sayHi('John');
say.sayBye('John');
A primera vista, importar todo parece algo tan genial, corto de escribir, por qu deberamos listar explcitamente lo que necesitamos importar?
Pues hay algunas razones.
- Listar explcitamente qu importar da nombres ms cortos:
sayHi()en lugar desay.sayHi(). - La lista explcita de importaciones ofrece una mejor visin general de la estructura del cdigo: qu se usa y dnde. Facilita el soporte de cdigo y la refactorizacin.
Las herramientas de empaquetado modernas, como webpack y otras, unen los mdulos, optimizan la velocidad de carga, y eliminan las importaciones no usadas.
Por ejemplo, si importas import * as library desde una librera de cdigo enorme, y usas solo unos pocos mtodos, los que no se usen no sern incluidos en el paquete optimizado.
Importar as
Tambin podemos utilizar as para importar bajo nombres diferentes.
Por ejemplo, importemos sayHi en la variable local hi para brevedad, e importar sayBye como bye:
// main.js
import {sayHi as hi, sayBye as bye} from './say.js';
hi('John'); // Hello, John!
bye('John'); // Bye, John!
Exportar as
Existe un sintaxis similar para export.
Exportemos funciones como hi y bye:
// say.js
...
export {sayHi as hi, sayBye as bye};
Ahora hi y bye son los nombres oficiales exportados, los que usarn otros mdulos al hacer sus importaciones.
// main.js
import * as say from './say.js';
say.hi('John'); // Hello, John!
say.bye('John'); // Bye, John!
Export default
En la prctica, existen principalmente dos tipos de mdulos.
- Mdulos que contienen una librera, un paquete de funciones como
say.jsde arriba. - Mdulos que declaran una entidad simple, por ejemplo un mdulo
user.jsque exporta nicamenteclass User.
Principalmente, se prefiere el segundo enfoque, de modo que cada cosa reside en su propio mdulo.
Naturalmente, eso requiere muchos archivos, ya que todo quiere su propio mdulo, pero eso no es un problema en absoluto. En realidad, la navegacin de cdigo se vuelve ms fcil si los archivos estn bien nombrados y estructurados en carpetas.
Los mdulos proporcionan una sintaxis especial export default (la exportacin predeterminada) para que la forma de una cosa por mdulo se vea mejor.
Poner export default antes de la entidad a exportar:
// user.js
export default class User { // slo agregar "default"
constructor(name) {
this.name = name;
}
}
Slo puede existir un slo export default por archivo.
Y luego importarlo sin llaves:
// main.js
import User from './user.js'; // no {User}, slo User
new User('John');
Las importaciones sin llaves se ven mejor. Un error comn al comenzar a usar mdulos es olvidarse de las llaves. Entonces, recuerde, import necesita llaves para las exportaciones con nombre y no las necesita para la predeterminada.
| Export con nombre | Export predeterminada |
|---|---|
export class User {...} |
export default class User {...} |
import {User} from ... |
import User from ... |
Tcnicamente, podemos tener exportaciones predeterminadas y con nombre en un solo mdulo, pero en la prctica la gente generalmente no las mezcla. Un mdulo tiene exportaciones con nombre o la predeterminada.
Como puede haber como mximo una exportacin predeterminada por archivo, la entidad exportada puede no tener nombre.
Por ejemplo, todas estas son exportaciones predeterminadas perfectamente vlidas:
export default class { // sin nombre de clase
constructor() { ... }
}
export default function(user) { // sin nombre de funcin
alert(`Hello, ${user}!`);
}
// exportar un nico valor, sin crear una variable
export default ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
No dar un nombre est bien, porque solo hay un export default por archivo, por lo que import sin llaves sabe qu importar.
Sin default, dicha exportacin dara un error:
export class { // Error! (exportacin no predeterminada necesita un nombre)
constructor() {}
}
El nombre default
En algunas situaciones, la palabra clave default se usa para hacer referencia a la exportacin predeterminada.
Por ejemplo, para exportar una funcin por separado de su definicin:
function sayHi(user) {
alert(`Hello, ${user}!`);
}
// lo mismo que si agregamos "export default" antes de la funcin
export {sayHi as default};
Otra situacin, supongamos un mdulo user.js exporta una cosa principal default, y algunas cosas con nombre (raro el caso, pero sucede):
// user.js
export default class User {
constructor(name) {
this.name = name;
}
}
export function sayHi(user) {
alert(`Hello, ${user}!`);
}
Aqu la manera de importar la exportacin predeterminada junto con la exportacin con nombre:
// main.js
import {default as User, sayHi} from './user.js';
new User('John');
Y por ltimo, si importamos todo * como un objeto, entonce la propiedad default es exactamente la exportacin predeterminada:
// main.js
import * as user from './user.js';
let User = user.default; // la exportacin predeterminada
new User('John');
Unas palabras contra exportaciones predeterminadas
Las exportaciones con nombre son explcitas. Nombran exactamente lo que importan, as que tenemos esa informacin de ellos; Eso es bueno.
Las exportaciones con nombre nos obligan a usar exactamente el nombre correcto para importar:
import {User} from './user.js';
// import {MyUser} no funcionar, el nombre debe ser {User}
Mientras que para una exportacin predeterminada siempre elegimos el nombre al importar:
import User from './user.js'; // funciona
import MyUser from './user.js'; // tambin funciona
// puede ser import Cualquiera... y aun funcionara
Por lo tanto, los miembros del equipo pueden usar diferentes nombres para importar lo mismo, y eso no es bueno.
Por lo general, para evitar eso y mantener el cdigo consistente, existe una regla que establece que las variables importadas deben corresponder a los nombres de los archivos, por ejemplo:
import User from './user.js';
import LoginForm from './loginForm.js';
import func from '/path/to/func.js';
...
An as, algunos equipos lo consideran un serio inconveniente de las exportaciones predeterminadas. Por lo tanto, prefieren usar siempre exportaciones con nombre. Incluso si solo se exporta una sola cosa, todava se exporta con un nombre, sin default.
Eso tambin hace que la reexportacin (ver ms abajo) sea un poco ms fcil.
Reexportacin
La sintaxis Reexportar export ... from ... permite importar cosas e inmediatamente exportarlas (posiblemente bajo otro nombre), de esta manera:
export {sayHi} from './say.js'; // reexportar sayHi
export {default as User} from './user.js'; // reexportar default
Por qu se necesitara eso? Veamos un caso de uso prctico.
Imagine que estamos escribiendo un paquete: una carpeta con muchos mdulos, con algunas de las funciones exportadas al exterior (herramientas como NPM nos permiten publicar y distribuir dichos paquetes pero no estamos obligados a usarlas), y muchos mdulos son solo ayudantes, para uso interno en otros mdulos de paquete.
La estructura de archivos podra ser algo as:
auth/
index.js
user.js
helpers.js
tests/
login.js
providers/
github.js
facebook.js
...
Nos gustara exponer la funcionalidad del paquete a travs de un nico punto de entrada.
En otras palabras, una persona que quiera usar nuestro paquete, debera importar solamente el archivo principal auth/index.js.
Como esto:
import {login, logout} from 'auth/index.js'
El archivo principal, auth/index.js, exporta toda la funcionalidad que queremos brindar en nuestro paquete.
La idea es que los desarrolladores que usen nuestro paquete no deban lidiar con su estructura interna, buscar archivos dentro de nuestra carpeta de paquetes. Exportamos solo lo que es necesario en auth/index.js y mantenemos el resto oculto a miradas indiscretas.
Como la funcionalidad real exportada se encuentra dispersa entre el paquete, podemos importarla en auth/index.js y exportar desde ella:
// auth/index.js
// importar login/logout e inmediatamente exportarlas
import {login, logout} from './helpers.js';
export {login, logout};
// importar default como User y exportarlo
import User from './user.js';
export {User};
...
Ahora los usuarios de nuestro paquete pueden hacer esto import {login} from "auth/index.js".
La sintaxis export ... from ... es solo una notacin ms corta para tal importacin-exportacin:
// auth/index.js
// re-exportar login/logout
export {login, logout} from './helpers.js';
// re-exportar export default como User
export {default as User} from './user.js';
...
La diferencia notable de export ... from comparado a import/export es que los mdulos re-exportados no estn disponibles en el archivo actual. Entonces en el ejemplo anterior de auth/index.js no podemos usar las funciones re-exportadas login/logout.
Reexportando la exportacin predeterminada
La exportacin predeterminada necesita un manejo separado cuando se reexporta.
Digamos que tenemos user.js con export default class User, y nos gustara volver a exportar la clase User de l:
// user.js
export default class User {
// ...
}
Podemos tener dos problemas:
-
export User from './user.js'no funcionar. Nos dar un error de sintaxis.Para reexportar la exportacin predeterminada, tenemos que escribir
export {default as User}, tal como en el ejemplo de arriba. -
export * from './user.js'reexporta nicamente las exportaciones con nombre, pero ignora la exportacin predeterminada.Si queremos reexportar tanto la exportacin con nombre como la predeterminada, se necesitan dos declaraciones:
export * from './user.js'; // para reexportar exportaciones con nombre export {default} from './user.js'; // para reexportar la exportacin predeterminada
Tales rarezas de reexportar la exportacin predeterminada son una de las razones por las que a algunos desarrolladores no les gustan las exportaciones predeterminadas y prefieren exportaciones con nombre.
Resumen
Aqu estn todos los tipos de exportacin que cubrimos en este y en artculos anteriores.
Puede comprobarlo al leerlos y recordar lo que significan:
- Antes de la declaracin de clase/funcin/:
export [default] clase/funcin/variable ...
- Export independiente:
export {x [as y], ...}.
- Reexportar:
export {x [as y], ...} from "module"export * from "module"(no reexporta la predeterminada).export {default [as y]} from "module"(reexporta la predeterminada).
Importacin:
- Importa las exportaciones con nombre:
import {x [as y], ...} from "module"
- Importa la exportacin predeterminada:
import x from "module"import {default as x} from "module"
- Importa todo:
import * as obj from "module"
- Importa el mdulo (su cdigo se ejecuta), pero no asigna ninguna de las exportaciones a variables:
import "module"
Podemos poner las declaraciones import/export en la parte superior o inferior de un script, eso no importa.
Entonces, tcnicamente este cdigo est bien:
sayHi();
// ...
import {sayHi} from './say.js'; // import al final del archivo
En la prctica, las importaciones generalmente se encuentran al comienzo del archivo, pero eso es solo para mayor comodidad.
Tenga en cuenta que import y export solo funcionan en el nivel superior del mdulo, no dentro de ningn bloque {...}.
Una importacin condicional como esta no funcionar:
if (something) {
import {sayHi} from "./say.js"; // Error: import debe estar en nivel superior
}
Pero qu pasa si realmente necesitamos importar algo condicionalmente? O en el momento adecuado? Por ejemplo, cargar un mdulo a pedido, cuando realmente se necesita.
Veremos importaciones dinmicas en el prximo artculo.
Comentarios
<code>, para varias lneas envolverlas en la etiqueta<pre>, para ms de 10 lneas utilice una entorno controlado (sandbox) (plnkr, jsbin, codepen)