如何在HTML body中内联加载requireJS模块?

13

我正在将RequireJS集成到一个CMS中,因此我将以下内容放在我的页面模板底部:

<html>
<body>
  {Placeholder1}
  <script src="scripts/require.js" data-main="scripts/main"></script>
  {Placeholder2}
</body>
</html>

然后在每个页面上,我想创建一个利用RequireJS的函数。所以我尝试把这个放在页面底部:

<html>
<body>
    <h1>Home</h1>
    <div class="slider">Slider content</div>

    <script src="scripts/require.js" data-main="scripts/main"></script>

    <script type="text/javascript">
      require([
        'jquery',
        'utils',
        'slider'
      ], function ($, utils, slider) {
        slider.start();
      });
    </script>
</body>
</html>

但是我在jquery、utils和slider js文件上遇到了404错误。似乎它没有读取我设置的main.js配置:

require.config({
    paths: {
        jquery: 'libs/jquery-1.8.1.min',
        utils: 'libs/utils',
        slider: 'libs/jquery.slider.min'
    },
    shim: {
        slider: ['jquery']
    }
});

require([ 'utils' ], function (utils) {
    utils.init();
});

我尝试在页面头部加载RequireJS和main.js,但结果不一致。有时候jquery、utils和slider会按时加载,而有时候则不会。这就像是页面底部的内联"require"不知道页面上的主RequireJS或依赖规则一样,但我的断点在main.js中被打到了,所以我知道它被调用了。这是因为main.js被异步加载,而我在页面渲染时载入的内联"require"块并未意识到吗?我该如何解决这个问题?

我之前已经成功使用过RequireJS,但没有使用CMS。我总是使用"define",并且模块总是异步调用的,但从未像这样内联调用RequireJS函数。您有关于正确操作的任何想法吗?

2个回答

12

重要的是在请求任何模块之前设置配置选项。正如您所指出的那样,存在竞争条件,这意味着您的 main.js 中的配置选项没有及时加载。最简单的解决方法是在加载 require.js 脚本之前将配置选项嵌入其中。

<script>
var require = {
    baseUrl: <?= $someVar ?>,
    paths: {
        // etc
    }
}
</script>
<script src="scripts/require.js" data-main="scripts/main"></script>

页面向下滚动:

<script>
    require([
        'jquery',
        'utils',
        'slider'
      ], function ($, utils, slider) {
        slider.start();
      });
</script>

另请参阅如何在多个页面和部分视图中使用RequireJS?


1
这种设置的问题在于,当脚本/主文件包含多个模块(通常在使用r.js优化器时)时,资源可能会被加载两次。如果这是一个不使用require的define的文件,则尤其会出现问题。假设“scripts/main”文件包含LESS JS模块。此外,假设内联require调用也需要此模块。现在,如果内联调用在主脚本加载之前访问(通常情况下),则该模块将被获取两次。如果内容不是AMD模块,则可能会导致错误! - Roel van Duijnhoven

6
似乎正在异步加载main.js文件,而内联的require立即被调用。这就是为什么会出现不一致的结果。解决方法是不使用data-main属性,而是通过在require.js脚本标记下方的script标记调用main.js文件。
您仍然可以通过在其中执行以下操作来确定从加载的main.js文件中获取baseUrl:
//DETERMINE BASE URL FROM CURRENT SCRIPT PATH
var scripts = document.getElementsByTagName("script");
var src = scripts[scripts.length-1].src;
var baseUrl = src.substring(src.indexOf(document.location.pathname), src.lastIndexOf('/'));

//CONFIGURE LIBRARIES AND DEPENDENCIES VIA REQUIREJS
require.config({
    baseUrl: baseUrl,
....

或者你可以将内联代码放入main.js文件中。或者你可以将路径定义放入内联代码块中。 - dqhendricks
使用RequireJS的原因之一是避免依赖全局变量,因此请避免这样做。 - Simon Smith
@Simon,感谢你的建议。对于CMS系统来说,很难避免这种情况。在我看来,几乎所有的CMS都已经过时了。 - Basem
由于回答变得相当长,我添加了一个答案 ;) - Simon Smith
这让我抓狂了!谢谢。愚蠢的部分是:我一年前已经遇到过完全相同的问题,但没有立刻想起来。 - Christof

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