Udostępniać zmienne między plikami w Node.js?

126

Oto 2 pliki:

// main.js
require('./modules');
console.log(name); // prints "foobar"

// module.js
name = "foobar";

Kiedy nie mam "var", to działa. Ale kiedy mam:

// module.js
var name = "foobar";

nazwa będzie niezdefiniowana w main.js.

Słyszałem, że zmienne globalne są złe i lepiej użyj "var" przed odniesieniami. Ale czy jest to przypadek, w którym zmienne globalne są dobre?

Never_had_a_name
źródło

Odpowiedzi:

184

Zmienne globalne prawie nigdy nie są dobre (może wyjątek lub dwa…). W tym przypadku wygląda na to, że naprawdę chcesz wyeksportować zmienną „name”. Na przykład,

// module.js
var name = "foobar";
// export it
exports.name = name;

Następnie w main.js ...

//main.js
// get a reference to your required module
var myModule = require('./module');

// name is a member of myModule due to the export above
var name = myModule.name;
jmar777
źródło
1
zmienne globalne są złe - całkowicie się z tym zgadzam. Ale mógłbym być, że moduł ma zależność od zmiennej. Czy istnieje sposób na przekazanie tej zmiennej do innego pliku js za pośrednictwem funkcji require?
appsthatmatter
1
@ jjoe64 Nie jestem pewien, co mam na myśli. Możesz skutecznie udostępniać dowolną wartość za pośrednictwem exportsobiektu.
jmar777
7
OP pyta, czy zmienną można zdefiniować w pliku main.js, a następnie użyć w module.js. Mam ten sam wymóg, aby definiować ścieżki, które są używane wielokrotnie.
designermonkey
4
@Designermonkey W takim przypadku prawdopodobnie lepiej będzie mieć obiekt konfiguracyjny z tymi typami wartości, które mogą być również require () 'd w danym pliku. Zauważ, że możesz po prostu zrobić, global.foo = 'bar'a następnie uzyskać dostęp w foodowolnym miejscu ... ale jak powiedziałem w mojej oryginalnej odpowiedzi, prawie nigdy nie jest to dobra rzecz.
jmar777
Dzięki za to, wymyśliłem, jak to zrobić i działa to wspaniale. Dzięki za sprawdzenie, że wpadłem na właściwy pomysł :)
designermonkey
37

Nie mogę znaleźć scenariusza, w którym globalny varjest najlepszą opcją, oczywiście możesz ją mieć, ale spójrz na te przykłady, a być może znajdziesz lepszy sposób na osiągnięcie tego samego:

Scenariusz 1: umieść rzeczy w plikach konfiguracyjnych

Potrzebujesz pewnej wartości, która jest taka sama w całej aplikacji, ale zmienia się w zależności od środowiska (produkcyjnego, deweloperskiego lub testowego), na przykład typu mailera, którego potrzebujesz:

// File: config/environments/production.json
{
    "mailerType": "SMTP",
    "mailerConfig": {
      "service": "Gmail",
      ....
}

i

// File: config/environments/test.json
{
    "mailerType": "Stub",
    "mailerConfig": {
      "error": false
    }
}

(zrób podobną konfigurację również dla programistów)

Aby zdecydować, która konfiguracja zostanie załadowana, utwórz główny plik konfiguracyjny (będzie on używany w całej aplikacji)

// File: config/config.js
var _ = require('underscore');

module.exports = _.extend(
    require(__dirname + '/../config/environments/' + process.env.NODE_ENV + '.json') || {});

A teraz możesz uzyskać takie dane :

// File: server.js
...
var config = require('./config/config');
...
mailer.setTransport(nodemailer.createTransport(config.mailerType, config.mailerConfig));

Scenariusz 2: użyj pliku stałych

// File: constants.js
module.exports = {
  appName: 'My neat app',
  currentAPIVersion: 3
};

I używaj tego w ten sposób

// File: config/routes.js

var constants = require('../constants');

module.exports = function(app, passport, auth) {
  var apiroot = '/api/v' + constants.currentAPIVersion;
...
  app.post(apiroot + '/users', users.create);
...

Scenariusz 3: Użyj funkcji pomocniczej, aby pobrać / ustawić dane

Nie jestem wielkim fanem tego, ale przynajmniej możesz śledzić użycie „nazwy” (cytując przykład PO) i wprowadzić walidacje.

// File: helpers/nameHelper.js

var _name = 'I shall not be null'

exports.getName = function() {
  return _name;
};

exports.setName = function(name) {
  //validate the name...
  _name = name;
};

I użyj go

// File: controllers/users.js

var nameHelper = require('../helpers/nameHelper.js');

exports.create = function(req, res, next) {
  var user = new User();
  user.name = req.body.name || nameHelper.getName();
  ...

Może wystąpić przypadek użycia, gdy nie ma innego rozwiązania niż posiadanie globalnego var, ale zwykle możesz udostępniać dane w swojej aplikacji za pomocą jednego z tych scenariuszy, jeśli zaczynasz używać node.js (tak jak kiedyś) spróbuj uporządkować sposób obsługi danych, ponieważ może to szybko stać się bałaganiarskie.

Felipe Pereira
źródło
Podobał mi się scenariusz 2, ale czy te wartości można zmienić po rozmowie o kompilacji? jak to najczęściej robimy, npm run build. A może znasz jakiś sposób na zmianę wartości po zbudowaniu?
Kashif Ullah
@KashifUllah nie jestem pewien, czy jestem w stanie odpowiedzieć na Twój komentarz, podając tylko podane informacje, możesz dodać nowe pytanie na stronie
Felipe Pereira
16

Jeśli musimy udostępniać wiele zmiennych, użyj poniższego formatu

//module.js
   let name='foobar';
   let city='xyz';
   let company='companyName';

   module.exports={
    name,
    city,
    company
  }

Stosowanie

  // main.js
    require('./modules');
    console.log(name); // print 'foobar'
Vineeth Bhaskaran
źródło
2
tylko krótka uwaga, aby pominąć nieporozumienia, które mogą pojawić się na pierwszym miejscu: należy użyć modułu.exports! jakkolwiek nazywa się twój plik js (np. global.js). moduł jest obiektem węzła, który istnieje w zasięgu globalnym! [więc w global.js używamy module.exports = .....]
Mohamed Allal,
zakończy się sukcesem, jeśli usuniesz „let” i nie ma potrzeby, aby „module.exports ..”
Ahmad Zahabi
6

Zapisz dowolną zmienną, która ma być udostępniana jako jeden obiekt. Następnie przekaż go do załadowanego modułu, aby mógł uzyskać dostęp do zmiennej poprzez odniesienie do obiektu.

// main.js
var myModule = require('./module.js');
var shares = {value:123};

// Initialize module and pass the shareable object
myModule.init(shares);

// The value was changed from init2 on the other file
console.log(shares.value); // 789

W innym pliku ...

// module.js
var shared = null;

function init2(){
    console.log(shared.value); // 123
    shared.value = 789;
}

module.exports = {
    init:function(obj){
        // Save the shared object on current module
        shared = obj;

        // Call something outside
        init2();
    }
}
StefansArya
źródło
1

zmienna zadeklarowana ze słowem kluczowym var lub bez niego została dołączona do obiektu globalnego. To jest podstawa tworzenia zmiennych globalnych w Node poprzez deklarowanie zmiennych bez słowa kluczowego var. Podczas gdy zmienne zadeklarowane za pomocą słowa kluczowego var pozostają lokalne dla modułu.

zobacz ten artykuł, aby uzyskać więcej informacji - https://www.hacksparrow.com/global-variables-in-node-js.html

criz
źródło
3
Czy te fragmenty są sprzeczne? 1) „zmienna zadeklarowana ze słowem kluczowym var lub bez niego została dołączona do obiektu globalnego”. oraz 2) „zmienne zadeklarowane za pomocą słowa kluczowego var pozostają lokalne dla modułu”.
BaldEagle
1

Mając inne zdanie, myślę, że globalzmienne mogą być najlepszym wyborem, jeśli zamierzasz opublikować swój kod npm, ponieważ nie możesz być pewien, że wszystkie pakiety używają tego samego wydania kodu. Więc jeśli używasz pliku do eksportu singletonobiektu, spowoduje to tutaj problemy.

Można wybrać global, require.mainczy wszelkie inne przedmioty, które są wspólne dla plików.

Proszę powiedz mi, czy są jakieś lepsze rozwiązania.

LCB
źródło