如何将JavaScript文件合并成一个文件?

40
我想为我的网站创建一个编译后的JavaScript文件。在开发过程中,我更喜欢将JavaScript保留在单独的文件中,并将这些文件连接到一个文件并运行压缩程序作为我的自动化脚本的一部分。
我的问题是,如果我使用旧的DOS复制命令,它会插入压缩程序投诉的EOF标记:
copy /A *.js compiled.js /Y
其他人都在做什么?

1
Phil,从你对我的回答的评论中,我现在可以看出你不理解EOF标记(好吧,我用复制得到了一个,在结果文件的末尾),但确实是BOM,因为你以某种方式编写了Unicode文件。将它们转换为Ascii,你应该摆脱它们。 - PhiLho
事实上,在我的工作日中,我跨越Windows、Linux和Mac,并使用许多不同的编程语言。Unicode对我来说是常态。 - Phil Hannent
11个回答

35

我建议使用Apache Ant和YUI Compressor。

http://ant.apache.org/

http://yui.github.com/yuicompressor/

在Ant构建xml中添加以下内容,它将创建两个文件application.js和application-min.js。

<target name="concatenate" description="Concatenate all js files">
    <concat destfile="build/application.js">
        <fileset dir="src/js" includes="*.js" />
    </concat>
</target>

<target name="compress" depends="concatenate" description="Compress application.js to application-min.js">
    <apply executable="java" parallel="false">
        <filelist dir="build" files="application.js" />
        <arg line="-jar" />
        <arg path="path/to/yuicompressor-2.4.2.jar" />
        <srcfile />
        <arg line="-o" />
        <mapper type="glob" from="*.js" to="build/*-min.js" />
        <targetfile />
    </apply>
</target>

2
这对我来说出现了一个意外的元素“{}target”{antlib:org.apache.tools.ant}target错误。有什么想法吗? - Jasdeep Khalsa
4
几年前,我停止使用Ant,现在我用Node来完成这些事情。看看这个链接:http://mechanics.flite.com/blog/2012/06/19/why-we-use-node-dot-js-and-grunt-to-build-javascript/ - David Brockman

11

若要无 EOF 复制,请使用二进制模式:

copy /B *.js compiled.js /Y

如果生成的文件仍然存在EOF(文件结束标志),可能是来自原始文件之一,可以通过以下方式解决:
copy /A *.js compiled.js /B /Y

/A 会从原始文件中删除任何尾随的 EOF,而/B 则防止将 EOF 添加到结果文件。如果 EOF 不在结尾,则源文件将被截断。开关的顺序很重要。

copy /A *.js /B compiled.js /Y  

源文件中的EOF不会被删除,但生成的EOF也不会被添加。

你可以自己试试,这就是我得到的。DOS命令很奇怪。


奇怪的是,/B 选项对我的文件没有任何影响。 - Phil Hannent
我尝试了你的建议,但我仍然得到: 作为文件连接处的标记... 感谢您查看此内容。 - Phil Hannent
2
看起来我明白了,是 UTF8 前缀字节。它应该位于文件的开头,否则它会被视为数据,编译器会对其进行投诉。这不是 EOF 问题。而且你不能使用复制。 - eugensk

6
在asp.net AJAX中,您可以使用“CompositeScript”标记。这将把所有脚本组合成一个大的js文件,通过减少http 304和可能的http 401来节省带宽。
示例:
 <asp:ScriptManager ID="ScriptManager1" runat="server">
        <CompositeScript>
            <Scripts>
                <asp:ScriptReference Path="~/Scripts/Script1.js" />
                <asp:ScriptReference Path="~/Scripts/Script2.js" />
                <asp:ScriptReference Path="~/Scripts/Script3.js" />
            </Scripts>
        </CompositeScript>
    </asp:ScriptManager>

更多信息请参见此处: http://msdn.microsoft.com/zh-cn/library/cc488552.aspx

6
这是一个很古老的问题,但我想提一下,也有用javascript来连接javascript的方法!当然要使用nodejs...例如,已经发布了像这样的npm模块工具,还有gruntgulp插件。

我还想提到一种非常、非常有趣的技术,它被用于像jQuery和Modernizr这样的大型项目中。这两个项目都是完全使用requirejs模块开发的,然后它们使用requirejs优化器作为一个非常智能的连接器。有趣的是,正如你所看到的,jQuery和Modernizr都不需要依赖requirejs才能工作,这是因为他们消除了requirejs语法仪式,以便在他们的代码中摆脱requirejs。所以他们最终得到了一个独立的库,该库是使用requirejs模块开发的!由于这一点,他们能够执行自定义构建,以及其他优点。这里有一篇博客文章,更详细地解释了所有这些。


5
在您的计算机上安装压缩工具uglifyjs:
sudo npm -g install uglify-js

接下来可以使用以下命令来合并和压缩所有的js文件。
cat myAppDir/*.js | uglifyjs > build/application.js

4
我们创建了一个机制,包括以下部分:
  • 缩小(用于js和css)
  • 打包聚合
  • 缓存(http状态304)
  • 在开发模式下发送原始文件
这可能对您的需求来说太多了,但是为了回答您的问题,其他人是如何做的,以下是它是如何工作的:
  1. 请求到达,例如, /css.aspx?package=core
  2. 我们在xml配置文件中查找包名(例如声明包“core”包含文件/js/mootools.js和/js/swfobject.js)
  3. 我们检查是否启用了缩小。例如,在开发环境中,我们不希望提供缩小后的js内容,而是编写原始文件。对于js,这是通过脚本包含的document.write完成的,对于css,我们编写导入规则。
  4. 如果需要缩小(在生产环境中),我们会检查来自请求的if-modified-since标头。如果此客户端已经具有缩小后的内容,则发送http标头304。如果客户端确实需要内容,则我们检查是否在缓存中具有缩小后的内容并提供该内容。否则,我们进行缩小并发送结果。
所有这些都被分解成单独的服务。缓存服务被注入到jsminificationwriter服务中。这利用了原始缩小服务,该服务仅负责缩小规则。
在这种方法中的好处是:
  • 它迫使我们的开发团队考虑js/css“包”,因此正确地拆分功能并将其分布在需要它们的页面上。
  • 在开发过程中,您可以完全调试,获取适当的文件和行号。
  • 您可以连接任何其他缩小服务实现,例如YUI等。 JsMin只是我们的第一次尝试。
  • 这是一种适用于不同内容类型的通用方法。
希望这有所帮助。如果您愿意,我可以发布一些代码片段来更详细地说明它。

3

1
Packer使用客户端压缩,这实际上会减慢用户的速度,因此不建议使用。 - timmow
或者获取Shrinker,它是Mac的强大压缩工具:http://itunes.apple.com/us/app/shrinker/id439567298?mt=12&ls=1# - JqueryToAddNumbers

3
您也可以这样做:
type *.js > compiled.js

我的设置肯定有问题,因为type方法返回的标记和copy方法完全一样: - Phil Hannent
3
BOM是什么?你的文件是ANSI还是UTF格式? - GalacticCowboy

1

我知道这是一个非常老的问题,但为了完整起见,我将提及使用Browserify的选项。它允许您使用NPM的require函数构建不同模块的项目以解决依赖关系,然后解决这些依赖关系并将整个项目连接成单个文件。

例如,假设您的项目名为FooBar,您想要输出一个名为foobar.js的文件。您需要创建一个main.js文件作为项目的入口点,需要引用所有应包含的模块。

main.js

require("./doFoo");
require("./doBar");

然后运行:
browserify main.js -o foobar.js

或者,为了在源文件更改时自动执行此操作,您还可以使用Watchify

watchify main.js -o foobar.js

Browserify还将解决模块之间的依赖关系。因此,例如,如果doBar.js依赖于doQux.js...

doBar.js

require("./doQux");
const doBar = ()=>{
    //Do some bar stuff.
}
exports.doBar = doBar;

然后Browserify会确保将doQux.js包含在foobar.js中,以便您不会有任何损坏的依赖关系。

0

这是我使用glob解决相同任务的方法:

const fs = require('fs');
const glob = require('glob');

const outputFile = './output/output.js';
const filesToConcat = './path/*.js';

// delete output file if exists (otherwise it will continue appending)
fs.unlink(outputFile, function(err) {
  console.log('output.js is removed');
});

// concat all js files into cam-benefits-ui.js
glob(filesToConcat, function(err, files) {
  files.forEach(file => {
    fs.readFile(file, 'utf8', function(err, data){
      fs.appendFileSync(outputFile, data);
    });
  });
});

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