Browserify - как вызвать функцию из файла, созданного с помощью browserify в браузере.

96

Я новичок в nodejs и просматриваю. Я начал с этой ссылки .

У меня есть файл main.js, содержащий этот код

var unique = require('uniq');

var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];

this.LogData =function(){
console.log(unique(data));
};

Теперь я устанавливаю модуль uniq с помощью npm:

 npm install uniq

Затем я собираю все необходимые модули, начиная с main.js, в один файл с именем bundle.js с помощью команды browserify:

browserify main.js -o bundle.js

Сгенерированный файл выглядит так:

(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var unique = require('uniq');

var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];

this.LogData =function(){
console.log(unique(data));
};

},{"uniq":2}],2:[function(require,module,exports){
"use strict"

function unique_pred(list, compare) {
  var ptr = 1
    , len = list.length
    , a=list[0], b=list[0]
  for(var i=1; i<len; ++i) {
    b = a
    a = list[i]
    if(compare(a, b)) {
      if(i === ptr) {
        ptr++
        continue
      }
      list[ptr++] = a
    }
  }
  list.length = ptr
  return list
}

function unique_eq(list) {
  var ptr = 1
    , len = list.length
    , a=list[0], b = list[0]
  for(var i=1; i<len; ++i, b=a) {
    b = a
    a = list[i]
    if(a !== b) {
      if(i === ptr) {
        ptr++
        continue
      }
      list[ptr++] = a
    }
  }
  list.length = ptr
  return list
}

function unique(list, compare, sorted) {
  if(list.length === 0) {
    return []
  }
  if(compare) {
    if(!sorted) {
      list.sort(compare)
    }
    return unique_pred(list, compare)
  }
  if(!sorted) {
    list.sort()
  }
  return unique_eq(list)
}

module.exports = unique
},{}]},{},[1])

После включения файла bundle.js на мою страницу index.htm, как мне вызвать функцию logData?

SharpCoder
источник
Где ты хочешь это назвать? И почему вы хотите это назвать?
artur grzesiak
2
@arturgrzesiak: Я хочу использовать эту функцию в одном из моих других проектов, которые я буду запускать в браузере.
SharpCoder

Ответы:

83

По умолчанию browserify не позволяет вам получить доступ к модулям извне просматриваемого кода - если вы хотите вызвать код в просматриваемом модуле, вы должны просматривать свой код вместе с модулем. См. Http://browserify.org/, чтобы увидеть примеры этого.

Конечно, вы также можете явно сделать свой метод доступным извне следующим образом:

window.LogData =function(){
  console.log(unique(data));
};

Тогда вы сможете позвонить LogData()из любого места на странице.

thejh
источник
1
Спасибо. Это работает. Означает ли это, что при создании функций вместо this.functionName я должен писать window.functionName? Есть ли у нас какие-нибудь другие решения для этого? Есть ли причины для использования window.functionName?
SharpCoder
21
«вы должны просматривать свой код вместе с модулем» - Ух, а что, если я хочу сделать что-то вроде onclick="someFunction()". Вы не можете утверждать, что это редкий вариант использования!?!
BlueRaja - Дэнни Пфлугхёфт,
57
Для начинающих существует серьезная нехватка документации о том, как на самом деле использовать Browserify на клиенте.
Оливер Диксон
1
да, в документации должно быть четко указано, что это дизайнерское решение, которого следует избегать, но указать четкий путь, чтобы заставить его работать, когда у вас нет альтернативы (в моем случае использование данных из шаблона для заполнения объекта JS) ... спасибо @thejh за указание на простое решение! ;)
Александр Мартини
1
Я даже не могу представить себе ситуацию, когда вы НЕ БУДЕТЕ делать ваши основные функции доступными вне модуля. Почему это не поведение по умолчанию? Какое веб-приложение не вызывает функции?
Cybernetic
101

Ключевой частью объединения автономных модулей с Browserify является --sопция. Он предоставляет все, что вы экспортируете из своего модуля, используя node module.exportsв качестве глобальной переменной. Затем файл можно включить в <script>тег.

Вам нужно сделать это только в том случае, если по какой-то причине вам нужно открыть эту глобальную переменную. В моем случае клиенту нужен был автономный модуль, который можно было бы включить в веб-страницы, не беспокоясь об этом бизнесе с Browserify.

Вот пример, в котором мы используем --sпараметр с аргументом module:

browserify index.js --s module > dist/module.js

Это откроет наш модуль как глобальную переменную с именем module.
Источник .

Обновление: спасибо @fotinakis. Убедитесь, что вы проходите --standalone your-module-name. Если вы забудете, что --standaloneпринимает аргумент, Browserify может незаметно сгенерировать пустой модуль, поскольку не сможет его найти.

Надеюсь, это сэкономит вам время.

Матас Вайткявичюс
источник
2
Я пытаюсь просмотреть детализированный код ES6. Но автономный объект пуст, когда я пытаюсь консолью в браузере. Простой код ES6 без каких-либо модулей отлично работает в автономном режиме. Есть указатели на это?
Джон
@jackyrudetsky не знаю, я бы рекомендовал добавить вопрос о SO, звучит как интересная проблема. может быть связано с этим. github.com/substack/node-browserify/issues/1357
Matas Vaitkevicius
1
@fotinakis На самом деле это была проблема в Browserify github.com/substack/node-browserify/issues/1537
Джон,
3
ИМО, это должен быть принятый ответ. Если вы используете глобальную функцию, гораздо лучше иметь собственное пространство имен, чем вешать каждую функцию за пределы окна.
VictorB
1
@VictorB: все глобальные переменные в Javascript являются элементами окна, поэтому оба метода достигают одного и того же (добавление глобальных переменных в окно)
Дэвид Лопес
37

@Matas Вайткявичюс в ответ с возможностью автономного Browserify в правильна (@ thejh - х ответ , используя глобальную переменную окна также работает, но , как уже отмечалось, это загрязняет глобальное пространство имен , так что это не идеально). Я хотел добавить немного подробностей о том, как использовать автономный вариант.

В исходном скрипте, который вы хотите связать, убедитесь, что вы открыли функции, которые хотите вызывать через module.exports. В клиентском скрипте вы можете вызывать эти открытые функции через <имя-связки>. <Имя-функции> . Вот пример:

Мой исходный файл src / script.js будет иметь следующее:
module.exports = {myFunc: func};

Моя команда browserify будет выглядеть примерно так:
browserify src/script.js --standalone myBundle > dist/bundle.js

И мой клиентский скрипт dist / client.js загрузит связанный скрипт,
<script src="bundle.js"></script>
а затем вызовет открытую функцию следующим образом:
<script>myBundle.myFunc();</script>


Нет необходимости запрашивать имя пакета в клиентском скрипте перед вызовом открытых функций, например, в <script src="bundle.js"></script><script>var bundled = require("myBundle"); bundled.myFunc();</script>этом нет необходимости и работать не будет.

Фактически, как и все функции, связанные с browserify без автономного режима, функция require не будет доступна вне связанного сценария . Browserify позволяет использовать некоторые функции Node на стороне клиента, но только в самом связанном скрипте ; он не предназначен для создания автономного модуля, который вы можете импортировать и использовать где угодно на стороне клиента, поэтому мы должны пойти на все эти дополнительные проблемы, просто чтобы вызвать одну функцию вне связанного с ней контекста.

Гален Лонг
источник
3
Вот это да! Напоследок практический пример.
N73k
1
Хороший пример, но поскольку «он загрязняет глобальное пространство имен, поэтому не идеально» не следует автоматически, это может быть приемлемо, если это только одна функция; Просто дым и зеркала, даже myBundleприкрепляются к объекту окна, window.myBundle.myFunc()вместо window.myFunc ()
joedotnot
1
Должны быть дополнительные баллы для людей, которые приводят примеры из конца в конец.
Шаруд
Так должна быть написана документация
Эллери Люн
8

Я только что прочитал ответы и, кажется, никто не упомянул об использовании области глобальной переменной? Это полезно, если вы хотите использовать один и тот же код в node.js и в браузере.

class Test
{
  constructor()
  {
  }
}
global.TestClass = Test;

Тогда вы можете получить доступ к TestClass где угодно.

<script src="bundle.js"></script>
<script>
var test = new TestClass(); // Enjoy!
</script>

Примечание: TestClass становится доступным везде. Это то же самое, что использовать переменную окна.

Кроме того, вы можете создать декоратор, который предоставляет класс в глобальную область видимости. Что действительно приятно, но затрудняет отслеживание того, где определена переменная.

DDD
источник
Как вы сами говорите, добавление функции в globalпроизводит тот же эффект, что и добавление в window, что уже было описано в jh. Этот ответ не добавляет новой информации.
Гален Лонг
@GalenLong, может быть, вы забыли, что в node.js нет переменной окна? А некоторые библиотеки, нацеленные на узел и браузер, могут захотеть использовать вместо этого global. Мой ответ получил несколько положительных отзывов и пока не в минусе, поэтому я думаю, что он информативен для других, если не для вас.
DDD
Ты прав, @Azarus. На странице было еще два повторяющихся ответа, и я неправильно включил ваш в кучу. Мои извенения.
Гален Лонг
просто хочу отметить, что висячие скобки здесь - очень плохая практика для javascript, например: примените этот шаблон к ключевому слову return и приготовьтесь плакать. например, return {}но опустите открывающую фигурную скобку до следующей строки.
Sgnl
1
@Azarus Я создал скрипку, чтобы продемонстрировать, что я имею в виду - jsfiddle.net/cubaksot/1
Sgnl
6

Прочтите README.md browserify о --standaloneпараметре или Google "browserify umd"

undoZen
источник
19
Это скорее подсказка, где найти ответ, чем ответ.
user2314737 01
это привело меня к решению, которое я искал два дня (как использовать вывод browserify из среды require.js). Спасибо!
Flion
2

Чтобы ваша функция была доступна как из HTML, так и из серверного узла:

main.js:

var unique = require('uniq');

function myFunction() {
    var data = [1, 2, 2, 4, 3];
    return unique(data).toString();
}
console.log ( myFunction() );

// When browserified - we can't call myFunction() from the HTML, so we'll externalize myExtFunction()
// On the server-side "window" is undef. so we hide it.
if (typeof window !== 'undefined') {
    window.myExtFunction = function() {
        return myFunction();
    }
}

main.html:

<html>
    <head>
        <script type='text/javascript' src="bundle.js"></script>
    <head>
    <body>
        Result: <span id="demo"></span>
        <script>document.getElementById("demo").innerHTML = myExtFunction();</script>
    </body>
</html>

Бегать:

npm install uniq
browserify main.js > bundle.js

и вы должны получить те же результаты при открытии main.html в браузере, что и при запуске

node main.js
Ори Миллер
источник
2

Минимальный запускаемый пример

Это в основном то же самое, что и: https://stackoverflow.com/a/43215928/895245 но с конкретными файлами, которые позволят вам просто запустить и легко воспроизвести его самостоятельно.

Этот код также доступен по адресу: https://github.com/cirosantilli/browserify-hello-world.

index.js

const uniq = require('uniq');

function myfunc() {
  return uniq([1, 2, 2, 3]).join(' ');
}
exports.myfunc = myfunc;

index.html

<!doctype html>
<html lang=en>
<head>
<meta charset=utf-8>
<title>Browserify hello world</title>
</head>
<body>
<div id="container">
</body>
</div>
<script src="out.js"></script>
<script>
document.getElementById('container').innerHTML = browserify_hello_world.myfunc();
</script>
</html>

Использование Node.js:

#!/usr/bin/env node

const browserify_hello_world = require('./index.js');

console.log(browserify_hello_world.myfunc());

Сгенерировать out.jsдля использования в браузере:

npx browserify --outfile out.js --standalone browserify_hello_world index.js

И браузер, и командная строка показывают ожидаемый результат:

1 2 3

Протестировано с помощью Browserify 16.5.0, Node.js v10.15.1, Chromium 78, Ubuntu 19.10.

Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
1
exports.myfunc.= myfuncЧасть этого абсолютно необходимо и пропустили в других ответах.
parttimeturtle
2

это действительно просто - вся эта концепция касается упаковки

1. альтернатива - объект "это"

для этой цели я предполагаю, что у вас есть «только 1 скрипт для всего приложения {{app_name}}» и «1 функция {{function_name}}»

добавить функцию {{function_name}} к объекту "this"

function {{function_name}}(param) {}
->
this.{{function_name}} = function(param) {}

тогда вы должны назвать этот объект, чтобы он был доступен - вы сделаете это, добавив параметр «автономный с именем», как советуют другие

поэтому, если вы используете "watchify" с "browserify", используйте это

var b = browserify({
    ...
    standalone: '{{app_name}}'
});

или командная строка

browserify index.js --standalone {{app_name}} > index-bundle.js

тогда вы можете вызвать свою функцию из браузера

{{app_name}}.{{function_name}}(param);
window.{{app_name}}.{{function_name}}(param);

2. альтернатива - объект «окно»

добавить функцию {{function_name}} в объект "окно"

function {{function_name}}(param) {}
->
window.{{function_name}} = function(param) {}

тогда вы можете вызвать свою функцию из браузера

{{function_name}}(param);
window.{{function_name}}(param);

-

может я помогу кому-то

BG BRUNO
источник
1

У вас есть несколько вариантов:

  1. Пусть плагин browserify-bridge автоматически экспортирует модули в сгенерированный модуль ввода. Это полезно для проектов SDK или ситуаций, когда вам не нужно вручную следить за тем, что экспортируется.

  2. Следуйте шаблону псевдо-пространства имен для свертывания экспонирования:

Во-первых, расположите свою библиотеку следующим образом, используя поиск по индексам в папках:

/src
--entry.js
--/helpers
--- index.js
--- someHelper.js
--/providers
--- index.js
--- someProvider.js
...

С помощью этого шаблона вы определяете запись следующим образом:

exports.Helpers = require('./helpers');
exports.Providers = require('./providers');
...

Обратите внимание, что require автоматически загружает index.js из каждой соответствующей подпапки.

В свои подпапки вы можете просто включить аналогичный манифест доступных модулей в этом контексте:

exports.SomeHelper = require('./someHelper');

Этот шаблон очень хорошо масштабируется и позволяет в контексте (папка за папкой) отслеживать, что нужно включить в свернутый api.

глубина
источник
-1
window.LogData =function(data){
   return unique(data);
};

Вызов функции просто LogData(data)

Это всего лишь небольшая модификация ответа jh, но важная

Pratik Khadtale
источник
Эта модификация не имеет отношения к проблеме, задающей вопрос, и не добавляет никакой новой информации с учетом уже существующих ответов.
Гален Лонг
-2

В целях отладки я добавил эту строку в свой code.js:

window.e = function(data) {eval(data);};

Тогда я мог запускать все, что угодно, даже вне пакета.

e("anything();");
Карвейани
источник