Para compartir datos entre módulos, un patrón habitual es encapsular los datos en un módulo común e importarlos en otros módulos.
En mi caso, los datos que se compartirán son un registrador, que deben inicializarse antes de usarse. Llamo a init()
en el punto de entrada de mi solicitud.
// main.js
let logger = require('@my/logger');
logger.init({...});
let xxx = require('./moduleX');
let yyy = require('./moduleY');
En otros módulos, se puede usar el registrador inicializado:
// moduleX.js
let logger = require('@my/logger');
const log = logger('moduleX');
function x() {
log.debug('some msg');
}
El código anterior funciona bien en node.js. Pero si cambio a la sintaxis del módulo ES6, no funciona porque la importación del módulo ES6 está activada.
// main.js
import {logger} from '@my/logger';
logger.init({...}); // this line is run after import moduleX
import {x} from './moduleX';
// moduleX.js
import {logger} from '@my/logger';
const log = logger('moduleX'); // logger is not initialized !
export function x() {
log.debug('some msg');
}
Con el módulo ES6, ¿cómo puedo inicializar algunos datos y compartirlos con otros módulos?
Hubo una pregunta similar, pero la respuesta no se ajusta a mi caso.
Actualizar:
Algunas respuestas sugieren poner en funcionamiento el código que accede a los datos compartidos para que el código no se invoque inmediatamente al cargar el módulo. Pero, ¿qué sucede si realmente necesito acceder a él durante la carga del módulo? Actualicé mi código para demostrar el caso de uso: sería demasiado trivial llamar a logger(name)
en cada función si no se hace log
como constante de alcance del módulo.
5 respuestas
Finalmente lo resuelvo de la manera que @PaoloMoretti mencionó en su comentario.
Escribir un módulo en mi aplicación para iniciar el registrador para mi aplicación:
// logger_init.js
import {logger} from '@my/logger';
logger.init({...});
Importe el módulo de inicialización una vez en el punto de entrada de la aplicación, antes de importar cualquier otro módulo que también use el registrador. Garantiza que la inicialización se realice antes de cargar otros módulos.
// main.js
import './logger_init';
import {x} from '@my/other_module';
import {y} from './module_in_myapp';
Otros módulos pueden usar el registrador inicializado directamente:
// @my/other_module
import {logger} from '@my/logger';
const log = logger('moduleX'); // logger has been initialized
export function x() {
log.debug('some msg');
}
El árbol de dependencias es:
<init>
myapp --+--> logger_init ------------> @my/logger
| <use> ↑
+--> module_in_myapp -----------+
| <use> |
+--> @my/other_module ----------+
La razón por la que no adopto la forma en que agrego un módulo contenedor que inicia y devuelve un registrador (como la respuesta de Bergi) se debe a que los módulos que utilizan el registrador podrían ser módulos reutilizables que no están en mi aplicación.
Un registrador, que debe inicializarse antes de usarse. ¿Qué sucede si necesito acceder a él durante la carga del módulo?
Cree un módulo adicional con un registrador inicializado:
// main.js
import {x as xxx} from './moduleX';
import {y as yyy} from './moduleY';
// logger_ready.js
import {logger} from '@my/logger';
logger.init({…});
export default logger;
// moduleX.js
import logger from 'logger_ready';
const log = logger('moduleX'); // logger is initialized here!
export function x() {
log.debug('some msg');
}
Después de los experimentos, descubrí que el BroadcastChannel api es perfecto para compartir datos o eventos fácilmente entre diferentes módulos es6, pestañas, trabajadores, marcos ...
Podemos pasar objetos o matrices usando json stringify allí también:
Para enviar datos:
new BroadcastChannel('myapp_section8').postMessage('Hello world!')
Ejemplo de recepción, desde otro módulo:
new BroadcastChannel('myapp_section8').addEventListener('message', function(e){
console.log(e.data)
})
Evita usar cualquier elemento DOM para dispatchEvent , simplifica mucho las cosas .
Intente proporcionar algunos puntos de entrada a sus módulos xxx.js y aaa.js o incluso hágalos como funciones.
// main.js
import {logger} from '@my/logger';
import * as xxx from './xxx';
logger.init({...});
xxx.run();
// xxx.js
import {logger} from '@my/logger';
export function run () {
logger.debug('In xxx');
}
Puede hacer que cada módulo que depende de alguna rutina de inicialización devuelva una función que puede ejecutarse manualmente para invocar su funcionalidad, en lugar de exponer esa funcionalidad como un efecto secundario inmediato de importarla. Por ejemplo, en xxx.js
:
// xxx.js
export default function (log) {
log('xxx');
}
Luego coloque todas las operaciones de inicialización dentro de un archivo de entrada e invoque los módulos definidos de la manera anterior después de que estas operaciones estén completas:
// main.js
import xxx from './xxx';
import {logger} from '@my/logger';
logger.init({...});
xxx(logger);
Pero, ¿qué sucede si realmente necesito acceder a él durante la carga del módulo?
Consulte los ejemplos de código enmendado anteriores. Pase la instancia de logger
a la función exportada por cada módulo.
Preguntas relacionadas
Nuevas preguntas
javascript
Para preguntas relacionadas con la programación en ECMAScript (JavaScript / JS) y sus diversos dialectos / implementaciones (excluyendo ActionScript). Esta etiqueta rara vez se usa sola, pero a menudo se asocia con las etiquetas [node.js], [json] y [html].