Browserify - jak wywołać funkcję zawartą w pliku wygenerowanym przez browserify w przeglądarce

96

Jestem nowy w nodejs i browserify. Zacząłem od tego linku .

Mam plik main.js, który zawiera ten kod

var unique = require('uniq');

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

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

Teraz instaluję moduł uniq z npm:

 npm install uniq

Następnie łączę wszystkie wymagane moduły zaczynając od main.js w jednym pliku o nazwie bundle.js za pomocą polecenia browserify:

browserify main.js -o bundle.js

Wygenerowany plik wygląda następująco:

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

Po dołączeniu pliku bundle.js do mojej strony index.htm, jak wywołać funkcję logData?

SharpCoder
źródło
Gdzie chcesz to nazwać? A dlaczego chcesz to nazwać?
artur grzesiak 25.04.14
2
@arturgrzesiak: Chcę wykorzystać tę funkcję w jednym z moich innych projektów, które będę uruchamiać w przeglądarce.
SharpCoder

Odpowiedzi:

83

Domyślnie browserify nie pozwala na dostęp do modułów spoza kodu przeglądarkowego - jeśli chcesz wywołać kod w module z przeglądarką, powinieneś przeglądać swój kod razem z modułem. Przykłady można znaleźć pod adresem http://browserify.org/ .

Oczywiście możesz również jawnie udostępnić swoją metodę z zewnątrz w następujący sposób:

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

Wtedy możesz zadzwonić LogData()z dowolnego innego miejsca na stronie.

thejh
źródło
1
Dziękuję Ci. To działa. Czy to oznacza, że ​​podczas tworzenia funkcji zamiast mówić this.functionName powinienem napisać window.functionName? Czy mamy na to jakieś inne rozwiązanie? Jakieś powody używania window.functionName?
SharpCoder
21
"powinieneś przeglądać swój kod razem z modułem" - Ugh, a co jeśli chcę zrobić coś takiego onclick="someFunction()". Nie możesz argumentować, że to rzadki przypadek użycia!?!
BlueRaja - Danny Pflughoeft
57
W dowolnym miejscu brakuje dokumentacji dla początkujących na temat faktycznego korzystania z Browserify na kliencie.
Oliver Dixon
1
tak, dokumentacja powinna jasno określać, że jest to decyzja projektowa, której należy unikać, ale należy podać jasną ścieżkę, aby to zadziałało, gdy nie masz alternatywy (w moim przypadku użycie danych z szablonu do wypełnienia obiektu JS) ... dzięki @thejh za wskazanie prostego rozwiązania! ;)
Alexandre Martini
1
Nie mogę nawet pomyśleć o sytuacji, w której nie chciałbyś udostępniać swoich głównych funkcji poza modułem. Dlaczego to nie jest zachowanie domyślne? Jakie aplikacje internetowe nie wywołują funkcji?
Cybernetic
101

Kluczową częścią łączenia samodzielnych modułów z Browserify jest --sopcja. Ujawnia wszystko, co eksportujesz z modułu, używając węzła module.exportsjako zmiennej globalnej. Plik można następnie umieścić w <script>tagu.

Musisz to zrobić tylko wtedy, gdy z jakiegoś powodu chcesz, aby ta zmienna globalna była ujawniona. W moim przypadku klient potrzebował samodzielnego modułu, który można umieścić na stronach internetowych bez martwienia się o ten biznes związany z przeglądarką.

Oto przykład, w którym używamy --sopcji z argumentem module:

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

Spowoduje to ujawnienie naszego modułu jako zmiennej globalnej o nazwie module.
Źródło .

Aktualizacja: dzięki @fotinakis. Upewnij się, że zdajesz --standalone your-module-name. Jeśli zapomnisz, że --standalonepobiera argument, Browserify może po cichu wygenerować pusty moduł, ponieważ nie może go znaleźć.

Mam nadzieję, że zaoszczędzi ci to trochę czasu.

Matas Vaitkevicius
źródło
2
Próbuję przeglądać babelified kodu ES6. Ale samodzielny obiekt jest pusty, gdy próbuję go pocieszyć w przeglądarce. Prosty kod ES6 bez żadnych modułów działa dobrze w trybie samodzielnym. Jakieś wskazówki na ten temat?
John
@jackyrudetsky nie mam pojęcia, polecam dodanie pytania na SO, brzmi jak ciekawa kwestia. może być z tym powiązany. github.com/substack/node-browserify/issues/1357
Matas Vaitkevicius
1
@fotinakis W rzeczywistości był to problem w przeglądarce Browserify github.com/substack/node-browserify/issues/1537
Jan
3
IMO to powinna być akceptowana odpowiedź. Jeśli używasz funkcji globalnej, znacznie lepiej jest mieć własną przestrzeń nazw niż zawieszać każdą funkcję poza oknem.
VictorB
1
@VictorB wszystkie zmienne globalne w JavaScript są elementami okna, więc obie metody osiągają to samo (dodawanie zmiennych globalnych do okna)
David Lopez
37

Odpowiedź @Matas Vaitkevicius z samodzielną opcją Browserify jest poprawna (odpowiedź @ thejh przy użyciu zmiennej globalnej okna również działa, ale jak zauważyli inni, zanieczyszcza globalną przestrzeń nazw, więc nie jest idealna). Chciałem dodać trochę więcej szczegółów na temat korzystania z opcji samodzielnej.

W skrypcie źródłowym, który chcesz spakować, upewnij się, że ujawnisz funkcje, które chcesz wywołać za pośrednictwem module.exports. W skrypcie klienta można wywołać te ujawnione funkcje za pomocą <nazwa-pakietu>. <nazwa-func> . Oto przykład:

Mój plik źródłowy src / script.js będzie miał to:
module.exports = {myFunc: func};

Moje polecenie browserify będzie wyglądać mniej więcej tak:
browserify src/script.js --standalone myBundle > dist/bundle.js

A mój skrypt klienta dist / client.js załaduje dołączony skrypt,
<script src="bundle.js"></script>
a następnie wywoła udostępnioną funkcję w następujący sposób:
<script>myBundle.myFunc();</script>


Nie ma potrzeby wymagania nazwy pakietu w skrypcie klienta przed wywołaniem udostępnionych funkcji, np. <script src="bundle.js"></script><script>var bundled = require("myBundle"); bundled.myFunc();</script>Nie jest to konieczne i nie będzie działać.

W rzeczywistości, podobnie jak wszystkie funkcje dostarczane przez browserify bez trybu samodzielnego, funkcja require nie będzie dostępna poza dołączonym skryptem . Browserify umożliwia korzystanie z niektórych funkcji węzła po stronie klienta, ale tylko w samym skrypcie dołączonym ; nie jest przeznaczony do tworzenia samodzielnego modułu, który można importować i używać w dowolnym miejscu po stronie klienta, dlatego musimy zadać sobie tyle trudu, aby wywołać pojedynczą funkcję poza powiązanym kontekstem.

Galen Long
źródło
3
Łał! Wreszcie praktyczny przykład.
N73k
1
Dobry przykład, ale o ile „zanieczyszcza globalną przestrzeń nazw, a zatem nie jest idealny”, nie następuje automatycznie, może być akceptowalny, jeśli jest tylko jedną funkcją; Po prostu dym i lustra, a nawet myBundleprzyczepia się do obiektu okna, window.myBundle.myFunc()zamiast do okna. MyFunc ()
joedotnot
1
Osoby, które podają kompleksowe przykłady, powinny mieć dodatkowe punkty.
Sharud,
Tak należy pisać dokumentację
Ellery Leung
8

Właśnie przeczytałem odpowiedzi i wygląda na to, że nikt nie wspomniał o zastosowaniu zakresu zmiennej globalnej? Co jest przydatne, jeśli chcesz użyć tego samego kodu w node.js iw przeglądarce.

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

Następnie możesz uzyskać dostęp do TestClass w dowolnym miejscu.

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

Uwaga: klasa TestClass staje się wtedy dostępna wszędzie. To jest to samo, co użycie zmiennej okna.

Dodatkowo możesz utworzyć dekorator, który uwidacznia klasę w zakresie globalnym. Co jest naprawdę fajne, ale utrudnia śledzenie, gdzie zdefiniowano zmienną.

DDD
źródło
Jak sam mówisz, dodanie funkcji do globaldaje taki sam efekt jak dodanie do window, co zostało już uwzględnione w jh. Ta odpowiedź nie dodaje żadnych nowych informacji.
Galen Long
@GalenLong Może zapomniałeś, że w node.js nie ma zmiennej okna? Niektóre biblioteki, które są przeznaczone dla węzła i przeglądarki, mogą zamiast tego chcieć używać funkcji globalnych. Moja odpowiedź otrzymała kilka pozytywnych głosów i nie jest jeszcze na minusie, więc myślę, że jest to pouczająca dla innych, jeśli nie dla ciebie.
DDD
Masz rację, @Azarus. Na stronie były dwie inne zduplikowane odpowiedzi i błędnie dołączyłem Twoją do paczki. Przepraszam.
Galen Long
chcę tylko zauważyć, że wiszące pareny są tutaj bardzo złą praktyką dla javascript, na przykład: zastosuj ten wzorzec do słowa kluczowego return i przygotuj się na płacz. np. return {}ale upuść otwierający nawias klamrowy do następnej linii.
Sgnl
1
@Azarus Stworzyłem skrzypce, aby zademonstrować, o co mi chodzi - jsfiddle.net/cubaksot/1
Sgnl
6

Przeczytaj plik README.md w pliku browserify o --standaloneparametrze lub google "browserify umd"

undoZen
źródło
19
Jest to raczej wskazówka, gdzie znaleźć odpowiedź niż odpowiedź.
user2314737
to doprowadziło mnie do rozwiązania, którego szukałem przez dwa dni (jak używać wyjścia browserify ze środowiska require.js). Dziękuję Ci!
Flion
2

Aby Twoja funkcja była dostępna zarówno z poziomu HTML, jak iz węzła po stronie serwera:

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>

Biegać:

npm install uniq
browserify main.js > bundle.js

i powinieneś uzyskać takie same wyniki podczas otwierania main.html w przeglądarce, jak podczas uruchamiania

node main.js
Ori Miller
źródło
2

Minimalny działający przykład

Jest to w zasadzie to samo, co: https://stackoverflow.com/a/43215928/895245, ale z konkretnymi plikami, które pozwolą ci po prostu uruchomić i łatwo odtworzyć go samodzielnie.

Ten kod jest również dostępny pod adresem : 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>

Użycie Node.js:

#!/usr/bin/env node

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

console.log(browserify_hello_world.myfunc());

Wygeneruj out.jsdo użytku w przeglądarce:

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

Zarówno przeglądarka, jak i wiersz poleceń wyświetlają oczekiwane dane wyjściowe:

1 2 3

Testowane z przeglądarką Browserify 16.5.0, Node.js 10.15.1, Chromium 78, Ubuntu 19.10.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
1
exports.myfunc.= myfuncCzęść to było absolutnie konieczne i brakowało w innych odpowiedzi.
parttimeturtle
2

to naprawdę proste - cała ta koncepcja dotyczy owijania

1. alternatywa - obiekt „this”

w tym celu zakładam, że masz „tylko 1 skrypt dla całej aplikacji {{app_name}}” i „1 funkcję {{function_name}}”

dodaj funkcję {{function_name}} do obiektu „this”

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

wtedy musisz nazwać ten obiekt aby był dostępny - zrobisz to dodając parametr "samodzielny z nazwą" tak jak radzili inni

więc jeśli używasz "watchify" z "browserify", użyj tego

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

lub wiersz poleceń

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

wtedy możesz wywołać swoją funkcję z przeglądarki

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

2. alternatywa - obiekt „okno”

dodaj funkcję {{function_name}} do obiektu „window”

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

wtedy możesz wywołać swoją funkcję z przeglądarki

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

-

może komuś pomogę

BG BRUNO
źródło
1

Masz kilka możliwości:

  1. Niech plugin browserify-bridge automatycznie wyeksportuje moduły do ​​wygenerowanego modułu wejściowego. Jest to przydatne w przypadku projektów SDK lub sytuacji, w których nie trzeba ręcznie nadążać za eksportowanymi danymi.

  2. Postępuj zgodnie z wzorcem pseudo-przestrzeni nazw, aby uzyskać pełną ekspozycję:

Najpierw ułóż swoją bibliotekę w ten sposób, korzystając z wyszukiwania indeksów w folderach:

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

Za pomocą tego wzorca definiujesz wpis w ten sposób:

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

Zwróć uwagę, że żądanie automatycznie ładuje plik index.js z każdego odpowiedniego podfolderu

W swoich podfolderach możesz po prostu dołączyć podobny manifest dostępnych modułów w tym kontekście:

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

Ten wzorzec bardzo dobrze się skaluje i pozwala na kontekstowe (folder po folderze) śledzenie tego, co należy uwzględnić w zwiniętym api.

deepelement
źródło
-1
window.LogData =function(data){
   return unique(data);
};

Wywołaj funkcję po prostu LogData(data)

To tylko niewielka modyfikacja odpowiedzi thejh, ale ważna

Pratik Khadtale
źródło
Ta modyfikacja nie ma znaczenia dla obaw osoby zadającej pytanie i nie dodaje żadnych nowych informacji, biorąc pod uwagę już istniejące odpowiedzi.
Galen Long
-2

W celu debugowania dodałem ten wiersz do mojego code.js:

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

Wtedy mogłem uruchomić wszystko, nawet poza pakietem.

e("anything();");
Karveiani
źródło