web-dev-qa-db-fra.com

Détection d'environnement: node.js ou navigateur

Je développe une application JS qui doit fonctionner à la fois côté client et côté serveur (en Javascript sur un navigateur et en Node.js), et j'aimerais pouvoir réutiliser les parties du code qui sont utilisé pour les deux côtés.

J'ai découvert que window était une variable accessible uniquement sur les navigateurs et global dans le nœud, donc je peux détecter dans quel environnement le code s'exécute (en supposant qu'aucun script ne déclare le window variable)

Ce sont deux problèmes.

  1. Comment dois-je détecter dans quel navigateur le code est exécuté. Par exemple, ce code est-il OK? (Ce code est en ligne, ce qui signifie qu'il est entouré d'un code global, réutilisé pour les deux environnements)

    if window?
        totalPath= "../examples/#{path}"
    else
        totalPath= "../../examples/#{path}"
    
  2. Comment puis-je utiliser des variables globales pour les deux environnements? Maintenant, je fais ce qui suit, mais cela ne me semble vraiment pas correct.

    if window?
        window.DocUtils = {}
        window.docX = []
        window.docXData= []
    else
        global.DocUtils= {}
        global.docX = []
        global.docXData = []
    
46
edi9999

REMARQUE: Cette question comportait deux parties, mais parce que le titre était "Détection d'environnement: node.js ou navigateur" - j'y arriverai en premier, car je suppose que beaucoup de gens viennent ici pour chercher une réponse à cette. Une question distincte pourrait être de mise.

En JavaScript, les variables peuvent être redéfinies par les portées internes, supposant ainsi que l'environnement n'a pas créé de variables nommées comme processus, global ou window pourrait facilement échouer, par exemple si l'on utilise le module node.js jsdom, le exemple d'utilisation de l'API a

var window = doc.defaultView;

Après quoi, la détection de l'environnement basé sur l'existence de la variable window échouerait systématiquement par tout module fonctionnant sous cette étendue. Avec la même logique, tout code basé sur un navigateur pourrait facilement remplacer global ou process, car ce ne sont pas des variables réservées dans cet environnement.

Heureusement, il existe un moyen d'exiger la portée globale et de tester ce qu'elle est. Si vous créez une nouvelle fonction à l'aide d'un constructeur new Function(), la portée d'exécution de this est liée à la portée globale et vous pouvez comparer directement la portée globale à la valeur attendue. *)

Donc, pour créer une fonction vérifier si la portée globale est "fenêtre" serait

var isBrowser=new Function("try {return this===window;}catch(e){ return false;}");

// tests if global scope is binded to window
if(isBrowser()) console.log("running under browser");

Et la fonction de tester si le sope global est lié au "global" serait

var isNode=new Function("try {return this===global;}catch(e){return false;}");

// tests if global scope is binded to "global"
if(isNode()) console.log("running under node.js");

la partie try ... catch s'assurera que si la variable n'est pas définie, false est retourné.

La isNode() pourrait également comparer this.process.title==="node" Ou une autre variable de portée globale trouvée dans node.js si vous voulez, mais la comparaison avec la globale devrait être suffisante en pratique.

http://jsfiddle.net/p6yedbqk/

[~ # ~] note [~ # ~]: la détection de l'environnement en cours d'exécution n'est pas recommandée . Cependant, il peut être utile dans un environnement spécifique, comme un environnement de développement et de test qui a certaines caractéristiques connues pour la portée mondiale.

Maintenant - la deuxième partie de la réponse. après la détection de l'environnement, vous pouvez sélectionner la stratégie basée sur l'environnement que vous souhaitez utiliser (le cas échéant) pour lier votre variable "globale" à votre application.

À mon avis, la stratégie recommandée serait d'utiliser un modèle singleton pour lier vos paramètres à l'intérieur d'une classe. Il existe déjà une bonne liste d'alternatives dans SO

Manière la plus simple/la plus propre d'implémenter singleton en JavaScript?

Ainsi, cela peut s'avérer si vous n'avez pas besoin d'une variable "globale", et vous n'avez pas du tout besoin de la détection d'environnement, utilisez simplement le modèle singleton pour définir un module, qui stockera les valeurs pour vous. D'accord, on peut affirmer que le module lui-même est une variable globale, ce qui en JavaScript est en fait, mais au moins en théorie, il semble une façon un peu plus propre de le faire.

*) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function

Remarque: Les fonctions créées avec le constructeur Function ne créent pas de fermetures à leurs contextes de création; ils sont toujours créés dans la portée globale. Lors de leur exécution, ils ne pourront accéder qu'à leurs propres variables locales et globales, pas à celles de la portée dans laquelle le constructeur Function a été appelé.

73
Tero Tolonen

Comme apparemment Node.js pourrait avoir les deux (avec NW.js?), Ma façon personnelle de le faire est de détecter si l'entrée node existe dans process.versions objet.

var isNode = false;    
if (typeof process === 'object') {
  if (typeof process.versions === 'object') {
    if (typeof process.versions.node !== 'undefined') {
      isNode = true;
    }
  }
}

Le multiniveau des conditions est d'éviter les erreurs lors de la recherche dans une variable non définie en raison des limitations de certains navigateurs.

Référence: https://nodejs.org/api/process.html#process_process_versions

14
Dan. B.

Vous pouvez attacher à une fenêtre variable ou globale - en fonction de la situation. Bien qu'il ne soit pas recommandé de créer une application JS multiplateforme:

var app = window ? window : global;

Il est préférable d'avoir votre variable globale, qui sera utilisée sur la logique d'application, mais sera composée de parties basées sur différentes plateformes. Quelque chose comme:

var app = {
    env: '',
    agent: ''
};

if (window) {
    app.env = 'browser';
    app.agent = navigator.userAgent;
} else if (process) {
    app.env = 'node';
}

L'idée est donc que votre logique d'application principale sera absolument la même et utilisera le même objet, seul cet objet global doit être modifié en fonction de l'environnement. Cela rend votre application beaucoup plus portable et flexible en termes de plates-formes.

7
moka

Il existe un package npm juste pour cela et il peut être utilisé à la fois côté client et côté serveur.

navigateur ou nœud

Vous pouvez l'utiliser de cette façon

if (isBrowser) {
  // do browser only stuff
}

if (isNode) {
  // do node.js only stuff
}

Avertissement: je suis l'auteur de ce package :)

3
Dinesh Pandiyan

Je sais que c'est une réponse tardive à une question ancienne (1,5 an) mais pourquoi ne pas copier le code source de jQuery ?

if (typeof module === "object" && typeof module.exports === "object") {
  // node
}

if (typeof window !== "undefined" && typeof window.document !== "undefined") {
  // browser
}

Bonne chance.

0
LogicalBranch