2016-03-23 19 views
8

Sto solo cercando di capire come funziona JS, quindi ti prego di avere pazienza con me. Ci sono diversi tipi di moduli nella mia app. Alcuni incapsulano solo i dati, altri gestiscono una parte del DOM. Alcuni moduli dipendono da altri, a volte un modulo dipende dallo stato di più altri moduli, ma non voglio che comunichino direttamente o passino da un modulo all'altro solo per un facile accesso. Ho cercato di creare lo scenario più semplice possibile per illustrare il mio problema (i moduli attuali sono molto più complesse, ovviamente):Mediazione e condivisione dei dati tra diversi moduli

Ho un datamodule che solo espone alcuni dati:

var dataModule = { data: 3 }; 

C'è una configModule che espone modificatori per visualizzare i dati:

var configModule = { factor: 2 }; 

Infine v'è un displayModule che unisce e rende i dati dagli altri due moduli:

var displayModule = { 
    display: function(data, factor) { 
    console.log(data * factor); 
    } 
}; 

Ho anche una semplice implementazione di pub-sub, quindi ho potuto solo mediare tra i moduli di questo tipo:

pubsub.subscribe("init", function() { 
    displayModule.display(dataModule.data, configModule.factor); 
}); 
pubsub.publish("init"); // output: 6 

Tuttavia in questo modo mi sembra di finire con un mediatore che deve sapere tutto delle istanze del modulo esplicitamente - esiste anche un modo per evitarlo? Inoltre, non so come funzionerebbe se ci fossero più istanze di questi moduli. Qual è il modo migliore per evitare le variabili di istanza globali? Credo che la mia domanda sia quale sarebbe il modo più flessibile per gestire qualcosa del genere? Sono sulla buona strada, o è completamente sbagliato? Scusa per non essere molto preciso con la mia domanda, ho solo bisogno di qualcuno che mi spinga nella giusta direzione.

+1

sembra che il tuo init dovrebbe sparare dopo ogni cambiamento, nel qual caso dovrebbe essere chiamato render. guarda nel modello di redux, è fondamentalmente un emettitore di eventi come te, con un evento jolly che re-rende lo stato. – dandavis

+1

_Ma così mi sembra di finire con un mediatore che deve conoscere tutte le istanze del modulo esplicitamente_ No, non lo fai. Perchè pensi questo? La funzione subscribe dovrebbe essere chiamata all'interno del contesto di un modulo. E tutto ciò che pubub deve sapere è richiamare la richiamata che viene data. – sahbeewah

risposta

1

Siete sulla strada giusta, cercherò di dare quella spinta in più si sta parlando:


E si desidera accoppiamento lasco, pub-sub è un buon modo andare.

Ma non è proprio questo "mediatore", ogni modulo dovrebbe idealmente essere autonomo e incapsulare la propria logica.

Questo viene fatto nel modo seguente: ogni modulo dipende dal servizio pubsub, sottoscrive tutti gli eventi rilevanti e agisce su di essi. Ogni modulo pubblica anche eventi che potrebbero essere rilevanti per gli altri (esempi di codice in un minuto, portami con me).

Penso che il bit che potreste mancare qui è che i moduli, che usano gli eventi, difficilmente saranno mai solo modelli semplici. Avranno un po 'di logica e possono anche tenere un modello (che aggiornano quando ricevono eventi).

Così, invece di un dataModule si hanno maggiori probabilità di avere un dataLoaderModule che pubblicherà il modello di dati (ad esempio {data: 3}), una volta che termina il caricamento.

Un altro grande requisito che si imposta è la condivisione dei dati evitando le variabili globali di istanza: questo è un concetto molto importante e anche un passo nella giusta direzione.Quello che ti manca nella soluzione per questo è - Iniezione delle dipendenze o almeno un sistema di moduli che consente di definire le dipendenze.

Vedete, avere un'applicazione guidata da evento non significa necessariamente che ogni parte del codice dovrebbe comunicare utilizzando gli eventi. Un modello di configurazione dell'applicazione o un servizio di utilità è sicuramente qualcosa che vorrei iniettare (quando si utilizza DI, come in Angolare), richiedere (quando si usa AMD/CommonJS) o importare (quando si usano i moduli ES6).
(vale a dire piuttosto che comunicare con un'utilità che utilizza eventi).

Nel tuo esempio non è chiaro se configModule è una configurazione di app statica o una manopola che posso modificare dall'interfaccia utente. Se è una configurazione app statica, la inietterei.

Ora, vediamo alcuni esempi:


Assumendo il seguente:

  • Invece di un dataModule abbiamo un dataLoaderModule
  • configModule è un modello statico configuration.
  • Stiamo usando i moduli AMD (e non i moduli ES6, che preferisco), dal momento che ti vedo bloccato usando solo le funzionalità ES5 (non vedo classi o Sali).

Avremmo:

data-loader.js (aka dataLoaderModule)

define(['pubsub'], function (pubsub) { 
    // ... load data using some logic... 
    // and publish it 
    pubsub.publish('data-loaded', {data: 3}); 
}); 

configuration.js (aka configModule)

define([], function() { 
    return {factor: 2}; 
}); 

display.js (aka displayModule)

define(['configuration', 'pubsub'], function (configuration, pubsub) { 
    var displayModule = { 
     display: function (data, factor) { 
      console.log(data * factor); 
     } 
    }; 

    pubsub.subscribe('data-loaded', function (data) { 
     displayModule.display(data, configuration.factor); 
    }); 
}); 

questo è tutto.

Si noterà che non abbiamo qui alcuna variabile globale (nemmeno pubub), ma invece stiamo richiedendo (o iniettando) le nostre dipendenze.


Qui si potrebbe chiedere: "e cosa succede se ho voluto che la mia configurazione per cambiare dall'interfaccia utente?", quindi vediamo anche questo:

In questo caso, piuttosto rinominare configModule in settingsDisplayModule (seguendo la convenzione di denominazione).

Inoltre, in un'app più realistica, i moduli dell'interfaccia utente solitamente contengono un modello, quindi facciamolo anche noi.

e permette anche loro "punti di vista" invece di "displayModules" chiamare, e avremo:

data-loader.js (aka dataLoaderModule)

define(['pubsub'], function (pubsub) { 
    // ... load data using some logic... 
    // and publish it 
    pubsub.publish('data-loaded', {data: 3}); 
}); 

impostazioni -view.js (aka settingsDisplayModule, aka config)

define(['pubsub'], function (pubsub) { 
    var settingsModel = {factor: 2}; 

    var settingsView = { 
     display: function() { 
      console.log(settingsModel); 

      // and when settings (aka config) changes due to user interaction, 
      // we publish the new settings ... 
      pubsub.publish('setting-changed', settingsModel); 
     } 
    }; 
}); 

data-view.js (aka displayModule)

define(['pubsub'], function (pubsub) { 
    var model = { 
     data: null, 
     factor: 0 
    }; 

    var view = { 
     display: function() { 
      if (model.data && model.factor) { 
       console.log(model.data * model.factor); 
      } else { 
       // whatever you do/show when you don't have data 
      } 
     } 
    }; 

    pubsub.subscribe('data-loaded', function (data) { 
     model.data = data; 
     view.display(); 
    }); 

    pubsub.subscribe('setting-changed', function (settings) { 
     model.factor = settings.factor; 
     view.display(); 
    }); 
}); 

E questo è tutto.

Speranza che aiuta :)

In caso contrario - commento!

+0

Questo è incredibile, esattamente quello che stavo cercando! Grazie per l'aiuto :) – subarachnid

1

Non hai bisogno di un intermediario. Basta importare i dati, configurare e visualizzare e chiamare display(data, config) dove è necessario.

// import data 
// import config 
function render(){ 
    display(data, config) 
} 
Problemi correlati