Require.js和仅在DOM中创建<script>元素之间有什么区别?[关闭]


138

使用Require.JS和仅<script>在DOM中创建元素之间有什么区别?

我对Require.JS的理解是,它提供了加载依赖项的能力,但是这不能简单地通过创建一个<script>加载必要的外部JS文件的元素来完成吗?

例如,假设我有功能doStuff(),需要功能needMe()doStuff()在外部文件中do_stuff.js,而needMe()在外部文件中need_me.js

使用Require.JS方法:

define(['need_me'],function(){
    function doStuff(){
        //do some stuff
        needMe();
        //do some more stuff
    }
});

只需创建一个脚本元素即可:

function doStuff(){
    var scriptElement  = document.createElement('script');
    scriptElement.src = 'need_me.js';
    scriptElement.type = 'text/javascript';
    document.getElementsByTagName('head')[0].appendChild(scriptElement);

    //do some stuff
    needMe();
    //do some more stuff
}

这两个工作。但是,第二个版本不需要我加载所有Require.js库。我真的没有看到任何功能上的区别...


1
浏览器缓存如何,requirejs是否会干扰它?
穆罕默德·乌默尔

我要重新打开它,因为它要问两个非常相似的事物之间的区别。可以客观地回答它,而且我看不出有什么意见与之相关。
RamenChef

Answers:


43

这是有关为什么使用ajaxian.com的不错的文章:

RequireJS:异步JavaScript加载

  • 某种#include / import / require
  • 加载嵌套依赖项的能力
  • 开发人员易于使用,但随后得到了有助于部署的优化工具的支持

2
我已经读过这些内容,但是现在我想得更多,我意识到嵌套依赖项的想法不能仅通过编写<script>标签来实现。谢谢。
maxedison 2011年

37
“易于开发人员使用”离事实真相还远。对于您和将要从事该项目的其他任何人来说,它绝对具有陡峭的学习曲线。
Sahat Yalkabov

3
@TwilightPony我认为自己并不那么聪明,requirejs对我来说并不是一件很难的事。它使您不必担心依赖关系并加快了页面速度。在如何声明依赖关系方面,您的代码变得与服务器端编程更加内联,我个人觉得这令人耳目一新。语法是最小的,并且通过设计来封闭,然后设置生产路线图以轻松组合脚本。最重要的是,调试就像静态声明一样。不知道还有什么比这更容易的。另一种方法要困难得多,就像我做了另一种方法一样。
杰森·塞布林2014年

我正在挣扎。尤其是那些尝试将自身附加到全局对象的模块。(反应模块)...
geilt'3

1
该页面上的评论实际上使我觉得应该远离需求而不是追求需求。特别是靠近stevesouders.com/tests/require.php
Dave Kanter

52

与仅在DOM中创建元素相比,Require.JS有什么优势?

在您的示例中,您正在异步创建script标记,这意味着您的needMe()函数将在need_me.js文件完成加载之前被调用。这会导致未定义函数的未捕获异常。

相反,要使您的建议切实可行,您需要执行以下操作:

function doStuff(){
    var scriptElement  = document.createElement('script');
    scriptElement.src = 'need_me.js';
    scriptElement.type = 'text/javascript';

    scriptElement.addEventListener("load", 
        function() { 
            console.log("script loaded - now it's safe to use it!");

            // do some stuff
            needMe();
            //do some more stuff

        }, false);

    document.getElementsByTagName('head')[0].appendChild(scriptElement);

}

可以争论的是,最好还是不一定要使用诸如RequireJS之类的程序包管理器,或者使用如上所述的纯JavaScript策略。虽然您的Web应用程序可能加载得更快,但是在站点上调用功能和特性会变慢,因为在执行该操作之前,它涉及到等待资源加载。

如果将Web应用程序构建为单页应用程序,那么请考虑人们实际上不会经常重新加载页面。在这些情况下,预加载所有内容将有助于使实际使用该应用时的体验看起来更快。在这种情况下,您是对的,只需将脚本标签包含在页面的开头或正文中,就可以加载所有资源。

但是,如果按照更传统的模式构建网站或Web应用程序,即从一个页面到另一个页面过渡,导致资源重新加载,则延迟加载方法可能有助于加快这些过渡。


10

使用RequireJS有意义的其他一些非常明确的原因:

  1. 对于大型项目,管理自己的依赖项很快就瓦解了。
  2. 您可以根据需要拥有任意数量的小文件,而不必担心跟踪依赖关系或加载顺序。
  3. RequireJS使编写整个模块化应用程序成为可能,而无需触摸窗口对象。

摘自rmurphey在本要点中的评论

抽象的层次可能是学习和适应的噩梦,但是当它达到目的并做好时,这才有意义。


9
您仍然必须管理所有这些require并定义语句,配置文件,与尚未实现AMD规范的其他系统和库的冲突等。我尝试在node-webkit项目和Require.JS中使用Require.JS奋斗了我的每一步……与以某种方式简单地订购脚本相反……当然,您可以通过Require.JS进行延迟​​加载,这就是我尝试使其工作的原因。:)
jmort253 2014年

我完全同意@ jmort253,这在开始时是一场挣扎,但现在我非常喜欢它。这三点都是正确的!AMDify库应该没有那么困难...或使用填充程序。
传奇

0

这是一个更具体的例子。

我正在一个有60个文件的项目中。我们有2种不同的运行方式。

  1. 加载一个串联的版本,一个大文件。(生产)

  2. 加载全部60个文件(开发)

我们使用的是加载程序,因此网页中只有一个脚本

<script src="loader.js"></script>

默认为模式#1(加载一个大的串联文件)。为了在模式2下运行(单独的文件),我们设置一些标志。可能是任何东西。查询字符串中的键。在此示例中,我们只是这样做

<script>useDebugVersion = true;</script>
<script src="loader.js"></script>

loader.js看起来像这样

if (useDebugVersion) {
   injectScript("app.js");
   injectScript("somelib.js");
   injectScript("someotherlib.js");
   injectScript("anotherlib.js");
   ... repeat for 60 files ...
} else {
   injectScript("large-concatinated.js");
}

构建脚本只是一个看起来像这样的.sh文件

cat > large-concantinated.js app.js somelib.js someotherlib.js anotherlib.js

等等...

如果添加了新文件,由于我们正在进行开发,我们可能会使用模式#2,因此必须在injectScript("somenewfile.js")loader.js中添加一行

然后,在以后的生产中,我们还必须向我们的构建脚本中添加somenewfile.js。我们经常忘记这一步,然后得到错误消息。

通过切换到AMD,我们不必编辑2个文件。保持loader.js与构建脚本同步的问题消失了。使用r.jswebpack它可以只读取要构建的代码large-concantinated.js

它还可以处理依赖项,例如,我们像这样加载了2个文件lib1.js和lib2.js

injectScript("lib1.js");
injectScript("lib2.js");

lib2需要lib1。它的内部代码执行类似

lib1Api.installPlugin(...);

但是,由于注入的脚本是异步加载的,因此无法保证它们将以正确的顺序加载。这两个脚本不是AMD脚本,但是使用require.js可以告诉他们它们的依赖关系

require.config({
    paths: {
        lib1: './path/to/lib1',
        lib2: './path/to/lib2',
    },
    shim: {
        lib1: {
            "exports": 'lib1Api',
        },
        lib2: {
            "deps": ["lib1"],
        },
    }
});

我我们使用lib1的模块执行此操作

define(['lib1'], function(lib1Api) {
   lib1Api.doSomething(...);
});

现在require.js将为我们注入脚本,并且直到我们加载lib1为止,它才会注入lib2,因为我们告诉过lib2依赖于lib1。在加载了lib2和lib1之前,它也不会启动使用lib1的模块。

这使开发变得更加美观(无需构建步骤,无需担心加载顺序),并使生产变得更加美观(无需为添加的每个脚本更新构建脚本)。

作为额外的好处,我们可以使用webpack的babel插件在旧版浏览器的代码上运行babel,同样,我们也不必维护该构建脚本。

请注意,如果Chrome(我们选择的浏览器)开始支持import真正的浏览器,我们可能会改用它进行开发,但这并不会真正改变任何东西。我们仍然可以使用webpack来创建一个串联文件,并且可以使用它在所有浏览器的代码上运行babel。

所有这些都是通过不使用脚本标签和使用AMD获得的

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.