Javascript 什么';s Require.js与简单创建<;脚本>;DOM中的元素?

Javascript 什么';s Require.js与简单创建<;脚本>;DOM中的元素?,javascript,requirejs,dynamic-script-loading,js-amd,Javascript,Requirejs,Dynamic Script Loading,Js Amd,使用Require.JS和简单地在DOM中创建元素有什么区别 我对Require.JS的理解是,它提供了加载依赖项的能力,但这不能简单地通过创建一个加载必要的外部JS文件的元素来实现吗 例如,假设我有函数doStuff(),它需要函数needMe()doStuff()在外部文件do_stuff.js中,而needMe()在外部文件need_me.js中 以Require.JS的方式执行此操作: define(['need_me'],function(){ function doStuff

使用Require.JS和简单地在DOM中创建
元素有什么区别

我对Require.JS的理解是,它提供了加载依赖项的能力,但这不能简单地通过创建一个加载必要的外部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
}
只需创建一个脚本元素即可执行此操作:

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库。我真的看不到任何功能上的差异…

这是ajaxian.com上关于为什么要使用它的好文章:

  • 某种#包括/导入/要求
  • 加载嵌套依赖项的能力
  • 易于开发人员使用,但有助于部署的优化工具支持
与简单地在DOM中创建元素相比,Require.JS有哪些优势

在您的示例中,您正在异步创建脚本标记,这意味着您的
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应用程序,即从一个页面转换到另一个页面,导致资源被重新加载,那么延迟加载方法可能有助于加快这些转换。

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

  • 对于规模庞大的项目,管理自己的依赖关系很快就会崩溃
  • 您可以拥有任意数量的小文件,并且不必担心跟踪依赖项或加载顺序
  • RequireJS使编写一个完整的模块化应用程序成为可能,而无需触摸窗口对象 摘自


    抽象层可能是一个需要学习和适应的噩梦,但当它达到目的并且做得很好时,它才有意义。

    这里有一个更具体的例子

    我在一个有60个文件的项目中工作。我们有两种不同的运行模式

  • 加载连接的版本,1个大文件。(制作)

  • 加载所有60个文件(开发)

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

    <script src="loader.js"></script>
    
    构建脚本只是一个.sh文件,如下所示

    if (useDebugVersion) {
       injectScript("app.js");
       injectScript("somelib.js");
       injectScript("someotherlib.js");
       injectScript("anotherlib.js");
       ... repeat for 60 files ...
    } else {
       injectScript("large-concatinated.js");
    }
    
    cat > large-concantinated.js app.js somelib.js someotherlib.js anotherlib.js
    
    injectScript("lib1.js");
    injectScript("lib2.js");
    
    等等

    如果添加了一个新文件,我们可能会使用模式2,因为我们正在进行开发,我们必须向loader.js添加一个
    injectScript(“somenewfile.js”)

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

    通过切换到AMD,我们不必编辑2个文件。保持loader.js和构建脚本同步的问题消失了。使用
    r.js
    webpack
    只需读取代码即可构建
    large concentated.js

    它还可以处理依赖关系,例如,我们有两个文件lib1.js和lib2.js像这样加载

    if (useDebugVersion) {
       injectScript("app.js");
       injectScript("somelib.js");
       injectScript("someotherlib.js");
       injectScript("anotherlib.js");
       ... repeat for 60 files ...
    } else {
       injectScript("large-concatinated.js");
    }
    
    cat > large-concantinated.js app.js somelib.js someotherlib.js anotherlib.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获得的,我已经读过这些,但是现在我想得更多了,我意识到嵌套依赖的想法不能通过简单地编写标记来实现。谢谢,“开发人员的易用性”与事实相差甚远。它肯定有一个陡峭的学习曲线,你和任何其他人谁会来工作,在这个项目。@ TwitelPyPoi我认为自己并不光明,要求JJ并不真的是一件很难的事情,我得到。这样你就不用担心depen了