39

I keep seeing the recommendation for making JS files ready for production to be concat then uglify.

For example here, in on of Yeoman's grunt tasks.

By default the flow is: concat -> uglifyjs.

Considering UglifyJS can do both concatenation and minification, why would you ever need both at the same time?

Thanks.

4

2 回答 2

49

concat运行基本测试以查看执行和 thenuglify与 just之间是否存在性能差异uglify

包.json

{
  "name": "grunt-concat-vs-uglify",
  "version": "0.0.1",
  "description": "A basic test to see if we can ditch concat and use only uglify for JS files.",
  "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-contrib-concat": "^0.5.0",
    "grunt-contrib-uglify": "^0.6.0",
    "load-grunt-tasks": "^1.0.0",
    "time-grunt": "^1.0.0"
  }
}

Gruntfile.js

module.exports = function (grunt) {

    // Display the elapsed execution time of grunt tasks
    require('time-grunt')(grunt);
    // Load all grunt-* packages from package.json
    require('load-grunt-tasks')(grunt);

    grunt.initConfig({
        paths: {
            src: {
                js: 'src/**/*.js'
            },
            dest: {
                js: 'dist/main.js',
                jsMin: 'dist/main.min.js'
            }
        },
        concat: {
            js: {
                options: {
                    separator: ';'
                },
                src: '<%= paths.src.js %>',
                dest: '<%= paths.dest.js %>'
            }
        },
        uglify: {
            options: {
                compress: true,
                mangle: true,
                sourceMap: true
            },
            target: {
                src: '<%= paths.src.js %>',
                dest: '<%= paths.dest.jsMin %>'
            }
        }
    });

    grunt.registerTask('default', 'concat vs. uglify', function (concat) {
        // grunt default:true
        if (concat) {
            // Update the uglify dest to be the result of concat
            var dest = grunt.config('concat.js.dest');
            grunt.config('uglify.target.src', dest);

            grunt.task.run('concat');
        }

        // grunt default
        grunt.task.run('uglify');
    });
};

src里面,我放了一堆 JS 文件,包括未压缩的 jQuery 源码,复制了好几次,分散到子文件夹中。比普通网站/应用程序通常拥有的要多得多。

事实证明,在这两种情况下,连接和压缩所有这些文件所需的时间基本相同。
除非同时使用该sourceMap: true选项concat(见下文)。

在我的电脑上:

grunt default      : 6.2s (just uglify)
grunt default:true : 6s   (concat and uglify)

值得注意的main.min.js是,两种情况下的结果是相同的。
此外,uglify在合并文件时会自动注意使用正确的分隔符。

唯一重要的情况是添加sourceMap: trueconcat options.
这会在main.js.map旁边创建一个文件main.js,并导致:

grunt default      : 6.2s (just uglify)
grunt default:true : 13s  (concat and uglify)

但是如果生产站点只加载min版本,这个选项是没有用的。

我确实发现了使用before的一个主要缺点。 当其中一个 JS 文件发生错误时,将链接到连接的文件而不是原始文件。而整个工作何时完成,它将链接到原始文件。concatuglify
sourcemapmain.jsuglify

更新:
我们可以添加另外 2 个选项uglify,将源映射链接uglify到源concat映射,从而处理我上面提到的“缺点”。

    uglify: {
        options: {
            compress: true,
            mangle: true,
            sourceMap: true,
            sourceMapIncludeSources: true,
            sourceMapIn: '<%= paths.dest.js %>.map',
        },
        target: {
            src: '<%= paths.src.js %>',
            dest: '<%= paths.dest.jsMin %>'
        }
    }

但这似乎非常不必要。

结论

我认为可以安全地得出结论,concat如果我们正在使用 JS 文件,我们可以放弃uglify,并在需要时将其用于其他目的。

于 2014-12-12T21:38:10.823 回答
29

在您提到的示例中,我在下面引用,文件首先与以下内容连接,concat然后通过以下方式进行丑化/缩小uglify

{
  concat: {
    '.tmp/concat/js/app.js': [
      'app/js/app.js',
      'app/js/controllers/thing-controller.js',
      'app/js/models/thing-model.js',
      'app/js/views/thing-view.js'
    ]
  },
  uglifyjs: {
    'dist/js/app.js': ['.tmp/concat/js/app.js']
  }
}

同样可以通过以下方式实现:

{
  uglifyjs: {
    'dist/js/app.js': [
      'app/js/app.js',
      'app/js/controllers/thing-controller.js',
      'app/js/models/thing-model.js',
      'app/js/views/thing-view.js'
    ]
  }
}

通常,该任务clean将在写入临时文件夹(在本例中)的任务之后运行,concat并删除该文件夹中的任何内容。有些人还喜欢clean在任务之前运行,例如compass删除随机命名的图像精灵(每次任务运行时新生成的)之类的东西。即使对于最偏执的人,这也可以保持车轮转动。

这完全取决于偏好和工作流程,就像何时运行一样jshint。有些人喜欢在编译之前运行它,有些人喜欢在编译后的文件上运行它。

具有大量JavaScript文件的复杂项目 - 或具有越来越多的同行和贡献者,可能会选择在外部连接文件uglify,以使内容更具可读性和可维护性。Yeoman我想这就是选择转型流程背后的原因。

uglify根据项目的配置,可能会非常慢,因此将它与 first 连接可能会有一些小的收益concat- 但这必须得到确认。

concat还支持分隔符,就文件而言,它uglify不支持。README.md

concat: {
  options: {
    separator: ';',
  }
}
于 2014-03-20T22:32:35.433 回答