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.
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}"
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 = []
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.
[~ # ~] 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é.
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
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.
Il existe un package npm juste pour cela et il peut être utilisé à la fois côté client et côté serveur.
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 :)
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.