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


95

我是nodejs和browserify的新手。我从这个链接开始。

我有文件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函数?


您想在哪里打电话?为什么要调用它?
artur grzesiak 2014年

2
@arturgrzesiak:我想在我将在浏览器中运行的另一个项目中利用此功能。
SharpCoder 2014年

Answers:


80

默认情况下,browserify不允许您从浏览器化的代码之外访问模块-如果要在浏览器化的模块中调用代码,则应将代码与模块一起浏览器化。有关示例,请参见http://browserify.org/

当然,您也可以像这样从外部显式地访问您的方法:

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

然后,您可以LogData()从页面上的其他任何地方打电话。


1
谢谢。这有效。这是否意味着在创建函数而不是说this.functionName时,我应该写window.functionName?我们还有其他解决方法吗?使用window.functionName的任何原因?
SharpCoder 2014年

19
“您应该将您的代码与模块一起浏览” -哎呀,如果我想做类似的事情onclick="someFunction()"。您不可能争论那是一种罕见的用例!?!
BlueRaja-Danny Pflughoeft 2015年

55
对于初学者来说,在任何地方都缺少关于如何在客户端上实际使用Browserify的文档。
奥利弗·迪克森

1
是的,文档中应明确指出这是应避免的设计决策,但是当您没有其他选择时(如果使用我的情况,请使用模板中的数据填充JS对象),请提供一条清晰的途径使其能够正常工作...感谢@thejh指出一个简单的解决方案!;)
亚历山德拉·马提尼

1
我什至无法想到您不想在模块之外使用主要功能的情况。这不是默认行为吗?哪种Web应用程序不调用函数?
控制论

100

--s选项是将独立模块与Browserify捆绑在一起的关键部分。它公开了使用node的module.exports全局变量从模块导出的任何内容。然后可以将该文件包含在<script>标签中。

仅在出于某些原因需要公开该全局变量的情况下才需要执行此操作。在我的情况下,客户端需要一个独立的模块,该模块可以包含在网页中,而无需担心Browserify业务。

这是一个示例,其中我们将--s选项与参数一起使用module

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

这会将我们的模块显示为名为的全局变量module
来源

更新: 感谢@fotinakis。确保您通过--standalone your-module-name。如果您忘记了--standalone该参数,Browserify可能会静默生成一个空模块,因为找不到它。

希望这可以节省您一些时间。


2
我正在尝试浏览器化ES6代码。但是,当我尝试在浏览器中对其进行操作时,独立对象为空。没有任何模块的简单ES6代码在独立模式下可以正常工作。关于这个有什么建议吗?
约翰

@jackyrudetsky不知道,我建议添加一个关于SO的问题,听起来像是一个有趣的问题。可能与此有关。github.com/substack/node-browserify/issues/1357
Matas Vaitkevicius,

1
@fotinakis这实际上是Browserify上的一个问题github.com/substack/node-browserify/issues/1537
约翰

3
海事组织,这应该是公认的答案。如果使用的是全局函数,那么拥有自己的名称空间比将每个函数挂在窗口之外要好得多。
VictorB

1
@VictorB Javascript中的所有全局变量都是window的元素,因此这两种方法都实现了相同的事情(将全局变量添加到window中)
David Lopez

37

@Matas Vaitkevicius的带有Browserify独立选项答案是正确的(使用窗口全局变量的 @thejh的答案也有效,但正如其他人所指出的那样,它污染了全局名称空间,因此并不理想)。我想添加更多有关如何使用独立选项的详细信息。

在要捆绑的源脚本中,确保公开要通过module.exports调用的函数。在客户端脚本中,可以通过<bundle-name>。<func-name>调用这些公开的函数。这是一个例子:

我的源文件src / script.js将具有以下内容:
module.exports = {myFunc: func};

我的browserify命令如下所示:
browserify src/script.js --standalone myBundle > dist/bundle.js

我的客户端脚本dist / client.js将加载捆绑的脚本
<script src="bundle.js"></script>
,然后像下面这样调用公开的函数:
<script>myBundle.myFunc();</script>


在调用公开的函数之前,不需要在客户端脚本中要求捆绑软件名称,例如,这<script src="bundle.js"></script><script>var bundled = require("myBundle"); bundled.myFunc();</script>是不必要的,将不起作用。

实际上,就像在没有独立模式的情况下,browserify捆绑的所有功能一样,require功能在捆绑的脚本之外也不可用。Browserify允许您在客户端使用某些Node函数,但只能在捆绑的脚本本身中使用;这并不是要创建一个可以在客户端的任何地方导入和使用的独立模块,这就是为什么我们不得不麻烦所有这些麻烦,只是为了在其绑定上下文之外调用单个函数。


3
哇!最后是一个实际的例子。
N73k

1
很好的例子,但是就“它污染了全局名称空间,因此不理想”而言,它不会自动遵循,如果它只是一个函数,那可能是可以接受的。只是烟雾和镜子,甚至myBundle附着在窗口对象上, window.myBundle.myFunc()而不是window.myFunc()
joedotnot19年

1
对于提供端到端示例的人,应该有更多的要点。
Sharud

那就是应该编写文档的方式
Ellery Leung

7

我只是通读了答案,似乎没有人提到全局变量作用域的使用?如果要在node.js和浏览器中使用相同的代码,这很有用。

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

然后,您可以在任何地方访问TestClass

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

注意:然后,TestClass随处可用。这与使用window变量相同。

另外,您可以创建一个将类暴露给全局范围的装饰器。这确实不错,但是很难跟踪定义变量的位置。


就像您自己说的那样,将功能添加到global会产生与添加到相同的效果window,这已经被thejh所涵盖。该答案没有添加任何新信息。
加伦·隆

@GalenLong也许您忘记了node.js中没有window变量?而且,某些针对节点和浏览器的库可能希望使用全局库。我的回答得到了一些好评,而且还没有减少,所以我认为它对其他人的信息有益,即使不是对您也有用。
DDD

你说得对,@ Azarus。页面上还有另外两个重复的答案,而我不正确地将您的答案包括在内。我很抱歉。
加伦·隆

只是要注意,这里的悬挂式括号对于javascript是非常糟糕的做法,例如:将这种模式应用于return关键字并准备哭泣。例如,return {}但是将左花括号放下一行。
Sgnl

1
@Azarus我创建了一个小提琴来演示我的意思-jsfiddle.net/cubaksot/1
Sgnl

6

阅读关于--standalone参数的browserify的README.md 或Google“ browserify umd”


19
这比在答案上更能提示在哪里找到答案。
user2314737 2014年

这使我找到了我一直在寻找的解决方案两天(如何使用require.js环境中的browserify输出)。谢谢!
Flion 2014年

2

要使功能可从HTML和服务器端节点使用:

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>

跑:

npm install uniq
browserify main.js > bundle.js

在浏览器中打开main.html时,运行时应该得到相同的结果

node main.js

2

最小的可运行示例

这基本上与:https//stackoverflow.com/a/43215928/895245相同,但是带有一些具体文件,可以让您直接运行并轻松地自己复制它。

该代码也可以在以下网址获得: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的用法:

#!/usr/bin/env node

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

console.log(browserify_hello_world.myfunc());

生成out.js供浏览器使用:

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

浏览器和命令行都显示预期的输出:

1 2 3

在Browserify 16.5.0,Node.js v10.15.1,Chromium 78,Ubuntu 19.10上进行了测试。


1
exports.myfunc.= myfunc部分是绝对关键的,并在其他答案中遗漏了。
parttimeturtle

1

您有几种选择:

  1. 让插件browserify-bridge自动将模块导出到生成的输入模块。这对于SDK项目或无需手动跟上导出内容的情况很有用。

  2. 遵循伪命名空间模式进行汇总暴露:

首先,像这样安排您的库,利用对文件夹的索引查找:

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

使用这种模式,您可以这样定义条目:

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

注意,require会自动从每个子文件夹中加载index.js

在子文件夹中,您可以仅在该上下文中包括可用模块的类似清单:

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

此模式的缩放比例非常好,并允许上下文(逐个文件夹)跟踪要包含在汇总api中的内容。


1

这非常简单-整个概念都是关于包装

1.替代-对象“ this”

为此,我假设您只有“整个应用{{app_name}}仅有1个脚本”和“ 1个函数{{function_name}}有1个脚本”

将函数{{function_name}}添加到对象“ this”

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

那么您必须命名该对象以使其可用-您将像其他建议的那样添加“带有名称的独立对象”参数

因此,如果您将“ watchify”和“ browserify”一起使用

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

或命令行

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

然后您可以从浏览器调用函数

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

2.替代-对象“窗口”

将函数{{function_name}}添加到对象“窗口”

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

然后您可以从浏览器调用函数

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

-

也许我帮助某人


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

只需通过以下方式调用函数 LogData(data)

这只是对thejh答案的轻微修改,但很重要


此修改与提问者所关心的问题无关,并且鉴于已经存在的答案,因此不会添加任何新信息。
加伦·朗

-2

为了调试,我在我的code.js中添加了以下行:

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

然后,我什至可以在捆绑包之外运行任何东西。

e("anything();");
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.