node.js design-patterns require

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; anteriormente exports.foo = foo; sintaxis en lugar de asignar module.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 o exports 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"