Browserify: cómo llamar a la función incluida en un archivo generado a través de browserify en el navegador

96

Soy nuevo en nodejs y browserify. Empecé con este enlace .

Tengo el archivo main.js que contiene 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));
};

Ahora instalo el módulo uniq con npm:

 npm install uniq

Luego agrupo todos los módulos requeridos comenzando en main.js en un solo archivo llamado bundle.js con el comando browserify:

browserify main.js -o bundle.js

El archivo generado se ve así:

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

Después de incluir el archivo bundle.js en mi página index.htm, ¿cómo llamo a la función logData?

SharpCoder
fuente
¿Dónde quieres llamarlo? ¿Y por qué quieres llamarlo?
artur grzesiak
2
@arturgrzesiak: Quiero utilizar esta función en uno de mis otros proyectos que ejecutaré en el navegador.
SharpCoder

Respuestas:

83

De forma predeterminada, browserify no le permite acceder a los módulos desde fuera del código explorado; si desea llamar al código en un módulo explorado, se supone que debe explorar su código junto con el módulo. Consulte http://browserify.org/ para ver ejemplos de eso.

Por supuesto, también puede hacer que su método sea accesible desde afuera de esta manera:

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

Entonces podría llamar LogData()desde cualquier otro lugar de la página.

thejh
fuente
1
Gracias. Esto funciona. ¿Significa esto que, al crear funciones en lugar de decir this.functionName, debería escribir window.functionName? ¿Tenemos alguna otra solución para esto? ¿Alguna razón para usar window.functionName?
SharpCoder
21
"Se supone que debes buscar tu código junto con el módulo" - Uf, ¿qué pasa si quiero hacer algo como onclick="someFunction()". ¡¿No puede estar argumentando que ese es un caso de uso poco común?!
BlueRaja - Danny Pflughoeft
57
Existe una gran falta de documentación en cualquier lugar para principiantes sobre cómo usar realmente Browserify en el cliente.
Oliver Dixon
1
sí, la documentación debe indicar claramente que esta es una decisión de diseño que debe evitarse, pero proporcionar una ruta clara para que funcione cuando no tenga una alternativa (en mi caso, usar datos de la plantilla para completar un objeto JS) ... ¡gracias @thejh por señalar una solución simple! ;)
Alexandre Martini
1
Ni siquiera puedo pensar en una situación en la que NO quieras que tus funciones principales estén disponibles fuera del módulo. ¿Cómo no es este un comportamiento predeterminado? ¿Qué tipo de aplicación web no llama a funciones?
Cybernetic
101

La parte clave de agrupar módulos independientes con Browserify es la --sopción. Expone todo lo que exporta desde su módulo utilizando nodos module.exportscomo una variable global. Luego, el archivo se puede incluir en una <script>etiqueta.

Solo necesita hacer esto si por alguna razón necesita que esa variable global esté expuesta. En mi caso, el cliente necesitaba un módulo independiente que pudiera incluirse en las páginas web sin necesidad de preocuparse por este negocio de Browserify.

Aquí hay un ejemplo donde usamos la --sopción con un argumento de module:

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

Esto expondrá nuestro módulo como una variable global llamada module.
Fuente .

Actualización: Gracias a @fotinakis. Asegúrate de pasar --standalone your-module-name. Si olvida que --standalonetoma un argumento, Browserify podría generar silenciosamente un módulo vacío ya que no pudo encontrarlo.

Espero que esto te ahorre algo de tiempo.

Matas Vaitkevicius
fuente
2
Estoy intentando navegar por el código ES6 babelificado. Pero el objeto independiente está vacío cuando intento consolarlo en el navegador. El código ES6 simple sin ningún módulo funciona bien en modo independiente. ¿Algún consejo sobre esto?
John
@jackyrudetsky no tengo idea, recomendaría agregar una pregunta sobre SO, suena como un tema interesante. podría estar relacionado con esto. github.com/substack/node-browserify/issues/1357
Matas Vaitkevicius
1
@fotinakis En realidad, fue un problema en Browserify github.com/substack/node-browserify/issues/1537
John
3
En mi opinión, esta debería ser la respuesta aceptada. Si está utilizando una función global, es mucho mejor tener su propio espacio de nombres que colgar todas las funciones fuera de la ventana.
VictorB
1
@VictorB todas las variables globales en Javascript son elementos de la ventana, por lo que ambos métodos logran lo mismo (agregando las variables globales a la ventana)
David Lopez
37

La respuesta de @Matas Vaitkevicius con la opción independiente de Browserify es correcta (la respuesta de @ thejh usando la variable global de ventana también funciona, pero como otros han señalado, contamina el espacio de nombres global, por lo que no es ideal). Quería agregar un poco más de detalles sobre cómo usar la opción independiente.

En la secuencia de comandos de origen que desea empaquetar, asegúrese de exponer las funciones que desea llamar a través de module.exports. En el script del cliente, puede llamar a estas funciones expuestas mediante <bundle-name>. <func-name> . He aquí un ejemplo:

Mi archivo fuente src / script.js tendrá esto:
module.exports = {myFunc: func};

Mi comando browserify se verá así:
browserify src/script.js --standalone myBundle > dist/bundle.js

Y mi script de cliente dist / client.js cargará el script incluido
<script src="bundle.js"></script>
y luego llamará a la función expuesta de esta manera:
<script>myBundle.myFunc();</script>


No es necesario solicitar el nombre del paquete en el script del cliente antes de llamar a las funciones expuestas, por ejemplo, <script src="bundle.js"></script><script>var bundled = require("myBundle"); bundled.myFunc();</script>no es necesario y no funcionará.

De hecho, al igual que todas las funciones incluidas en browserify sin el modo independiente, la función require no estará disponible fuera del script incluido . Browserify le permite utilizar algunas funciones de Node en el lado del cliente, pero solo en el propio script incluido ; no está destinado a crear un módulo independiente que pueda importar y usar en cualquier lugar del lado del cliente, por lo que tenemos que hacer todo este problema adicional solo para llamar a una sola función fuera de su contexto incluido.

Galen Long
fuente
3
¡Guauu! Finalmente un ejemplo práctico.
N73k
1
Buen ejemplo, pero en la medida en que "contamina el espacio de nombres global, por lo tanto, no es ideal" no sigue automáticamente, puede ser aceptable si es solo una función; Solo humo y espejos, incluso myBundlese adjunta al objeto de la ventana, en window.myBundle.myFunc()lugar de window.myFunc ()
joedotnot
1
Debería haber puntos extra para las personas que dan ejemplos de un extremo a otro.
Sharud
Así es como se debe escribir la documentación
Ellery Leung
8

Acabo de leer las respuestas y parece que nadie mencionó el uso del alcance de la variable global. Lo cual es útil si desea utilizar el mismo código en node.js y en el navegador.

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

Entonces puede acceder a TestClass en cualquier lugar.

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

Nota: TestClass estará disponible en todas partes. Que es lo mismo que usar la variable de ventana.

Además, puede crear un decorador que exponga una clase al ámbito global. Lo cual es realmente bueno, pero dificulta el seguimiento de dónde se define una variable.

DDD
fuente
Como usted mismo dice, agregar la función a globalproduce el mismo efecto que agregar a window, que ya estaba cubierto por jh. Esta respuesta no agrega información nueva.
Galen Long
@GalenLong ¿tal vez olvidó que no hay una variable de ventana en node.js? Y algunas bibliotecas que apuntan al nodo y al navegador pueden querer usar global en su lugar. Mi respuesta obtuvo algunos votos a favor y aún no está en menos, así que creo que es informativo para otros, si no para usted.
DDD
Tienes razón, @Azarus. Había otras dos respuestas duplicadas en la página e incluí incorrectamente la suya en el grupo. Mis disculpas.
Galen Long
solo quiero tener en cuenta que los parens colgantes aquí son una muy mala práctica para javascript, por ejemplo: aplique este patrón a la palabra clave return y prepárese para llorar. por ejemplo, return {}pero suelte la llave de apertura hasta la siguiente línea.
Sgnl
1
@Azarus Creé un violín para demostrar lo que quiero decir - jsfiddle.net/cubaksot/1
Sgnl
6

Leer README.md de browserify sobre el --standaloneparámetro o google "browserify umd"

deshacerZen
fuente
19
Esto es más una pista sobre dónde encontrar una respuesta que una respuesta.
user2314737
esto me llevó a la solución que estaba buscando durante dos días (cómo usar la salida de browserify de un entorno require.js). ¡gracias!
Flion
2

Para tener su función disponible tanto desde HTML como desde el nodo del lado del 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>

Correr:

npm install uniq
browserify main.js > bundle.js

y debería obtener los mismos resultados al abrir main.html en un navegador que al ejecutar

node main.js
Ori Miller
fuente
2

Ejemplo mínimo ejecutable

Esto es básicamente lo mismo que: https://stackoverflow.com/a/43215928/895245 pero con archivos concretos que le permitirán ejecutarlo y reproducirlo fácilmente usted mismo.

Este código también está disponible en: 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());

Generar out.jspara uso del navegador:

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

Tanto el navegador como la línea de comandos muestran el resultado esperado:

1 2 3

Probado con Browserify 16.5.0, Node.js v10.15.1, Chromium 78, Ubuntu 19.10.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
1
La exports.myfunc.= myfuncparte de esto fue absolutamente crítica y se perdió en otras respuestas.
parttimeturtle
2

es realmente simple: todo este concepto se trata de envolver

1. alternativa - objetar "esto"

para este propósito, asumiré que tiene "solo 1 secuencia de comandos para toda la aplicación {{app_name}}" y "1 función {{function_name}}"

agregar la función {{function_name}} para objetar "esto"

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

luego debe nombrar ese objeto para que esté disponible; lo hará agregue el parámetro "independiente con nombre" como otros aconsejaron

por lo que si usa "watchify" con "browserify", use este

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

o línea de comando

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

entonces puedes llamar a tu función desde el navegador

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

2. alternativa - objeto "ventana"

agregar la función {{function_name}} al objeto "ventana"

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

entonces puedes llamar a tu función desde el navegador

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

-

tal vez ayude a alguien

BG BRUNO
fuente
1

Tienes pocas opciones:

  1. Deje que el plugin browserify-bridge exporte automáticamente los módulos a un módulo de entrada generado. Esto es útil para proyectos de SDK o situaciones en las que no tiene que mantenerse al día manualmente con lo que se exporta.

  2. Siga un patrón de pseudo-espacio de nombres para la exposición acumulada:

Primero, organice su biblioteca de esta manera, aprovechando las búsquedas de índices en carpetas:

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

Con este patrón, define una entrada como esta:

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

Observe que require carga automáticamente el index.js de cada subcarpeta respectiva

En sus subcarpetas, puede incluir un manifiesto similar de los módulos disponibles en ese contexto:

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

Este patrón se escala realmente bien y permite un seguimiento contextual (carpeta por carpeta) de lo que se debe incluir en la API acumulada.

profundización
fuente
-1
window.LogData =function(data){
   return unique(data);
};

Llame a la función simplemente por LogData(data)

Esta es solo una pequeña modificación a la respuesta de thejh, pero es importante

Pratik Khadtale
fuente
Esta modificación es irrelevante para las preocupaciones del autor de la pregunta y no agrega ninguna información nueva dadas las respuestas ya existentes.
Galen Long
-2

Para fines de depuración, agregué esta línea a mi code.js:

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

Entonces podría ejecutar cualquier cosa incluso fuera del paquete.

e("anything();");
Karveiani
fuente