23

我有几个包含辅助函数的实用程序库,我想加载它们以便可以从控制器中使用它们,我想知道在 node.js 中编写实用程序库的最佳实践是什么。

我有点困惑,因为有几种方法可以做到这一点,我不确定什么是最好/更合适/更可靠的。这里有 2 个选项,但我想知道它们是否是最好的(例如,我见过使用 的片段module.exports = exports = function(){}等)

//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');
4

2 回答 2

40

我认为我的文件分为 3 个部分:

第 1 节:CommonJS 依赖项

var lib1 = require("lib1");
var lib2 = require("lib2");

您不需要任何额外的包装函数。所有节点模块都由 node.js自动包装在一个函数中,这样做没有任何好处,只会增加混乱

第 2 部分:纯 JavaScript 代码

如果需要,这应该几乎完全是带有支持变量或顶级模块代码的函数。

var MY_CONST = 42;

function helper1() {
    //excellent code here
}

function helper2() {
    //excellent code here
}

保留第 2 节纯 JS。不要在这个中间的“纯”部分使用 commonJS 成语。不要使用module, exports,require等。这只是我个人的指导方针,因为 JS 本身是稳定的,但是打包到模块中仍然有很多变化,最好将那些无关的且可能发生变化的 CommonJS 位与有趣的代码。ECMAScript 6 模块最有可能在几年内取代 CommonJS,所以通过保留第 2 节纯 ECMAScript 5 并制作一个我喜欢称之为“CommonJS Sandwich™”的方式,让自己更容易做到这一点。

第 3 节:CommonJS 导出

exports.helper1 = helper1;
exports.helper2 = helper2;
  • 将所有导出放在最后还可以让您快速了解公共 API 是什么,并防止由于不小心复制/粘贴而意外导出属性。
  • 我更喜欢上面的exports.foo = foo;语法,而不是分配module.exports给一个新的对象文字。我发现这避免了对象文字的最后一个属性的尾随逗号问题。
  • require用你的or语句做任何其他事情exports几乎肯定是没有必要的,而且是不必要的花哨或魔法。在你进阶之前,不要在这里做任何花哨的事情。(即使那样,如果你不是 TJ Holowaychuk,你可能只是在傻)

我应该导出什么

单个函数(@substack 样式)

function degreesToRadians(degrees) {}

module.exports = degreesToRadians;

保持小而简单。

函数对象

如果你的模块是一组辅助函数,你应该导出一个包含这些函数的对象作为属性

var foo = require("foo");

function doubleFoo(value) {
  return foo(value) * 2;
}

function tripleFoo(value) {
  return foo(value) * 3;
}

exports.doubleFoo = doubleFoo;
exports.tripleFoo = tripleFoo;

构造函数

如果您的模块是面向对象使用的类设计,请导出构造函数

function GoCart() {
  this.wheels = 4;
}

GoCart.prototype.drive = function drive() {
  //vroom vroom
}

module.exports = GoCart;

工厂/配置关闭功能

一旦你掌握了上述 2 种模式(真的!)并且有信心导出一个带有选项的工厂函数,并且可能会做一些其他动态的东西,那就去做吧,但是如果有疑问,坚持前 2 个更简单的选择。

//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;

所以你可以像这样使用

var doStuff = require("./do-stuff")({howFast: "blazing speed"});
console.log(doStuff.doStuff("jogging"));
//"I am doing jogging at speed blazing speed"
于 2013-07-30T02:03:02.340 回答
2

对于无状态实用程序库,option2.js是可行的方法,因为不需要module.exports每次都执行in 的代码require,这就是调用require('./option1.js')()使用option1.js时发生的情况。

另一方面,如果您的模块暴露了创建持有状态的对象的构造函数,那么您将需要option1.js。例如,

人.js:

module.exports = function(firstName,lastName){
    this.firstName = firstName;
    this.lastName = lastName;
};

然后使用它:

var Person = require("./person");

var person1 = new Person("Foo","Bar");
var person2 = new Person("Joe","Shmoe");

option1.js的另一个优点(如上所示)是它允许您将参数传递到模块中。

于 2013-07-30T02:00:29.903 回答