Browserify - como chamar a função agrupada em um arquivo gerado por meio do browserify no navegador

95

Eu sou novo em nodejs e browserify. Comecei com este link .

Eu tenho o arquivo main.js que contém este código

var unique = require('uniq');

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

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

Agora eu instalo o módulo uniq com npm:

 npm install uniq

Em seguida, agrupo todos os módulos necessários começando em main.js em um único arquivo chamado bundle.js com o comando browserify:

browserify main.js -o bundle.js

O arquivo gerado tem a seguinte aparência:

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

Depois de incluir o arquivo bundle.js na minha página index.htm, como faço para chamar a função logData ??

SharpCoder
fonte
De onde você quer chamar? E por que você quer ligar?
Artur Grzesiak
2
@arturgrzesiak: Eu quero utilizar esta função em um dos meus outros projetos que estarei executando no navegador.
SharpCoder

Respostas:

80

Por padrão, o browserify não permite que você acesse os módulos de fora do código browserified - se você deseja chamar o código em um módulo browserified, você deve browserify seu código junto com o módulo. Veja http://browserify.org/ para exemplos disso.

Claro, você também pode tornar seu método explicitamente acessível de fora desta forma:

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

Em seguida, você pode ligar LogData()de qualquer outro lugar na página.

thejh
fonte
1
Obrigado. Isso funciona. Isso significa que, ao criar funções em vez de dizer this.functionName, devo escrever window.functionName? Temos alguma outra solução alternativa para isso? Algum motivo para usar window.functionName?
SharpCoder
19
"você deve pesquisar seu código junto com o módulo" - Ugh, e se eu quiser fazer algo assim onclick="someFunction()". Você não pode estar argumentando que esse é um caso de uso raro!?!
BlueRaja - Danny Pflughoeft
55
Há uma séria falta de documentação em qualquer lugar para iniciantes sobre como realmente usar o Browserify no cliente.
Oliver Dixon
1
sim, a documentação deve afirmar claramente que esta é uma decisão de design a ser evitada, mas fornecer um caminho claro para fazê-la funcionar quando você não tiver uma alternativa (no meu caso, usar dados do modelo para preencher um objeto JS) ... obrigado @thejh por apontar para uma solução simples! ;)
Alexandre Martini
1
Não consigo nem pensar em uma situação em que você NÃO queira disponibilizar suas funções principais fora do módulo. Como esse comportamento não é padrão? Que tipo de aplicativo da web não chama funções?
Cybernetic
100

A parte principal de agrupar módulos autônomos com o Browserify é a --sopção. Ele expõe tudo o que você exporta de seu módulo usando o nó module.exportscomo uma variável global. O arquivo pode então ser incluído em uma <script>tag.

Você só precisa fazer isso se, por algum motivo, precisar que essa variável global seja exposta. No meu caso, o cliente precisava de um módulo autônomo que pudesse ser incluído nas páginas da web sem que eles precisassem se preocupar com esse negócio do Browserify.

Aqui está um exemplo em que usamos a --sopção com um argumento de module:

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

Isso irá expor nosso módulo como uma variável global chamada module.
Fonte .

Atualização: Obrigado a @fotinakis. Certifique-se de que você está passando --standalone your-module-name. Se você esquecer que --standaloneleva um argumento, o Browserify pode gerar silenciosamente um módulo vazio, pois não foi possível encontrá-lo.

Espero que isso economize algum tempo.

Matas Vaitkevicius
fonte
2
Estou tentando navegar no código ES6 babelificado. Mas o objeto autônomo está vazio quando tento consolá-lo no navegador. O código ES6 simples sem nenhum módulo funciona bem no modo autônomo. Alguma indicação sobre isso?
João
@jackyrudetsky não faço ideia, recomendo adicionar uma pergunta sobre o SO, parece um assunto interessante. poderia estar relacionado a isso. github.com/substack/node-browserify/issues/1357
Matas Vaitkevicius
1
@fotinakis Na verdade, era um problema no Browserify github.com/substack/node-browserify/issues/1537
John
3
IMO esta deve ser a resposta aceita. Se você estiver usando uma função global, é muito melhor ter seu próprio namespace do que desligar todas as funções da janela.
VictorB
1
@VictorB todas as variáveis ​​globais em Javascript são elementos da janela, então ambos os métodos obtêm a mesma coisa (adicionando as variáveis ​​globais à janela)
David Lopez
37

A resposta de @Matas Vaitkevicius com a opção autônoma do Browserify está correta (a resposta de @ thejh usando a variável global de janela também funciona, mas como outros notaram, ela polui o namespace global, por isso não é ideal). Eu queria adicionar mais detalhes sobre como usar a opção autônoma.

No script de origem que você deseja agrupar, certifique-se de expor as funções que deseja chamar por meio de module.exports. No script do cliente, você pode chamar essas funções expostas por meio de <bundle-name>. <func-name> . Aqui está um exemplo:

Meu arquivo de origem src / script.js terá este:
module.exports = {myFunc: func};

Meu comando browserify será semelhante a este:
browserify src/script.js --standalone myBundle > dist/bundle.js

E meu script de cliente dist / client.js carregará o script agrupado
<script src="bundle.js"></script>
e chamará a função exposta desta forma:
<script>myBundle.myFunc();</script>


Não há necessidade de exigir o nome do pacote no script do cliente antes de chamar as funções expostas, por exemplo, <script src="bundle.js"></script><script>var bundled = require("myBundle"); bundled.myFunc();</script>não é necessário e não funcionará.

Na verdade, assim como todas as funções agrupadas por browserify sem modo autônomo, a função require não estará disponível fora do script agrupado . O Browserify permite que você use algumas funções do Node no lado do cliente, mas apenas no próprio script empacotado ; não foi criado para criar um módulo autônomo que você possa importar e usar em qualquer lugar do lado do cliente, e é por isso que temos que ter todo esse trabalho extra apenas para chamar uma única função fora de seu contexto agrupado.

Galen Long
fonte
3
Uau! Finalmente um exemplo prático.
N73k
1
Bom exemplo, mas na medida em que "polui o namespace global, portanto não é ideal" não segue automaticamente, pode ser aceitável se for apenas uma função; Apenas fumaça e espelhos, até mesmo myBundleé anexado ao objeto da janela, em window.myBundle.myFunc()vez de window.myFunc ()
joedotnot
1
Deve haver pontos extras para pessoas que dão exemplos de ponta a ponta.
Sharud
É assim que a documentação deve ser escrita
Ellery Leung
7

Acabei de ler as respostas e parece que ninguém mencionou o uso do escopo da variável global? O que é útil se você deseja usar o mesmo código em node.js e no navegador.

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

Então você pode acessar o TestClass em qualquer lugar.

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

Nota: O TestClass torna-se então disponível em qualquer lugar. O que é o mesmo que usar a variável de janela.

Além disso, você pode criar um decorador que expõe uma classe ao escopo global. O que é muito bom, mas torna difícil rastrear onde uma variável é definida.

DDD
fonte
Como você mesmo disse, adicionar a função a globalproduz o mesmo efeito que adicionar a window, que já foi abordado por thejh. Esta resposta não adiciona nenhuma informação nova.
Galen Long
@GalenLong talvez você tenha esquecido que não há variável de janela no node.js? E algumas bibliotecas que têm como destino o nó e o navegador podem querer usar o global. Minha resposta recebeu alguns votos positivos e ainda não está em menos, então acho que é informativo para outras pessoas, se não para você.
DDD
Você está certo, @Azarus. Havia duas outras respostas duplicadas na página e eu incluí incorretamente a sua no grupo. Me desculpe.
Galen Long
só quero observar que os parênteses pendurados aqui são uma prática muito ruim para javascript, por exemplo: aplique esse padrão à palavra-chave return e prepare-se para chorar. por exemplo, return {}mas solte a chave de abertura para a próxima linha.
Sgnl
1
@Azarus Eu criei um violino para demonstrar o que quero dizer - jsfiddle.net/cubaksot/1
Sgnl
6

Leia README.md do browserify sobre o --standaloneparâmetro ou google "browserify umd"

desfazer
fonte
19
Esta é mais uma dica de onde encontrar uma resposta do que uma resposta.
user2314737
isso me levou à solução que estava procurando por dois dias (como usar a saída do browserify de um ambiente require.js). obrigado!
Flion
2

Para ter sua função disponível tanto no HTML quanto no nó do servidor:

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>

Corre:

npm install uniq
browserify main.js > bundle.js

e você deve obter os mesmos resultados ao abrir main.html em um navegador e ao executar

node main.js
Ori Miller
fonte
2

Exemplo de execução mínima

É basicamente o mesmo que: https://stackoverflow.com/a/43215928/895245, mas com arquivos concretos que permitirão que você apenas execute e reproduza facilmente você mesmo.

Este código também está disponível em: 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>

Uso de Node.js:

#!/usr/bin/env node

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

console.log(browserify_hello_world.myfunc());

Gere out.jspara uso do navegador:

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

Tanto o navegador quanto a linha de comando mostram a saída esperada:

1 2 3

Testado com Browserify 16.5.0, Node.js v10.15.1, Chromium 78, Ubuntu 19.10.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fonte
1
A exports.myfunc.= myfuncparte disso foi absolutamente crítica e perdida em outras respostas.
parttimeturtle
1

Você tem poucas opções:

  1. Deixe o plugin browserify-bridge exportar automaticamente os módulos para um módulo de entrada gerado. Isso é útil para projetos de SDK ou situações em que você não precisa acompanhar manualmente o que é exportado.

  2. Siga um padrão de pseudo-namespace para exposição de roll-up:

Primeiro, organize sua biblioteca assim, aproveitando as pesquisas de índice nas pastas:

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

Com este padrão, você define a entrada assim:

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

Observe que o require carrega automaticamente o index.js de cada subpasta respectiva

Em suas subpastas, você pode apenas incluir um manifesto semelhante dos módulos disponíveis nesse contexto:

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

Esse padrão é muito bem dimensionado e permite o rastreamento contextual (pasta por pasta) do que incluir na API acumulada.

elemento profundo
fonte
1

é muito simples - todo esse conceito é sobre embrulhar

1. alternativa - objeto "este"

para este propósito, assumirei que você tem "apenas 1 script para todo o aplicativo {{app_name}}" e "1 função {{function_name}}"

adicione a função {{function_name}} ao objeto "este"

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

então você tem que nomear aquele objeto para estar disponível - você irá fazê-lo adicionar o parâmetro "autônomo com nome" como outros aconselharam

então se você usar "watchify" com "browserify", use este

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

ou linha de comando

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

então você pode chamar sua função do navegador

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

2. alternativa - objeto "janela"

adicione a função {{function_name}} ao objeto "janela"

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

então você pode chamar sua função do navegador

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

-

talvez eu ajude alguém

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

Chame a função simplesmente por LogData(data)

Esta é apenas uma ligeira modificação na resposta do jh, mas importante

Pratik Khadtale
fonte
Essa modificação é irrelevante para as preocupações do questionador e não adiciona nenhuma informação nova, dadas as respostas já existentes.
Galen Long
-2

Para fins de depuração, adicionei esta linha ao meu code.js:

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

Então eu poderia executar qualquer coisa, mesmo fora do pacote.

e("anything();");
Karveiani
fonte