Як у "Node.js" я можу "включити" функції з інших файлів?


967

Скажімо, у мене є файл під назвою app.js. Досить просто:

var express = require('express');
var app = express.createServer();
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.get('/', function(req, res){
  res.render('index', {locals: {
    title: 'NowJS + Express Example'
  }});
});

app.listen(8080);

Що робити, якщо у мене є функції всередині "tools.js". Як я можу імпортувати їх для використання в apps.js?

Або ... я повинен перетворити "інструменти" в модуль, а потім вимагати цього? << здається важким, я радше основний імпорт файлу tools.js.


4
Що мене тут відкинуло - requireце папка в тому самому каталозі Windows. Ви повинні використовувати адресацію в стилі unix: ./mydirзамість простої старої mydir.
Бен

1
Я створив модуль для імпорту скриптів, експорту у файл та включення модуля із зовнішньої node_modulesпапки. npmjs.com/package/node-import Сподіваюся, що це може допомогти. Дякую!
Нананг Махден Ель-Агунг

Відповіді:


1414

Ви можете вимагати будь-якого js-файлу, просто потрібно оголосити, що ви хочете викрити.

// tools.js
// ========
module.exports = {
  foo: function () {
    // whatever
  },
  bar: function () {
    // whatever
  }
};

var zemba = function () {
}

І у вашому файлі програми:

// app.js
// ======
var tools = require('./tools');
console.log(typeof tools.foo); // => 'function'
console.log(typeof tools.bar); // => 'function'
console.log(typeof tools.zemba); // => undefined

101
+1 Чудово виконано, навіть обмежує імпортований код у власному просторі імен. Мені доведеться зазначити це на потім.
Еван Плейс

8
Цікаво, чи можливо імпортувати зовнішні сценарії. require("http://javascript-modules.googlecode.com/svn/functionChecker.js")здається, що модуль не імпортується правильно. Чи є інший спосіб імпортувати зовнішні сценарії?
Андерсон Грін

6
А що, якщо мені доведеться перейти змінну у функцію Like, bar: function (a, b) {// деякий код}
Nishutosh Sharma

5
Оскільки ви експонуєте властивості, я б використав експорт замість module.exports. Для експорту проти module.exports: stackoverflow.com/questions/5311334 / ...
Ферма

4
як викликати функцію бар (), функцію всередині Foo (), засіб , як доступ до однієї функції з п іншим
Піт

303

Якщо, незважаючи на всі інші відповіді, ви все ще хочете традиційно включати файл у вихідний файл node.js, ви можете використовувати це:

var fs = require('fs');

// file is included here:
eval(fs.readFileSync('tools.js')+'');
  • Зв'язування порожнього рядка +''необхідне для отримання вмісту файлу як рядка, а не об'єкта (ви також можете використовувати, .toString()якщо бажаєте).
  • Евал () не можна використовувати всередині функції, і його потрібно викликати всередині глобальної області, інакше жодні функції чи змінні не будуть доступними (тобто ви не можете створити функцію include()утиліти чи щось подібне).

Зверніть увагу, що в більшості випадків це погана практика, і вам слід замість цього написати модуль . Однак бувають рідкісні ситуації, коли забруднення вашого локального контексту / простору імен - те, чого ви дійсно хочете.

Оновити 2015-08-06

Зауважте також, що це не буде працювати "use strict";(коли ви перебуваєте в "суворому режимі" ), оскільки функції та змінні, визначені у "імпортованому" файлі, не можуть отримати доступ до коду, який імпортує. Суворий режим застосовує деякі правила, визначені новішими версіями мовного стандарту. Це може бути ще однією причиною уникати описаного тут рішення.


41
Прикольно, це корисно для швидкого введення JS-ліфтів, призначених для клієнта, у додаток node.js, не потребуючи підтримки вилки, стилізованої до Вузла.
Кос

18
Я щойно відповів на початкове запитання, яке стосується включення коду, а не написання модулів. Перші можуть мати переваги в певних ситуаціях. Крім того, ваше припущення про вимогу є помилковим: код, безумовно, eval'd, але він залишається у власному просторі імен і не має ніякого способу "забруднити" простір імен виклику контексту, отже, вам потрібно його оцінювати (). У більшості випадків використання методу, описаного в моїй передпорядці, є поганою практикою, але не я повинен вирішувати, чи є це для TIMEX.
Udo G

14
@EvanPlaice: чи є краща пропозиція, яка насправді відповідає на питання ? Якщо вам потрібно включити файл, який не є модулем , чи є у вас кращий підхід, ніж цей?
джелф

8
Іноді, коли потрібно включати, а іноді і вимагати, це дві принципово різні концепції в більшості мов програмування, також Node JS. Можливість включити js на місце повинна бути частиною Node, щоб бути чесним, але це, по суті, є гідним рішенням. Отримано.
Дж. Мартін

4
Зауважте, що це несумісно із строгим використанням - оскільки строгий режим використання обмежує використання eval, блокуючи введення нових змінних через eval тощо.
timbo

189

Вам не потрібно ні нових функцій, ні нових модулів. Вам просто потрібно виконати модуль, який ви телефонуєте, якщо ви не хочете використовувати простір імен.

в tools.js

module.exports = function() { 
    this.sum = function(a,b) { return a+b };
    this.multiply = function(a,b) { return a*b };
    //etc
}

в app.js

або в будь-якому іншому .js, наприклад, myController.js:

замість

var tools = require('tools.js') які змушують нас використовувати простір імен та такі засоби виклику tools.sum(1,2);

ми можемо просто зателефонувати

require('tools.js')();

і потім

sum(1,2);

у моєму випадку у мене є файл з контролерами ctrls.js

module.exports = function() {
    this.Categories = require('categories.js');
}

і я можу використовуватись Categoriesу будь-якому контексті як публічний клас післяrequire('ctrls.js')()


12
Як це не має більше +1? Це справжнє рішення того, що задається питанням (хоч і не «офіційним»). Це також в мільйон разів простіше налагодити, ніж eval (), оскільки вузол може надати корисний стек викликів, а не вказувати на фактичний файл, а не просто визначений.
користувач3413723

5
Зверніть увагу, що ви не можете "використовувати" суворий "режим в імпортованому модулі.
Девід

1
@ Нік Панов: блискучий! Варто зазначити, що це працює, оскільки thisфункція - це глобальна область, коли функція викликається безпосередньо (не пов'язана жодним чином).
Udo G

3
це просто змінило моє життя, без жарту - був файл рядка 1000+, який я не міг розбити, тому що змінні різних методів взаємопов'язані між собою і потрібно, щоб усі були в одному і тому ж просторі ... require('blah.js')();повинен дозвольте імпортувати їх усіх в той же обсяг !!! Дякую!!!
Сем Джонсон

2
Це чудова порада! Застереження: якщо ви оголосите функцію module.exports за допомогою синтаксису швидкого доступу () => {}, а не стандартної функції () {}, вона не спрацьовує. Мені потрібна година, щоб зрозуміти, де проблема! (У функцій стрілок немає власного "цього" властивості: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… )
Ryan Griggs

117

Створіть два js-файли

// File cal.js
module.exports = {
    sum: function(a,b) {
        return a+b
    },
    multiply: function(a,b) {
        return a*b
    }
};

Основний файл js

// File app.js
var tools = require("./cal.js");
var value = tools.sum(10,20);
console.log("Value: "+value);

Вихід консолі

Value: 30

У якій версії js це працює?
Мірв - Метт

39

Ось просте і просте пояснення:

Вміст Server.js:

// Include the public functions from 'helpers.js'
var helpers = require('./helpers');

// Let's assume this is the data which comes from the database or somewhere else
var databaseName = 'Walter';
var databaseSurname = 'Heisenberg';

// Use the function from 'helpers.js' in the main file, which is server.js
var fullname = helpers.concatenateNames(databaseName, databaseSurname);

Вміст Helpers.js:

// 'module.exports' is a node.JS specific feature, it does not work with regular JavaScript
module.exports = 
{
  // This is the function which will be called in the main file, which is server.js
  // The parameters 'name' and 'surname' will be provided inside the function
  // when the function is called in the main file.
  // Example: concatenameNames('John,'Doe');
  concatenateNames: function (name, surname) 
  {
     var wholeName = name + " " + surname;

     return wholeName;
  },

  sampleFunctionTwo: function () 
  {

  }
};

// Private variables and functions which will not be accessible outside this file
var privateFunction = function () 
{
};

34

Я також шукав функцію "включати" NodeJS, і я перевірив рішення, запропоноване Udo G - див. Повідомлення https://stackoverflow.com/a/8744519/2979590 . Його код не працює з моїми включеними файлами JS. Нарешті я вирішив проблему так:

var fs = require("fs");

function read(f) {
  return fs.readFileSync(f).toString();
}
function include(f) {
  eval.apply(global, [read(f)]);
}

include('somefile_with_some_declarations.js');

Звичайно, це допомагає.


2
Я розумію, наскільки це некрасиво, але це точно допомогло мені.
lindhe

30

створити два файли, наприклад, app.jsіtools.js

app.js

const tools= require("./tools.js")


var x = tools.add(4,2) ;

var y = tools.subtract(4,2);


console.log(x);
console.log(y);

tools.js

 const add = function(x, y){
        return x+y;
    }
 const subtract = function(x, y){
            return x-y;
    }

    module.exports ={
        add,subtract
    }

вихід

6
2

26

скажімо, ми хочемо викликати функцію ping () та додати (30,20), яка знаходиться у файлі lib.js від main.js

main.js

lib = require("./lib.js")

output = lib.ping();
console.log(output);

//Passing Parameters
console.log("Sum of A and B = " + lib.add(20,30))

lib.js

this.ping=function ()
{
    return  "Ping Success"
}
//Functions with parameters
this.add=function(a,b)
    {
        return a+b
    }

1
Це працює, але чи не слід використовувати синтаксис модулів при включенні сценаріїв?
Кокодоко

25

Модуль vm в Node.js забезпечує можливість виконання коду JavaScript у поточному контексті (включаючи глобальний об'єкт). Дивіться http://nodejs.org/docs/latest/api/vm.html#vm_vm_runinthiscontext_code_filename

Зауважте, що станом на сьогодні в модулі vm є помилка, яка заважає runInThisContext виконувати право, коли викликається з нового контексту. Це має значення лише в тому випадку, якщо ваша основна програма виконує код у новому контексті, а потім цей код викликає runInThisContext. Дивіться https://github.com/joyent/node/isissue/898

На жаль, підхід (глобальний), який запропонував Фернандо, не працює для названих функцій, таких як "function foo () {}"

Коротше кажучи, ось функція include (), яка працює для мене:

function include(path) {
    var code = fs.readFileSync(path, 'utf-8');
    vm.runInThisContext(code, path);
}

Я знайшов vm.runInThisContext в іншій відповіді ТА і використовував його для включення файлів коду Javascript "vanilla". Тоді я намагався використати його для включення коду, який залежав від функціональності вузла (наприклад, "var fs = requ ('fs')"), і він не працював. Однак у цьому випадку рішення (-и), відзначене в парі відповідей, дійсно працює.
Dexygen

Продумавши це ще трохи, коли вам потрібно буде включити код, який залежить від функціональності вузла, можливо, саме час написати модуль, хоча рішення eval може бути першим кроком у цьому процесі
Dexygen

Лише той, хто працював у node.js у 2019 році
meesern

13

Удо Г. сказав:

  • Евал () не можна використовувати всередині функції, і його потрібно викликати всередині глобальної області, інакше жодні функції чи змінні не будуть доступними (тобто ви не можете створити функцію утиліти include () або щось подібне).

Він правий, але є спосіб вплинути на глобальну сферу використання функції. Удосконалення його прикладу:

function include(file_) {
    with (global) {
        eval(fs.readFileSync(file_) + '');
    };
};

include('somefile_with_some_declarations.js');

// the declarations are now accessible here.

Сподіваюся, це допомагає.


12

Зі мною це працювало, як і наступне….

Lib1.js

//Any other private code here 

// Code you want to export
exports.function1 = function(params) {.......};
exports.function2 = function(params) {.......};

// Again any private code

тепер у файл Main.js потрібно включити Lib1.js

var mylib = requires('lib1.js');
mylib.function1(params);
mylib.function2(params);

Будь ласка, не забудьте помістити Lib1.js у папку node_modules .


11

Ви можете розмістити свої функції в глобальних змінних, але краще практично просто перетворити скрипт інструментів у модуль. Це насправді не надто складно - просто приєднайте свій громадський API до exportsоб’єкта. Подивіться на експорт модуля Node.js РОЗУМІННЯ для деяких більш докладно.


1
Приклад - кращий, ніж посилання
tno2007

11

Ще один спосіб зробити це, на мою думку, - це виконати все, що знаходиться у файлі lib, коли ви викликаєте функцію requ () за допомогою (function (/ * things here * /) {}) (); роблячи це зробить всі ці функції глобального масштабу, так само , як Eval) ( рішення

src / lib.js

(function () {
    funcOne = function() {
            console.log('mlt funcOne here');
    }

    funcThree = function(firstName) {
            console.log(firstName, 'calls funcThree here');
    }

    name = "Mulatinho";
    myobject = {
            title: 'Node.JS is cool',
            funcFour: function() {
                    return console.log('internal funcFour() called here');
            }
    }
})();

А потім у своєму головному коді ви можете зателефонувати вашим функціям за назвою:

main.js

require('./src/lib')
funcOne();
funcThree('Alex');
console.log(name);
console.log(myobject);
console.log(myobject.funcFour());

Зробимо цей вихід

bash-3.2$ node -v
v7.2.1
bash-3.2$ node main.js 
mlt funcOne here
Alex calls funcThree here
Mulatinho
{ title: 'Node.JS is cool', funcFour: [Function: funcFour] }
internal funcFour() called here
undefined

Зверніть увагу на невизначені, коли ви телефонуєте на мій object.funcFour () , це буде те саме, якщо ви завантажуєте eval () . Сподіваюся, це допомагає :)


10

Я просто хочу додати, якщо вам потрібні лише певні функції, імпортовані з вашого tools.js , тоді ви можете використовувати завдання деструктуризації, яке підтримується в node.js з версії 6.4 - див. Node.green .


Приклад : (обидва файли знаходяться в одній папці)

tools.js

module.exports = {
    sum: function(a,b) {
        return a + b;
    },
    isEven: function(a) {
        return a % 2 == 0;
    }
};

main.js

const { isEven } = require('./tools.js');

console.log(isEven(10));

вихід: true


Це також дозволяє уникнути призначення цих функцій як властивостей іншого об'єкта, як це має місце в наступному (загальному) призначенні:

const tools = require('./tools.js');

куди потрібно дзвонити tools.isEven(10).


ПРИМІТКА:

Не забудьте префіксувати своє ім’я файлу правильним шляхом - навіть якщо обидва файли знаходяться в одній папці, вам потрібно встановити префікс із ./

З документів Node.js :

Без провідних '/', './' або '../' для вказівки файлу, модуль повинен бути або основним модулем, або завантажений із папки node_modules.


10

app.js

let { func_name } = require('path_to_tools.js');
func_name();    //function calling

tools.js

let func_name = function() {
    ...
    //function body
    ...
};

module.exports = { func_name };

3

Включіть файл і запустіть його у заданому (не глобальному) контексті

fileToInclude.js

define({
    "data": "XYZ"
});

main.js

var fs = require("fs");
var vm = require("vm");

function include(path, context) {
    var code = fs.readFileSync(path, 'utf-8');
    vm.runInContext(code, vm.createContext(context));
}


// Include file

var customContext = {
    "define": function (data) {
        console.log(data);
    }
};
include('./fileToInclude.js', customContext);

2

Це найкращий спосіб, який я створив поки що.

var fs = require('fs'),
    includedFiles_ = {};

global.include = function (fileName) {
  var sys = require('sys');
  sys.puts('Loading file: ' + fileName);
  var ev = require(fileName);
  for (var prop in ev) {
    global[prop] = ev[prop];
  }
  includedFiles_[fileName] = true;
};

global.includeOnce = function (fileName) {
  if (!includedFiles_[fileName]) {
    include(fileName);
  }
};

global.includeFolderOnce = function (folder) {
  var file, fileName,
      sys = require('sys'),
      files = fs.readdirSync(folder);

  var getFileName = function(str) {
        var splited = str.split('.');
        splited.pop();
        return splited.join('.');
      },
      getExtension = function(str) {
        var splited = str.split('.');
        return splited[splited.length - 1];
      };

  for (var i = 0; i < files.length; i++) {
    file = files[i];
    if (getExtension(file) === 'js') {
      fileName = getFileName(file);
      try {
        includeOnce(folder + '/' + file);
      } catch (err) {
        // if (ext.vars) {
        //   console.log(ext.vars.dump(err));
        // } else {
        sys.puts(err);
        // }
      }
    }
  }
};

includeFolderOnce('./extensions');
includeOnce('./bin/Lara.js');

var lara = new Lara();

Ще потрібно повідомити, що ви хочете експортувати

includeOnce('./bin/WebServer.js');

function Lara() {
  this.webServer = new WebServer();
  this.webServer.start();
}

Lara.prototype.webServer = null;

module.exports.Lara = Lara;

2

Наче у вас є файл abc.txt та багато іншого?

Створіть 2 файли: fileread.jsі fetchingfile.js, потім fileread.jsнапишіть цей код:

function fileread(filename) {
    var contents= fs.readFileSync(filename);
        return contents;
    }

    var fs = require("fs");  // file system

    //var data = fileread("abc.txt");
    module.exports.fileread = fileread;
    //data.say();
    //console.log(data.toString());
}

У fetchingfile.jsзаписи цього коду:

function myerror(){
    console.log("Hey need some help");
    console.log("type file=abc.txt");
}

var ags = require("minimist")(process.argv.slice(2), { string: "file" });
if(ags.help || !ags.file) {
    myerror();
    process.exit(1);
}
var hello = require("./fileread.js");
var data = hello.fileread(ags.file);  // importing module here 
console.log(data.toString());

Тепер у терміналі: $ node fetchingfile.js --file = abc.txt

Ви передаєте ім'я файлу як аргумент, до того ж включайте всі файли в readfile.js а не передаючи їх.

Дякую


2

Можна просто, просто require('./filename') .

Напр.

// file: index.js
var express = require('express');
var app = express();
var child = require('./child');
app.use('/child', child);
app.get('/', function (req, res) {
  res.send('parent');
});
app.listen(process.env.PORT, function () {
  console.log('Example app listening on port '+process.env.PORT+'!');
});
// file: child.js
var express = require('express'),
child = express.Router();
console.log('child');
child.get('/child', function(req, res){
  res.send('Child2');
});
child.get('/', function(req, res){
  res.send('Child');
});

module.exports = child;

Будь ласка, зверніть увагу, що:

  1. ви не можете слухати PORT у дочірньому файлі, лише батьківський експрес-модуль має слухача PORT
  2. Дитина використовує "Маршрутизатор", а не батьківську швидку мишкою.

1

Я також шукав варіант включити код без написання модулів, відповідно. використовувати ті самі перевірені автономні джерела з іншого проекту для сервісу Node.js - та jmparatte зробила це для мене.

Перевага в тому, що ви не забруднюєте простір імен, у мене немає проблем "use strict"; і це працює добре.

Тут повний зразок:

Сценарій для завантаження - /lib/foo.js

"use strict";

(function(){

    var Foo = function(e){
        this.foo = e;
    }

    Foo.prototype.x = 1;

    return Foo;

}())

SampleModule - index.js

"use strict";

const fs = require('fs');
const path = require('path');

var SampleModule = module.exports = {

    instAFoo: function(){
        var Foo = eval.apply(
            this, [fs.readFileSync(path.join(__dirname, '/lib/foo.js')).toString()]
        );
        var instance = new Foo('bar');
        console.log(instance.foo); // 'bar'
        console.log(instance.x); // '1'
    }

}

Сподіваюся, це якось допомогло.


1

Інший метод при використанні рамки node.js і express.js

var f1 = function(){
   console.log("f1");
}
var f2 = function(){
   console.log("f2");
}

module.exports = {
   f1 : f1,
   f2 : f2
}

збережіть це у js-файлі з ім'ям s та в статиці папки

Тепер використовувати функцію

var s = require('../statics/s');
s.f1();
s.f2();

1

Я придумав досить грубий метод поводження з цим для шаблонів HTML. Аналогічно PHP<?php include("navigation.html"); ?>

server.js

var fs = require('fs');

String.prototype.filter = function(search,replace){
    var regex = new RegExp("{{" + search.toUpperCase() + "}}","ig");
    return this.replace(regex,replace);
}

var navigation = fs.readFileSync(__dirname + "/parts/navigation.html");

function preProcessPage(html){
    return html.filter("nav",navigation);
}

var express = require('express');
var app = express();
// Keep your server directory safe.
app.use(express.static(__dirname + '/public/'));
// Sorta a server-side .htaccess call I suppose.
app.get("/page_name/",function(req,res){
    var html = fs.readFileSync(__dirname + "/pages/page_name.html");
    res.send(preProcessPage(html));
});

page_name.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>NodeJS Templated Page</title>
    <link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="/css/font-awesome.min.css">
    <!-- Scripts Load After Page -->
    <script type="text/javascript" src="/js/jquery.min.js"></script>
    <script type="text/javascript" src="/js/tether.min.js"></script>
    <script type="text/javascript" src="/js/bootstrap.min.js"></script>
</head>
<body>
    {{NAV}}
    <!-- Page Specific Content Below Here-->
</body>
</html>

navigation.html

<nav></nav>

Результат завантаженої сторінки

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>NodeJS Templated Page</title>
    <link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="/css/font-awesome.min.css">
    <!-- Scripts Load After Page -->
    <script type="text/javascript" src="/js/jquery.min.js"></script>
    <script type="text/javascript" src="/js/tether.min.js"></script>
    <script type="text/javascript" src="/js/bootstrap.min.js"></script>
</head>
<body>
    <nav></nav>
    <!-- Page Specific Content Below Here-->
</body>
</html>

0

Якщо ви хочете скористатися кількома процесорами та архітектурою мікросервісу, щоб прискорити роботу ... Використовуйте RPC над роздвоєними процесами.

Звучить складно, але це просто, якщо ви використовуєте восьминога .

Ось приклад:

на tools.js додати:

const octopus = require('octopus');
var rpc = new octopus('tools:tool1');

rpc.over(process, 'processRemote');

var sum = rpc.command('sum'); // This is the example tool.js function to make available in app.js

sum.provide(function (data) { // This is the function body
    return data.a + data.b;
});

на app.js додайте:

const { fork } = require('child_process');
const octopus = require('octopus');
const toolprocess = fork('tools.js');

var rpc = new octopus('parent:parent1');
rpc.over(toolprocess, 'processRemote');

var sum = rpc.command('sum');

// Calling the tool.js sum function from app.js
sum.call('tools:*', {
    a:2, 
    b:3
})
.then((res)=>console.log('response : ',rpc.parseResponses(res)[0].response));

розкриття - Я є автором восьминога, і створив, якби для подібного шаблону мій, оскільки я не міг знайти жодної полегшеної бібліотеки.


0

Щоб перетворити "інструменти" в модуль, я зовсім не бачу важкості. Незважаючи на всі інші відповіді, я все-таки рекомендую використовувати module.exports:

//util.js
module.exports = {
   myFunction: function () {
   // your logic in here
   let message = "I am message from myFunction";
   return message; 
  }
}

Тепер нам потрібно призначити цей експорт у глобальному масштабі (у вашому додатку | індекс | server.js)

var util = require('./util');

Тепер ви можете посилатися та функцію дзвінка як:

//util.myFunction();
console.log(util.myFunction()); // prints in console :I am message from myFunction 

-3

Використання:

var mymodule = require("./tools.js")

app.js:

module.exports.<your function> = function () {
    <what should the function do>
}

1
Ви майже ніколи не повинні використовувати повний каталог. Вам слід подумати про використання відносних шляхів, таких як:./tools.js
Matthew D Auld
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.