Browserify-如何调用在浏览器中通过 Browserify 生成的文件中绑定的函数

我是新的 nodejs 和浏览器。我从这个 链接开始。

我有一个包含这段代码的 main.js 文件

var unique = require('uniq');


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


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

现在我用 npm 安装 uniq 模块:

 npm install uniq

然后,我使用 Browserify 命令将所有从 main.js 开始的必需模块绑定到一个名为 bundle.js 的文件中:

browserify main.js -o bundle.js

生成的文件如下所示:

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

在将 bundle.js 文件包含到 index.htm 页面之后,如何调用 logData 函数?

47664 次浏览

By default, browserify doesn't let you access the modules from outside of the browserified code – if you want to call code in a browserified module, you're supposed to browserify your code together with the module. See http://browserify.org/ for examples of that.

Of course, you could also explicitly make your method accessible from outside like this:

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

Then you could call LogData() from anywhere else on the page.

Read README.md of browserify about --standalone parameter or google "browserify umd"

The key part of bundling standalone modules with Browserify is the --s option. It exposes whatever you export from your module using node's module.exports as a global variable. The file can then be included in a <script> tag.

You only need to do this if for some reason you need that global variable to be exposed. In my case the client needed a standalone module that could be included in web pages without them needing to worry about this Browserify business.

Here's an example where we use the --s option with an argument of module:

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

This will expose our module as a global variable named module.
Source.

Update: Thanks to @fotinakis. Make sure you're passing --standalone your-module-name. If you forget that --standalone takes an argument, Browserify might silently generate an empty module since it couldn't find it.

Hope this saves you some time.

You have a few options:

  1. Let plugin browserify-bridge auto-export the modules to a generated entry module. This is helpful for SDK projects or situations where you don't have to manually keep up with what is exported.

  2. Follow a pseudo-namespace pattern for roll-up exposure:

First, arrange your library like this, taking advantage of index look-ups on folders:

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

With this pattern, you define entry like this:

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

Notice the require automatically loads the index.js from each respective sub-folder

In your subfolders, you can just include a similar manifest of the available modules in that context:

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

This pattern scales really well and allows for contextual (folder by folder) tracking of what to include in the rolled-up api.

@Matas Vaitkevicius's answer with Browserify's standalone option is correct (@thejh's answer using the window global variable also works, but as others have noted, it pollutes the global namespace so it's not ideal). I wanted to add a little more detail on how to use the standalone option.

In the source script that you want to bundle, make sure to expose the functions you want to call via module.exports. In the client script, you can call these exposed functions via <bundle-name>.<func-name>. Here's an example:

My source file src/script.js will have this:
module.exports = {myFunc: func};

My browserify command will look something like this:
browserify src/script.js --standalone myBundle > dist/bundle.js

And my client script dist/client.js will load the bundled script
<script src="bundle.js"></script>
and then call the exposed function like this:
<script>myBundle.myFunc();</script>


There's no need to require the bundle name in the client script before calling the exposed functions, e.g. <script src="bundle.js"></script><script>var bundled = require("myBundle"); bundled.myFunc();</script> isn't necessary and won't work.

In fact, just like all functions bundled by browserify without standalone mode, the require function won't be available outside of the bundled script. Browserify allows you to use some Node functions client-side, but only in the bundled script itself; it's not meant to create a standalone module you can import and use anywhere client-side, which is why we have to go to all this extra trouble just to call a single function outside of its bundled context.

I just read through the answers and seems like nobody mentioned the use of the global variable scope? Which is usefull if you want to use the same code in node.js and in the browser.

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

Then you can access the TestClass anywhere.

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

Note: The TestClass then becomes available everywhere. Which is the same as using the window variable.

Additionally you can create a decorator that exposes a class to the global scope. Which is really nice but makes it hard to track where a variable is defined.

For debugging purposes I added this line to my code.js:

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

Then I could run anything even outside the bundle.

e("anything();");
window.LogData =function(data){
return unique(data);
};

Call the function simply by LogData(data)

This is just a slight modification to thejh's answer but important one

Whole concept is about wrapping.

1.) Alternative - Object "this"

for this purpose I'll assume you have "only 1 script for whole app \{\{app_name}}" and "1 function \{\{function_name}}"

add function \{\{function_name}}

function \{\{function_name}}(param) { ... }

to object this

this.\{\{function_name}} = function(param) { ... }

then you have to name that object to be available - you will do it add param "standalone with name" like others advised

so if you use "watchify" with "browserify" use this

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

or command line

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

then you can call the function directly

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

2.) Alternative - Object "window"

add function \{\{function_name}}

function \{\{function_name}}(param) { ... }

to object window

window.\{\{function_name}} = function(param) { ... }

then you can call the function directly

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

To have your function available from both the HTML and from server-side node:

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>

Run:

npm install uniq
browserify main.js > bundle.js

and you should get same results when opening main.html in a browser as when running

node main.js

Minimal runnable example

This is basically the same as: https://stackoverflow.com/a/43215928/895245 but with concrete files that will allow you to just run and easily reproduce it yourself.

This code is also available at: 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>

Node.js usage:

#!/usr/bin/env node


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


console.log(browserify_hello_world.myfunc());

Generate out.js for browser usage:

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

Both the browser and the command line show the expected output:

1 2 3

Tested with Browserify 16.5.0, Node.js v10.15.1, Chromium 78, Ubuntu 19.10.

You can also call your function from the html file like this:

main.js: (will be in bundle.js)

window.onload = function () {
document.getElementById('build-file')
.addEventListener('click', buildFile)


}
function buildFile() {
...
}

index.html:

<button id="build-file"">Build file</button>