Prácticas recomendadas para estructurar bibliotecas que se requieren en node.js
design-patterns require (2)
Tengo varias bibliotecas de utilidades que contienen funciones auxiliares y quiero cargarlas para poder usarlas desde los controladores y me pregunto cuál es la mejor práctica para codificar bibliotecas de utilidades en el nodo.
Estoy un poco confundido porque hay varias formas de hacerlo y no estoy seguro de cuál es la mejor / más adecuada / más confiable. Aquí hay 2 opciones, pero me pregunto si son las mejores (por ejemplo, he visto fragmentos que utilizan module.exports = exports = function(){}
, etc.)
//option1.js
"use strict";
module.exports = function(){
exports.test1 = function(){ console.log(''hi I''m test1'')};
exports.test2 = function(){ console.log(''hi I''m test2'')};
return exports;
};
//option2.js
"use strict";
module.exports = {
test1 : function(){ console.log(''soy test1'')},
test2 : function(){ console.log(''soy test2'')}
};
//test_controller.js
/* Requiring helpers in different ways */
var option1 = require(''./option1.js'')();
var option2 = require(''./option2.js'');
Para las bibliotecas de utilidades sin estado, option2.js es el camino a seguir porque no es necesario que el código en module.exports
se ejecute cada vez que lo require
, que es lo que sucede cuando se require(''./option1.js'')()
call require(''./option1.js'')()
para utilizar option1.js .
Si, por otro lado, sus módulos expusieron constructores que crearon objetos que mantenían el estado, entonces usted querría option1.js . Por ejemplo,
persona.js:
module.exports = function(firstName,lastName){
this.firstName = firstName;
this.lastName = lastName;
};
y luego usarlo:
var Person = require("./person");
var person1 = new Person("Foo","Bar");
var person2 = new Person("Joe","Shmoe");
La otra ventaja de option1.js (como se demostró anteriormente) es que le permite pasar parámetros al módulo.
Pienso en mis archivos en 3 secciones:
Sección 1: Dependencias de CommonJS
var lib1 = require("lib1");
var lib2 = require("lib2");
No necesitas funciones de envoltorio adicionales. Todos los módulos de nodo se envuelven automáticamente por node.js en una función y al hacerlo no tiene beneficios y solo agrega desorden
Sección 2: Código JavaScript simple
Esto debería ser casi exclusivamente funciones con una rociada de variables de apoyo o código de módulo de nivel superior si es necesario.
var MY_CONST = 42;
function helper1() {
//excellent code here
}
function helper2() {
//excellent code here
}
Mantener la sección 2 pura JS. No use expresiones comunes en esta sección media "pura". No use el module
, exports
, require
, etc. Esta es solo mi pauta personal ya que JS es estable, pero el empaquetado de los módulos aún está bajo muchos cambios y es mejor mantener los bits de CommonJS que son extraños y es probable que cambien separado de los bits interesantes de código. Es probable que los módulos ECMAScript 6 reemplacen a CommonJS en unos pocos años, así que haga esto más fácil para usted manteniendo el ECMAScript 5 puro de la sección 2 y haciendo un "Sandwich ™ CommonJS" como me gusta llamarlo.
Sección 3: Exportaciones CommonJS
exports.helper1 = helper1;
exports.helper2 = helper2;
- Poner todas sus exportaciones al final también le brinda una manera rápida de entender cuál es su API pública y evita la exportación accidental de propiedades debido a una copia / pegado descuidado.
- Yo prefiero las
exports.foo = foo;
anteriormenteexports.foo = foo;
sintaxis en lugar de asignarmodule.exports
a un nuevo objeto literal. Encuentro que esto evita el problema de coma final con la última propiedad de un objeto literal. - Hacer cualquier otra cosa con sus declaraciones de
require
oexports
es casi seguramente innecesario y innecesariamente ingenioso o mágico. Hasta que llegues a ser avanzado, no hagas nada lujoso aquí. (incluso entonces, si no eres TJ Holowaychuk, probablemente solo estás siendo tonto)
¿Qué debo exportar?
Una sola función (estilo @substack)
function degreesToRadians(degrees) {}
module.exports = degreesToRadians;
Mantenlo pequeño y simple.
Un objeto de funciones.
Si su módulo es un conjunto de funciones auxiliares, debe exportar un objeto que contenga esas funciones como propiedades
var foo = require("foo");
function doubleFoo(value) {
return foo(value) * 2;
}
function tripleFoo(value) {
return foo(value) * 3;
}
exports.doubleFoo = doubleFoo;
exports.tripleFoo = tripleFoo;
Una función constructora
Si su módulo es un diseño de clase para uso orientado a objetos, exporte la función constructora
function GoCart() {
this.wheels = 4;
}
GoCart.prototype.drive = function drive() {
//vroom vroom
}
module.exports = GoCart;
Una función de cierre de fábrica / configuración
Una vez que haya dominado los 2 patrones anteriores (¡en serio!) Y se sienta confiado al exportar una función de fábrica que toma opciones y tal vez realiza otras cosas dinámicas, inténtelo, pero en caso de duda, continúe con las primeras 2 opciones más simples.
//do-stuff.js
function doStuff(howFast, what) {
return "I am doing " + what + " at speed " + howFast;
}
function setup(options) {
//The object returned by this will have closure access to options
//for its entire lifetime
return {doStuff: doStuff.bind(null, options.howFast)};
}
module.exports = setup;
Así que podrías usar eso como
var doStuff = require("./do-stuff")({howFast: "blazing speed"});
console.log(doStuff.doStuff("jogging"));
//"I am doing jogging at speed blazing speed"