如何将JavaScript应用程序拆分为多个文件?

50

我创建了一个JavaScript应用程序,所有代码都在一个文件中。该应用程序已经变得相当庞大,我认为现在是将其拆分成多个文件的时候了,但我很难弄清楚如何做到这一点。我认为问题出在我决定构建应用程序的方式上,它使用以下模板:

var myApp = function(){

    //there are several global variables that all of the modules use;
    var global1, global2, module1, module2;

    global1 = {
        prop1:1
    };

    //There are also several functions that are shared between modules
    function globalFunction(){

    }

    var ModuleOne = function(){

        function doSomething(){
            //using the global function
            globalFunction();
        }

        return{
            doSomething:doSomething
        }
    };

    var ModuleTwo = function(){

        function doSomething(){
            //module 2 also needs the global function
            globalFunction();

            //Use the functionality in module 1
            //I can refactor this part to be loosely coupled/event driven
            module1.doSomething();
        }
    };

    module1 = new ModuleOne();
    module2 = new ModuleTwo();
};

即使所有模块都是松散耦合的和事件驱动的,但我仍然不知道如何将其拆分成多个文件,因为每个模块都依赖于共享的函数/变量。有没有人有建议?


说实话,像module8这样的工具可以解决你的问题。 - Raynos
1
看一下这篇文章中的设计模式:http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth - 你可以将模块定义分散到多个文件中,以便共享公共属性,但也可以创建仅限于特定文件的私有变量或方法。 - nnnnnn
@nnnnnn,感谢您的文章。非常完美。您应该提交一个带有链接的答案,这样我就可以接受它了... - Mark Brown
如果模块使用共享的函数/变量,那么它们就不是松耦合的。 - Michael Mior
5个回答

47

请参考这篇文章中的设计模式:http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth - 你可以按照这种方式将模块定义拆分到多个文件中,其中共有的属性可以被共享,而你也可以创建变量或方法,使它们只在特定的文件中为私有。

基本思想是各个JS文件通过以下代码添加到同一个模块:

var MODULE = (function (my) {
     var privateToThisFile = "something";

    // add capabilities...

     my.publicProperty = "something";

    return my;
}(MODULE || {}));

如果MODULE已经在另一个JS文件中定义,你需要在每个JS文件的哪里添加它,否则你需要创建它。你可以设置它(大部分情况下)不会管各个文件的包含顺序。

本文详细介绍了几种变化,当然,你可能会想出自己的调整...


1
在整整一天的头疼之后,我很高兴找到了这个答案。 - Raja Khoury

4

不想让事情更加混乱,但出于C++背景的考虑,我试图构建类似于C++命名空间的东西,如下所述。它能够工作,但我想知道这种模式是否符合OP的要求?

--------------------------------文件 main.js:----------------

var namespacename = function(){}

namespacename.mainvalue = 5;

namespacename.maintest = function() {
    var cm = new namespacename.game();
    cm.callme();
}

--------------------------------文件 game.js:----------------

namespacename.gamevalue = 15;

namespacename.game = function(){
    this.callme = function(){
        console.log( "callme" );
    }
}

namespacename.gametest = function() {
    console.log( "gametest:gamevalue:" + this.gamevalue );
    console.log( "gametest:mainvalue:" + this.mainvalue );
}

--------------------------------文件 index.html:--------------


这是一个HTML文件,它用于在Web浏览器中显示网页。该文件包含网页的结构和内容,并可以通过超链接与其他网页链接。如果您需要更改网页的外观或添加新的元素,您可以编辑此文件并保存更改。
<html>
    <head>
        <title>testbed</title>
    </head>

    <body onload="init();">
    </body>

    <script type="text/javascript" src="main.js"></script>
    <script type="text/javascript" src="game.js"></script>

    <script type="text/javascript">

        init = function() 
        {
            namespacename.maintest();
            namespacename.gametest();

            console.log( "html main:" + namespacename.mainvalue );
            console.log( "html game:" + namespacename.gamevalue );
        }

   </script>
</html>

6
为什么要将 namespacename 声明为一个从未调用的空函数?你可以将它声明为一个空对象,使用 _var namespacename = {}_,并且你的代码不需要做任何改变。 - nnnnnn
如果你来自C++,那么你一定知道最终会有几十个、有时甚至是数百或数千个文件。你不想从服务器请求这么多JS脚本。相反,最好将所有文件打包成一个脚本(例如使用Webpack)。 - user6106573

3

试试require.js。 http://requirejs.org/

示例:

require(["dir/file"], function() {
    // Called when file.js loads
});

是的,我已经研究过了,但我仍然不知道如何解决共享函数的问题... - Mark Brown
但是这如何帮助拆分当前相互依赖的大型代码文件呢? - nnnnnn
1
你可以为每个类创建一个文件(因此是面向对象编程标签),运行脚本只需提取这些类以供使用。如果你给我看代码,我可以帮你将其分发到不同的文件中。 - Fabián Heredia Montiel
2
@fabianhjr 这段代码的结构与问题中包含的示例完全相同。您会如何拆分该示例?谢谢。 - Mark Brown
2
@fabianhjr 不需要猜测。问题中已经包含了结构以及我遇到的具体问题。我的问题是寻求如何解决这些问题的建议。如果你除了称其为“设计问题”之外没有其他建议,为什么要回复呢? - Mark Brown
显示剩余4条评论

2

您可以将共享函数和共享模块放在myApp对象上,这样它们就不会污染全局命名空间,但可以在任何地方访问而不必在同一个闭包内。

myApp.moduleOne = function() {...}
myApp.moduleTwo = function() {...}
myApp.globalFunction = function() {...}

然后,您可以在任何文件中定义它们,并在任何文件中使用它们。

您还可以将文件分成多个文件,但需要按特定顺序包含它们以保留闭包。如果您是出于实际编辑原因而将文件拆分,但重新组合和最小化它们用于实际部署,则无论您如何编写代码或如何部署,都不会增加任何成本,但会为您提供许多更小的便于编辑的文件。


myApp不是真正的名称,我只是用它作为示例。根据您的建议,我需要将每个对globalFunction的调用更改为myApp.globalFunction(),对吗? - Mark Brown
@MarkBrown - 正确。这在编写代码时可能不太理想,但可以让您更灵活地将代码分段到完全独立的文件中,这些文件可以更加独立。我还在我的答案中添加了另一个选项。 - jfriend00

-2

我最喜欢的解决方案是使用服务器端脚本将其他文件包含在我的“主”文件中。例如,使用Perl的模板工具包:

var myApp = function(){
    [% INCLUDE lib/module1.js %]
    [% INCLUDE lib/module2.js %]
    [% INCLUDE lib/module3.js %]
}

或者 PHP:

var myApp = function(){
  <?php
    include 'lib/module1.js';
    include 'lib/module2.js';
  ?>
}

3
这是动态生成的 JavaScript。为了静态编译而不是动态包含,应该避免使用它。 - Raynos
无论是TT还是PHP都可以从控制台运行,因此如果您想要进行静态编译,可以轻松编写构建脚本。哦,另外TT可以配置缓存文件,因此只需要进行一次编译即可。 - slebetman

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