Browserify - Aufrufen einer Funktion, die in einer durch browserify im Browser generierten Datei gebündelt ist


94

Ich bin neu in NodeJS und Browserify. Ich habe mit diesem Link angefangen .

Ich habe die Datei main.js, die diesen Code enthält

var unique = require('uniq');

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

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

Jetzt installiere ich das Uniq-Modul mit npm:

 npm install uniq

Dann bündle ich alle erforderlichen Module ab main.js mit dem Befehl browserify in einer einzigen Datei namens bundle.js:

browserify main.js -o bundle.js

Die generierte Datei sieht folgendermaßen aus:

(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])

Wie rufe ich die logData-Funktion auf, nachdem ich die Datei bundle.js in meine index.htm-Seite aufgenommen habe?


Wo willst du es nennen? Und warum willst du es nennen?
Artur Grzesiak

2
@arturgrzesiak: Ich möchte diese Funktion in einem meiner anderen Projekte verwenden, die ich im Browser ausführen werde.
SharpCoder

Antworten:


80

Mit browserify können Sie standardmäßig nicht von außerhalb des browserifizierten Codes auf die Module zugreifen. Wenn Sie Code in einem browserifizierten Modul aufrufen möchten, sollten Sie Ihren Code zusammen mit dem Modul browserifizieren. Beispiele hierfür finden Sie unter http://browserify.org/ .

Natürlich können Sie Ihre Methode auch explizit von außen zugänglich machen:

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

Dann können Sie LogData()von überall auf der Seite anrufen .


1
Danke dir. Das funktioniert. Bedeutet dies, dass ich beim Erstellen von Funktionen, anstatt this.functionName zu sagen, window.functionName schreiben sollte? Haben wir noch andere Möglichkeiten dafür? Irgendwelche Gründe für die Verwendung von window.functionName?
SharpCoder

19
„Sie sollten Ihren Code zusammen mit dem Modul browserify“ - Ach, was ist, wenn ich so etwas tun will onclick="someFunction()". Sie können unmöglich argumentieren, dass dies ein seltener Anwendungsfall ist!?!
BlueRaja - Danny Pflughoeft

55
Anfängern mangelt es an Dokumentation, wie Browserify tatsächlich auf dem Client verwendet werden kann.
Oliver Dixon

1
Ja, in der Dokumentation sollte klar angegeben sein, dass dies eine zu vermeidende Entwurfsentscheidung ist. Geben Sie jedoch einen klaren Pfad an, damit dies funktioniert, wenn Sie keine Alternative haben (in meinem Fall verwenden Sie Daten aus der Vorlage, um ein JS-Objekt zu füllen). ... danke @thejh für den Hinweis auf eine einfache Lösung! ;)
Alexandre Martini

1
Ich kann mir nicht einmal eine Situation vorstellen, in der Sie Ihre Hauptfunktionen NICHT außerhalb des Moduls verfügbar machen möchten. Wie ist das nicht Standardverhalten? Welche Art von Webanwendung ruft keine Funktionen auf?
Kybernetisch

99

Der Schlüssel zum Bündeln von eigenständigen Modulen mit Browserify ist die --sOption. Es macht alles verfügbar, was Sie aus Ihrem Modul exportieren, indem Sie Knoten module.exportsals globale Variable verwenden. Die Datei kann dann in ein <script>Tag aufgenommen werden.

Sie müssen dies nur tun, wenn Sie aus irgendeinem Grund diese globale Variable verfügbar machen müssen. In meinem Fall benötigte der Client ein eigenständiges Modul, das in Webseiten aufgenommen werden konnte, ohne dass er sich um dieses Browserify-Geschäft kümmern musste.

Hier ist ein Beispiel, in dem wir die --sOption mit dem Argument verwenden module:

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

Dadurch wird unser Modul als globale Variable mit dem Namen verfügbar gemacht module.
Quelle .

Update: Danke an @fotinakis. Stellen Sie sicher, dass Sie vorbei sind --standalone your-module-name. Wenn Sie vergessen, dass --standaloneein Argument erforderlich ist, generiert Browserify möglicherweise stillschweigend ein leeres Modul, da es nicht gefunden werden konnte.

Hoffe das spart dir etwas Zeit.


2
Ich versuche, babelifizierten ES6-Code zu browserifizieren. Das eigenständige Objekt ist jedoch leer, wenn ich versuche, es im Browser zu trösten. Einfacher ES6-Code ohne Module funktioniert im Standalone-Modus einwandfrei. Irgendwelche Hinweise dazu?
John

@jackyrudetsky keine Ahnung, ich würde empfehlen, eine Frage zu SO hinzuzufügen, klingt nach einem interessanten Thema. könnte damit zusammenhängen. github.com/substack/node-browserify/issues/1357
Matas Vaitkevicius

1
@fotinakis Es war tatsächlich ein Problem in Browserify github.com/substack/node-browserify/issues/1537
John

3
IMO sollte dies die akzeptierte Antwort sein. Wenn Sie eine globale Funktion verwenden, ist es viel besser, einen eigenen Namespace zu haben, als jede Funktion außerhalb des Fensters aufzuhängen.
VictorB

1
@ VictorBB alle globalen Variablen in Javascript sind Elemente des Fensters, so dass beide Methoden dasselbe erreichen (Hinzufügen der globalen Variablen zum Fenster)
David Lopez

37

Die Antwort von @Matas Vaitkevicius mit der eigenständigen Option von Browserify ist korrekt (die Antwort von @ thejh unter Verwendung der globalen Fenstervariablen funktioniert ebenfalls, aber wie andere angemerkt haben, verschmutzt sie den globalen Namespace, sodass sie nicht ideal ist). Ich wollte etwas mehr Details zur Verwendung der Standalone-Option hinzufügen.

Stellen Sie im Quellenskript, das Sie bündeln möchten, sicher, dass Sie die Funktionen verfügbar machen, die Sie über module.exports aufrufen möchten. Im Client-Skript können Sie diese offengelegten Funktionen über <Bündelname>. < Funktionsname > aufrufen . Hier ist ein Beispiel:

Meine Quelldatei src / script.js hat Folgendes :
module.exports = {myFunc: func};

Mein browserify-Befehl sieht ungefähr so aus:
browserify src/script.js --standalone myBundle > dist/bundle.js

Und mein Client-Skript dist / client.js lädt das gebündelte Skript
<script src="bundle.js"></script>
und ruft dann die exponierte Funktion wie folgt auf :
<script>myBundle.myFunc();</script>


Es ist nicht erforderlich, den Bundle-Namen im Client-Skript zu benötigen, bevor die bereitgestellten Funktionen aufgerufen werden. Dies ist z. B. <script src="bundle.js"></script><script>var bundled = require("myBundle"); bundled.myFunc();</script>nicht erforderlich und funktioniert nicht.

Genau wie alle Funktionen, die von browserify ohne Standalone-Modus gebündelt werden, ist die Funktion require außerhalb des gebündelten Skripts nicht verfügbar . Mit Browserify können Sie einige Knotenfunktionen clientseitig verwenden, jedoch nur im mitgelieferten Skript . Es ist nicht dazu gedacht, ein eigenständiges Modul zu erstellen, das Sie überall auf der Clientseite importieren und verwenden können. Deshalb müssen wir uns um all diese zusätzlichen Probleme kümmern, um nur eine einzelne Funktion außerhalb des gebündelten Kontexts aufzurufen.


3
Beeindruckend! Endlich ein praktisches Beispiel.
N73k

1
Gutes Beispiel, aber soweit "es verschmutzt den globalen Namespace daher nicht ideal" nicht automatisch folgt, kann es akzeptabel sein, wenn es nur eine Funktion ist; Nur Rauch und Spiegel, wird sogar myBundlean das Fensterobjekt angehängt, window.myBundle.myFunc()anstatt an window.myFunc ()
joedotnot

1
Es sollte zusätzliche Punkte für Personen geben, die End-to-End-Beispiele geben.
Sharud

So sollte Dokumentation geschrieben werden
Ellery Leung

7

Ich habe gerade die Antworten durchgelesen und es scheint, als hätte niemand die Verwendung des globalen Variablenbereichs erwähnt. Dies ist nützlich, wenn Sie denselben Code in node.js und im Browser verwenden möchten.

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

Dann können Sie überall auf die TestClass zugreifen .

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

Hinweis: Die Testklasse wird dann überall verfügbar. Dies entspricht der Verwendung der Fenstervariablen.

Darüber hinaus können Sie einen Dekorator erstellen, der eine Klasse für den globalen Bereich verfügbar macht. Das ist wirklich schön, macht es aber schwierig zu verfolgen, wo eine Variable definiert ist.


Wie Sie selbst sagen, globalerzeugt das Hinzufügen der Funktion zu den gleichen Effekt wie das Hinzufügen zu window, der bereits von thejh behandelt wurde. Diese Antwort fügt keine neuen Informationen hinzu.
Galen Long

@GalenLong Vielleicht haben Sie vergessen, dass es in node.js keine Fenstervariable gibt? Einige Bibliotheken, die auf Knoten und Browser abzielen, möchten möglicherweise stattdessen global verwenden. Meine Antwort hat ein paar positive Stimmen bekommen und ist noch nicht im Minus, also denke ich, dass sie für andere informativ ist, wenn nicht für Sie.
DDD

Du hast recht, @Azarus. Es gab zwei weitere doppelte Antworten auf der Seite, und ich habe Ihre fälschlicherweise in den Haufen aufgenommen. Entschuldigen Sie.
Galen Long

Ich möchte nur darauf hinweisen, dass die hängenden Parens hier eine sehr schlechte Praxis für Javascript sind. Beispiel: Wenden Sie dieses Muster auf das Schlüsselwort return an und bereiten Sie sich auf das Weinen vor. zB return {}aber lassen Sie die öffnende geschweifte Klammer in die nächste Zeile fallen.
Sgnl

1
@ Azarus Ich habe eine Geige erstellt, um zu demonstrieren, was ich meine - jsfiddle.net/cubaksot/1
Sgnl

6

Lesen Sie README.md von browserify über --standaloneParameter oder google "browserify umd".


19
Dies ist eher ein Hinweis darauf, wo eine Antwort zu finden ist, als eine Antwort.
user2314737

Dies führte mich zu der Lösung, nach der ich zwei Tage lang gesucht hatte (Verwendung der browserify-Ausgabe aus einer require.js-Umgebung). Danke!
Flion

2

So stellen Sie Ihre Funktion sowohl im HTML-Code als auch im serverseitigen Knoten zur Verfügung:

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>

Lauf:

npm install uniq
browserify main.js > bundle.js

und Sie sollten beim Öffnen von main.html in einem Browser dieselben Ergebnisse erzielen wie beim Ausführen

node main.js

2

Minimal lauffähiges Beispiel

Dies ist im Grunde dasselbe wie: https://stackoverflow.com/a/43215928/895245, jedoch mit konkreten Dateien, mit denen Sie es einfach ausführen und einfach selbst reproduzieren können.

Dieser Code ist auch verfügbar unter: 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>

Verwendung von Node.js:

#!/usr/bin/env node

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

console.log(browserify_hello_world.myfunc());

out.jsFür die Verwendung im Browser generieren :

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

Sowohl der Browser als auch die Befehlszeile zeigen die erwartete Ausgabe an:

1 2 3

Getestet mit Browserify 16.5.0, Node.js v10.15.1, Chromium 78, Ubuntu 19.10.


1
Der exports.myfunc.= myfuncTeil davon war absolut kritisch und wurde in anderen Antworten übersehen.
parttimeturtle

1

Sie haben einige Möglichkeiten:

  1. Lassen Sie das Plugin browserify-bridge die Module automatisch in ein generiertes Eingabemodul exportieren. Dies ist hilfreich für SDK-Projekte oder Situationen, in denen Sie nicht manuell mit dem Export Schritt halten müssen.

  2. Folgen Sie einem Pseudo-Namespace-Muster für die Roll-up-Belichtung:

Ordnen Sie Ihre Bibliothek zunächst so an, indem Sie die Indexsuche für Ordner nutzen:

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

Mit diesem Muster definieren Sie den Eintrag wie folgt:

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

Beachten Sie, dass die Anforderung die Datei index.js automatisch aus dem jeweiligen Unterordner lädt

In Ihren Unterordnern können Sie einfach ein ähnliches Manifest der verfügbaren Module in diesem Kontext einfügen:

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

Dieses Muster lässt sich sehr gut skalieren und ermöglicht eine kontextbezogene (Ordner für Ordner) Verfolgung dessen, was in der aufgerollten API enthalten sein soll.


1

Es ist wirklich einfach - bei diesem ganzen Konzept geht es um das Verpacken

1. Alternative - Objekt "this"

Zu diesem Zweck gehe ich davon aus, dass Sie "nur 1 Skript für die gesamte App {{app_name}}" und "1 Funktion {{Funktionsname}}" haben.

füge die Funktion {{Funktionsname}} zum Objekt "this" hinzu

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

Dann müssen Sie das Objekt benennen, um verfügbar zu sein. Anschließend fügen Sie den Parameter "Standalone with Name" hinzu, wie von anderen empfohlen

Wenn Sie also "watchify" mit "browserify" verwenden, verwenden Sie diese Option

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

oder Befehlszeile

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

dann können Sie Ihre Funktion über den Browser aufrufen

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

2. Alternative - Objekt "Fenster"

füge die Funktion {{Funktionsname}} zum Objekt "Fenster" hinzu

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

dann können Sie Ihre Funktion über den Browser aufrufen

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

- -

Vielleicht helfe ich jemandem


-1
window.LogData =function(data){
   return unique(data);
};

Rufen Sie die Funktion einfach über auf LogData(data)

Dies ist nur eine geringfügige Änderung der Antwort von thejh, aber eine wichtige


Diese Änderung ist für die Bedenken des Fragestellers irrelevant und fügt angesichts der bereits vorhandenen Antworten keine neuen Informationen hinzu.
Galen Long

-2

Zu Debugging-Zwecken habe ich diese Zeile zu meiner code.js hinzugefügt:

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

Dann könnte ich alles auch außerhalb des Bundles laufen lassen.

e("anything();");
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.