我该如何从Require.js导出全局变量?

9
我正在尝试使用Require.js制作外部库。感谢Require.js无法正确编译单个js文件Require.js(almond.js)时间不准确,我已经知道如何将所有内容“编译”为一个单一的优化/构建文件,并且该单一的文件可用。只有一个问题:我不知道如何为我的库设置变量。
假设我想让我的库创建window.Foo。我尝试使用包含以下内容的main.js文件:
window.Foo = require([], function() {
    window.Foo = {someValue: 1};
    return {someValue: 2};
});

并且包装器的结尾片段为:

    return require('main');
}));

如您所见,我试图通过在 require 调用内部显式设置 window.Foo 和通过结束片段的返回值来显式设置它来将 Foo 暴露给全局空间。但两者都不起作用;如果我在加载构建文件后添加一个 console.log(window.foo),它会告诉我 window.Foo 未定义。
如果我使用 window.setTimeout,最终会设置 window.Foo(为 {someValue: 1}),但我不能指望用户必须用定时器包装所有代码。请问有谁能解释一下如何在我的优化/构建文件加载后立即定义 window.Foo?

在库中使用RequireJS有什么特殊原因吗?最好支持RequireJS和CommonJS,并像大多数库一样回退到设置全局变量。 - jgillich
我们的主要代码库使用Require,而这个外部库使用了那个代码库的多个组件。为了使外部库能够利用我们主要代码库中的现有代码,它需要能够“说话”Require...尽管它一旦获取了那个代码就完全不需要Require,这就是为什么我试图让它看起来像一个完全独立的库对用户。 - machineghost
2个回答

13
如果你按照 James 的指示 here,就可以轻松地生成一个库,它被设计为 RequireJS 模块的捆绑包,可以同步加载。
我有一个 Github repository,展示了整个过程。重点如下:
  • The main module exports Foo to the global space:

    define('main', ["./modC"], function () {
    
    console.log("inside");
    window.Foo = {someValue: 1};
    
    return {someValue: 2};
    
    });
    

    It also returns a value that is exported by the start fragment as Bar (that's the line that says root.Bar = factory();). So it illustrates two ways to export to the global space.

  • The wrapper code used by r.js starts main with the synchronous form of require:

    require(`main`);
    

如果你加载它,你将看到以下输出:

outside
loaded modC
inside
out in the HTML!
value of window.Foo: Object {someValue: 1}
value of window.Bar: Object {someValue: 2}

啊,谢谢你提供的示例代码:我想我在应该使用define的地方使用了require。今天早上有时间后,我会尝试实现你的方法。 - machineghost
除了这个答案(对我非常有效,谢谢Louis),在一个Require问题的线程中,有人也指出了这个库(所以我为了完整性而提到它):https://github.com/gfranko/amdclean - machineghost

3

require()函数的调用是异步的,这意味着作为参数传递的函数的返回值不会require()本身返回。你的问题可以通过不同的方式解决,RequireJS的方式是定义你的代码为一个模块,并将其作为依赖项进行引用:

// mymodule.js
define([], function() {
    window.Foo = {someValue: 1};
    return {someValue: 2};
});

// main.js
require(['mymodule'], function (mymodule) {
    console.log(window.Foo); // {someValue: 1}
    console.log(mymodule); // {someValue: 2}
});

2
是的,但这样我就必须让我的用户使用Require。这意味着他们只需将库添加到页面并调用Foo.bar(),而不是让他们加载require然后执行require(['fooLibrary'], (Foo) { Foo.bar(); });。大多数人习惯于前者:他们下载jQuery,然后使用$...他们不需要require(['jQuery'], function($) { $.doSomething()});。理想情况下,我希望我们的库能够以同样的方式工作,并且因为我已经预先将所有内容编译成一个单独的文件,理论上不需要任何异步操作。 - machineghost
1
@machineghost jQuery 在其构建过程中正在做您想要的事情,但这并不是 RequireJS 本身支持的内容:https://github.com/jquery/jquery/blob/master/build/tasks/build.js - jgillich
哦,那很有趣。不过,我有点震惊的是,将一些Require.js代码转换为同步代码是不可能的;Require优化器可以将代码完成95%的工作... - machineghost
@machineghost 它将是同步的,你可以同步地require一些东西,如果你知道它们已经被加载。在RequireJS中,全局或本地模块的require函数会首先查找缓存,如果文件在那里,它将同步返回它,并且如果有回调函数,它也会将文件传递给回调函数,所有这些都是同步的。如果文件不在缓存中,并且提供了回调函数,则它将异步地检索文件并在回调中提供给require()。 - Alexander Mills

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接