Browserify - Comment appeler la fonction regroupée dans un fichier généré via browserify dans le navigateur


95

Je suis nouveau sur nodejs et browserify. J'ai commencé avec ce lien .

J'ai le fichier main.js qui contient ce code

var unique = require('uniq');

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

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

Maintenant, j'installe le module uniq avec npm:

 npm install uniq

Ensuite, je regroupe tous les modules requis à partir de main.js dans un seul fichier appelé bundle.js avec la commande browserify:

browserify main.js -o bundle.js

Le fichier généré ressemble à ceci:

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

Après avoir inclus le fichier bundle.js dans ma page index.htm, comment appeler la fonction logData ??


Où voulez-vous l'appeler? Et pourquoi voulez-vous l'appeler?
artur grzesiak

2
@arturgrzesiak: Je souhaite utiliser cette fonction dans l'un de mes autres projets que je vais exécuter dans le navigateur.
SharpCoder

Réponses:


80

Par défaut, browserify ne vous permet pas d'accéder aux modules depuis l'extérieur du code navigateur - si vous souhaitez appeler du code dans un module navigateur, vous êtes censé explorer votre code avec le module. Voir http://browserify.org/ pour des exemples de cela.

Bien sûr, vous pouvez également rendre votre méthode explicitement accessible de l'extérieur comme ceci:

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

Ensuite, vous pouvez appeler LogData()de n'importe où ailleurs sur la page.


1
Je vous remercie. Cela marche. Cela signifie-t-il que lors de la création de fonctions au lieu de dire this.functionName, je devrais écrire window.functionName? Avons-nous d'autres solutions pour cela? Des raisons d'utiliser window.functionName?
SharpCoder

19
"vous êtes censé explorer votre code avec le module" - Ugh, et si je veux faire quelque chose comme onclick="someFunction()". Vous ne pouvez pas prétendre que c'est un cas d'utilisation rare!?!
BlueRaja - Danny Pflughoeft

55
Il existe un grave manque de documentation pour les débutants sur la façon d'utiliser réellement Browserify sur le client.
Oliver Dixon

1
oui, la documentation doit clairement indiquer qu'il s'agit d'une décision de conception à éviter, mais fournir un chemin clair pour le faire fonctionner lorsque vous n'avez pas d'alternative (dans mon cas, utiliser les données du modèle pour peupler un objet JS) ... merci @thejh pour avoir indiqué une solution simple! ;)
Alexandre Martini

1
Je ne peux même pas penser à une situation où vous ne voudriez PAS rendre vos principales fonctions disponibles en dehors du module. En quoi ce comportement n'est-il pas par défaut? Quel type d'application Web n'appelle pas de fonctions?
Cybernetic

100

L'élément clé du regroupement de modules autonomes avec Browserify est l' --soption. Il expose tout ce que vous exportez depuis votre module en utilisant les nœuds module.exportscomme variable globale. Le fichier peut alors être inclus dans une <script>balise.

Vous ne devez le faire que si, pour une raison quelconque, vous avez besoin que cette variable globale soit exposée. Dans mon cas, le client avait besoin d'un module autonome qui pourrait être inclus dans les pages Web sans avoir à se soucier de cette entreprise Browserify.

Voici un exemple où nous utilisons l' --soption avec un argument de module:

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

Cela exposera notre module en tant que variable globale nommée module.
Source .

Mise à jour: Merci à @fotinakis. Assurez-vous de passer --standalone your-module-name. Si vous oubliez que --standaloneprend un argument, Browserify peut générer silencieusement un module vide car il ne le trouve pas.

J'espère que cela vous fera gagner du temps.


2
J'essaye de browserify code ES6 babelified. Mais l'objet autonome est vide lorsque j'essaye de le consoler dans le navigateur. Un code ES6 simple sans aucun module fonctionne très bien en mode autonome. Des conseils à ce sujet?
John

@jackyrudetsky aucune idée, je recommanderais d'ajouter une question sur SO, cela semble être un problème intéressant. pourrait être lié à cela. github.com/substack/node-browserify/issues/1357
Matas Vaitkevicius

1
@fotinakis C'était en fait un problème dans Browserify github.com/substack/node-browserify/issues/1537
John

3
OMI, cela devrait être la réponse acceptée. Si vous utilisez une fonction globale, il est bien préférable d'avoir votre propre espace de noms plutôt que de suspendre chaque fonction hors de la fenêtre.
VictorB

1
@VictorB toutes les variables globales en Javascript sont des éléments de window, donc les deux méthodes réalisent la même chose (ajout des variables globales à window)
David Lopez

37

La réponse de @Matas Vaitkevicius avec l'option autonome de Browserify est correcte (la réponse de @ thejh en utilisant la variable globale window fonctionne également, mais comme d'autres l'ont noté, elle pollue l'espace de noms global donc ce n'est pas idéal). Je voulais ajouter un peu plus de détails sur la façon d'utiliser l'option autonome.

Dans le script source que vous souhaitez regrouper, assurez-vous d'exposer les fonctions que vous souhaitez appeler via module.exports. Dans le script client, vous pouvez appeler ces fonctions exposées via <bundle-name>. <func-name> . Voici un exemple:

Mon fichier source src / script.js aura ceci:
module.exports = {myFunc: func};

Ma commande browserify ressemblera à ceci:
browserify src/script.js --standalone myBundle > dist/bundle.js

Et mon script client dist / client.js chargera le script fourni
<script src="bundle.js"></script>
, puis appellera la fonction exposée comme ceci:
<script>myBundle.myFunc();</script>


Il n'est pas nécessaire d'exiger le nom du bundle dans le script client avant d'appeler les fonctions exposées, par exemple <script src="bundle.js"></script><script>var bundled = require("myBundle"); bundled.myFunc();</script>n'est pas nécessaire et ne fonctionnera pas.

En fait, tout comme toutes les fonctions fournies par browserify sans mode autonome, la fonction require ne sera pas disponible en dehors du script fourni . Browserify vous permet d'utiliser certaines fonctions Node côté client, mais uniquement dans le script fourni lui-même ; il n'est pas destiné à créer un module autonome que vous pouvez importer et utiliser n'importe où côté client, c'est pourquoi nous devons nous attaquer à tous ces problèmes supplémentaires simplement pour appeler une seule fonction en dehors de son contexte groupé.


3
Hou la la! Enfin un exemple pratique.
N73k

1
Bon exemple, mais dans la mesure où "il pollue l'espace de noms global donc pas idéal" ne suit pas automatiquement, cela peut être acceptable s'il ne s'agit que d'une fonction; Juste de la fumée et des miroirs, myBundlese fixe même à l'objet window, window.myBundle.myFunc()au lieu de window.myFunc ()
joedotnot

1
Il devrait y avoir des points supplémentaires pour les personnes qui donnent des exemples de bout en bout.
Sharud

C'est ainsi que la documentation doit être écrite
Ellery Leung

7

Je viens de lire les réponses et il semble que personne n'ait mentionné l'utilisation de la portée de la variable globale? Ce qui est utile si vous souhaitez utiliser le même code dans node.js et dans le navigateur.

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

Ensuite, vous pouvez accéder à la TestClass n'importe où.

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

Remarque: La TestClass devient alors disponible partout. Ce qui revient à utiliser la variable window.

En outre, vous pouvez créer un décorateur qui expose une classe à la portée globale. Ce qui est vraiment sympa mais rend difficile le suivi de l'endroit où une variable est définie.


Comme vous le dites vous-même, ajouter la fonction à globalproduit le même effet que l'ajout à window, qui était déjà couvert par thejh. Cette réponse n'ajoute aucune nouvelle information.
Galen Long

@GalenLong peut-être avez-vous oublié qu'il n'y a pas de variable de fenêtre dans node.js? Et certaines bibliothèques qui ciblent le nœud et le navigateur peuvent vouloir utiliser global à la place. Ma réponse a reçu quelques upvotes et n'est pas encore moins, donc je pense que c'est informatif pour les autres sinon pour vous.
DDD

Vous avez raison, @Azarus. Il y avait deux autres réponses en double sur la page et j'ai incorrectement inclus la vôtre dans le groupe. Mes excuses.
Galen Long

Je veux juste noter que les parenthèses suspendues ici sont une très mauvaise pratique pour javascript, par exemple: appliquez ce modèle au mot-clé return et préparez-vous à pleurer. par exemple, return {}mais déposez l'accolade d'ouverture jusqu'à la ligne suivante.
Sgnl

1
@Azarus J'ai créé un violon pour démontrer ce que je veux dire - jsfiddle.net/cubaksot/1
Sgnl

6

Lire README.md de browserify sur le --standaloneparamètre ou google "browserify umd"


19
C'est plus un indice sur où trouver une réponse qu'une réponse.
user2314737

cela m'a conduit à la solution que je recherchais depuis deux jours (comment utiliser la sortie browserify d'un environnement require.js). Merci!
Flion

2

Pour que votre fonction soit disponible à la fois à partir du HTML et du nœud côté serveur:

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>

Courir:

npm install uniq
browserify main.js > bundle.js

et vous devriez obtenir les mêmes résultats lors de l'ouverture de main.html dans un navigateur que lors de l'exécution

node main.js

2

Exemple exécutable minimal

C'est fondamentalement le même que: https://stackoverflow.com/a/43215928/895245 mais avec des fichiers concrets qui vous permettront de simplement l'exécuter et de le reproduire facilement vous-même.

Ce code est également disponible sur: 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>

Utilisation de Node.js:

#!/usr/bin/env node

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

console.log(browserify_hello_world.myfunc());

Générer out.jspour l'utilisation du navigateur:

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

Le navigateur et la ligne de commande affichent le résultat attendu:

1 2 3

Testé avec Browserify 16.5.0, Node.js v10.15.1, Chromium 78, Ubuntu 19.10.


1
La exports.myfunc.= myfuncpartie de ceci était absolument critique et manquait dans d'autres réponses.
parttimeturtle

1

Vous avez quelques options:

  1. Laissez le plugin browserify-bridge exporter automatiquement les modules vers un module d'entrée généré. Cela est utile pour les projets SDK ou les situations où vous n'avez pas à suivre manuellement ce qui est exporté.

  2. Suivez un modèle de pseudo-espace de noms pour l'exposition cumulative:

Commencez par organiser votre bibliothèque comme ceci, en tirant parti des recherches d'index sur les dossiers:

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

Avec ce modèle, vous définissez l'entrée comme ceci:

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

Notez que le require charge automatiquement le fichier index.js de chaque sous-dossier respectif

Dans vos sous-dossiers, vous pouvez simplement inclure un manifeste similaire des modules disponibles dans ce contexte:

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

Ce modèle s'adapte très bien et permet un suivi contextuel (dossier par dossier) de ce qu'il faut inclure dans l'API roll-up.


1

c'est vraiment simple - tout ce concept concerne l'emballage

1. alternative - objet "ceci"

à cet effet, je suppose que vous n'avez "qu'un seul script pour l'ensemble de l'application {{app_name}}" et "1 fonction {{function_name}}"

ajouter la fonction {{function_name}} à l'objet "this"

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

alors vous devez nommer cet objet pour être disponible - vous le ferez ajouter le paramètre "autonome avec nom" comme d'autres l'ont conseillé

donc si vous utilisez "watchify" avec "browserify" utilisez ceci

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

ou ligne de commande

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

alors vous pouvez appeler votre fonction depuis le navigateur

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

2. alternative - objet "fenêtre"

ajouter la fonction {{function_name}} à l'objet "window"

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

alors vous pouvez appeler votre fonction depuis le navigateur

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

-

peut-être que j'aide quelqu'un


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

Appelez la fonction simplement en LogData(data)

Ceci est juste une légère modification de la réponse de la jh mais importante


Cette modification est sans rapport avec les préoccupations du demandeur et n'ajoute aucune nouvelle information étant donné les réponses déjà existantes.
Galen Long

-2

À des fins de débogage, j'ai ajouté cette ligne à mon code.js:

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

Ensuite, je pourrais exécuter n'importe quoi, même en dehors du bundle.

e("anything();");
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.