【问题标题】:Accessing "public" members after bundling with browserify or webpack与 browserify 或 webpack 捆绑后访问“公共”成员
【发布时间】:2017-01-25 23:20:54
【问题描述】:

我有一个test.js 脚本,它定义了一个App 类,它是从一个HTML 文件加载的,一切正常。

当我使用 browserify 或 webpack 从 test.js 创建 testBundle.js 包时,testBundle.js 中的 App 类似乎不再定义。

我应该如何编写代码或者我应该给 browserify 什么选项来定义 App 并像以前一样从 HTML 中使用它,但是从包中使用它?

捆绑后我得到的错误是:

Uncaught ReferenceError: App is not defined

html文件如下:

<html>
   <script src="testBundle.js"></script>

   <script>
       var app = new App();
   </script>
</html>

test.js:

'use strict';

class App {
    constructor() {
        console.log("App ctor")
    }
}

构建包的命令:

browserify -t [ babelify --presets [ es2015 ] ] test.js  -o testBundle.js 

在我看来,从包的内部看,App 实际上是私有的。

testBundle.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);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.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){
'use strict';

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var App = function App() {
    _classCallCheck(this, App);

    console.log("App ctor");
};


},{}]},{},[1]);

所有这些都使用浏览器中的 Javascript,ES6。

【问题讨论】:

  • 对不起,但我相信(希望)有更好的方法来解决这个问题,我开始赏金了

标签: javascript webpack bundle browserify


【解决方案1】:

您正尝试在全局上下文中访问 App。但是,默认情况下,App 不是全局可用的。 Node.js 使用模块,Browserify 尊重模块提供的封装。在使用 Browserify 或 Webpack 等工具时,最常见的情况是拥有一个或多个入口点模块(即文件),这些模块使用模块导入/require() 来访问其他模块。

但是如果您希望能够在浏览器内的全局上下文中访问App,您可以将App 类的引用分配给test.js 中window 的属性:

例如

window.App = App;

Object.assign(window, { App });

【讨论】:

  • 这行得通,但是如果我有其他类,我必须在每个脚本的末尾使用 windows.MyClass = MyClass “导出”每个类?我希望有另一种解决方案
  • 从其他模块访问实体的正常方法是使用 import/export 或 require()。您希望通过让您的所有类全局可访问来规避这一点,这不是 Browserify 或 Webpack 的预期用途。您可能不需要在您的 html 中有任何单独的 &lt;script&gt;&lt;/script&gt; ,而是应该有一个入口点 js 文件,该文件导入/需要它所依赖的脚本。那应该是你指定给 Browserify 或 Webpack 的入口文件。
  • 你说“应该有一个入口点 js 文件来导入/需要脚本”,但我正在使用浏览器中的 javascript 并且不能使用 import 或 require...
  • 这是 Browserify 和 Webpack 处理的事情之一。通过将您的 JS 转换为单个捆绑文件,它们还可以解析您的导入/导出/要求,以便您的代码在浏览器中运行时可以正常工作。我强烈建议您在继续阅读之前花时间阅读有关 Browserify/Webpack、它们提供的内容、它们为何有用以及它们如何工作的信息。
  • 您所描述的是将多个 JS 文件串联成一个 JS 文件。肯定有很多工具可以做到这一点,不一定只适用于 JS。效果与使用 &lt;script src="..."&gt; 将每个单独的 JS 文件包含到您的 html 中相同。或者,如果您没有太多代码,您可以将它们全部保存在一个文件中,因此无需连接您的 JS。但归根结底,处理多个 JS 文件的最简洁和推荐的方法是将它们视为模块并使用模块捆绑器(例如 Webpack、Rollup 或 Browserify)进行捆绑。
【解决方案2】:

基本上,您想要做的是创建一个库而不是常规捆绑包。如果你使用 webpack,你可以指定你希望你的输出是一个库,这将使它通过命名空间在全球范围内可用,类似于 jQuery。

// relevant part of your webpack config
// from webpack docs
{
  output: {
    // export itself to a global var
    libraryTarget: "var",
    // name of the global var: "Foo"
    library: "Foo"
  }
}

然后你就可以通过 Foo 调用你所有的函数了:

var app = new Foo.App()
var somethingElse = Foo.doSomethingElse()

文档和示例:https://webpack.github.io/docs/library-and-externals.html#examples

【讨论】:

    【解决方案3】:

    Webpack 或 Browserify 将您的脚本捆绑在特定上下文中,因此您只能联系该捆绑上下文中的类或函数,该上下文位于捆绑脚本的某个位置。因此,当您在 HTML 中添加另一个脚本时,它无法访问那些捆绑脚本的上下文。如果您想访问 App 类,据我所知,您可以将新脚本(现在只是制作一个新 App)导入/导出到 test.js 中

    【讨论】:

    • 你能更好地为“导入/导出新脚本”指定什么吗?
    • 基本上我会避免使用 在 html 中添加新脚本,并使 webpack 入口文件包含所有脚本,例如,在另一个文件 app.js 中实现 App 并将其导入test.js(入口文件)