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?


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.


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.


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.


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ą.


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"


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

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.


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ę


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.


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

Wywołaj funkcję po prostu LogData(data)

To tylko niewielka modyfikacja odpowiedzi thejh, ale ważna


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();");
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.