如何通过grunt-contrib-uglify按顺序压缩JS文件?

36

我有一个目录如下:

/folder/b.js
/folder/jQuery.js
/folder/a.js
/folder/sub/c.js

我想将所有这些JS文件压缩成一个JS文件,按顺序

jQuery.js -> a.js -> b.js -> c.js

问:
1.如何通过grunt-contrib-uglify完成此操作?(实际上有很多文件,逐个指定源文件路径是不切实际的)

2.另外,如何在调试时获取未经压缩的文件,在发布时获取已经压缩的单个文件,并且无需更改HTML中的脚本标记(如何编写脚本标记)?


  1. folder/*.js,2. 通常所有未压缩的文件都会保留,然后创建一个包含所有JS合并和压缩的新文件,所有信息都在doc中。
- Jonathan de M.
2
@JonathandeM。我认为“有序”部分是一个比较棘手的问题。 - loganfsmyth
1
@loganfsmyth 你是对的。 - just4fun
1
哦,是的,Patrick Gunderson的答案是我能想到的最好的。 - Jonathan de M.
9个回答

25

好问题!

1)Uglify将重新排列目标文件中的函数,使函数定义在顶部,函数执行在底部,但它似乎会保留函数执行的顺序。

这意味着如果你确保在Gruntfile中首先提到jQuery,那么运行定义其全局函数的函数jQuery将被放在最前面。

我使用这个配置:

uglify: {
    options: {
        sourceMap: true
    },
    build: {
        files: {
            'public/all.min.js': ['public/js/vendor/jquery-1.10.2.min.js', 'public/js/*.js'],
        }
    }
}

2)我认为没有一种确定的方法来完成这个任务。它取决于你使用什么网络框架、模板框架以及你有哪些要求。我使用的是 Express + Jade,在我的主 Jade 布局中包含了以下内容:

if process.env.NODE_ENV === 'production'
  script(src='/all.min.js')
else
  script(src='/js/vendor/jquery-1.10.2.min.js')
  script(src='/js/someScript.js')
  script(src='/js/otherScript.js')

在我的 package.json 文件中,我有:

"scripts": {
  "postinstall": "grunt"
},

这意味着在部署(Heroku)时,当我运行npm install时,会运行grunt来压缩/合并文件,并且当使用NODE_ENV=production启动应用程序时,将使用已压缩的客户端JavaScript。本地调试时,我会使用原始的客户端JavaScript以便于调试。

这样做有两个缺点:

  • 我必须保持Gruntfile和layout.js中脚本文件列表的同步(我通过在Gruntfile中使用*.js来解决这个问题,但这可能并不适合所有人)。您可以将javascript列表放在Gruntfile中,并从此创建jade模板,但对于大多数项目而言,这似乎过于繁琐。
  • 如果你不信任你的Grunt配置,你基本上必须使用NODE_ENV=production在本地测试运行应用程序,以验证最小化是否按照你的意图工作。

2
最好通过为调试目的生成源映射来解决第二个问题。脚本标记始终指向您的缩小文件,生成的源映射将允许您无缝地调试缩小文件。请参阅http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/和https://www.npmjs.org/package/grunt-contrib-uglify了解有关源映射以及如何使用Grunt生成它们的信息。 - icyitscold

8

1
他已经在使用grunt-contrib-uglify(请参见问题标题);他想知道如何控制uglify连接文件的顺序。 - Ian Dunn
1
他的意思是,如果你首先连接它,它会按顺序变得丑陋。 - Neo

6
您可能不太喜欢这个方法,但最好的方式是将JS源文件定义为AMD模块,并使用Requirejs来管理它们加载的顺序。grunt-contrib-requirejs任务将递归您的依赖树,并按照必要的顺序将JS文件连接成一个大的JS文件。然后,您将使用uglify(实际上r.js内置了uglify)来压缩大文件。 https://github.com/danheberden/yeoman-generator-requirejs有一个很好的示例gruntfile和模板JS文件供您参考。 编辑 我最近开始使用CommonJS模块而不是AMD,因为它更接近ES6模块规范。通过Browserify运行commonjs模块,您可以获得相同的结果(1个大的已编译和连接的JS文件)。有gruntgulp的插件可以为您管理此任务。

编辑

我想补充一下,如果您的网站使用ES6编写,那么Rollup是最好的新连接包。除了捆绑文件外,它还将执行树摇,如果通过import语句包含,则删除您使用的库的部分。这样可以将代码库减少到仅需的内容,而不会有永远不会使用的代码膨胀。


1
是的,现在Browserify是一个更好的选择。 - light24bulbs

5

我认为你不能仅通过uglify任务完成此操作,但是你有多种选择可以实现你想要的结果。

可能的工作流程是首先使用grunt-contrib-concat将文件按顺序连接到一个单一文件中,然后将这个连接后的文件传递给uglify。你可以在Gruntfile中定义concat的顺序,或者使用其中一个插件:

第一个是https://github.com/yeoman/grunt-usemin,在这里你可以在HTML文件中指定顺序,在你的脚本块周围放置一些注释。谷歌开发的这个插件非常好用。

第二个是https://github.com/trek/grunt-neuter,在这里你可以使用require定义一些依赖项,但不需要使用require.js中的大量代码。它需要对你的JS代码进行更改,所以你可能不喜欢它。我会选择选项一。


3

这可能只是与您的问题略有关联,但我想要类似的东西。只是我的顺序在以下方面很重要:

我正在使用通配符(['vendor/**/*.js'])加载所有供应商文件(angular、jquery及其相关插件)。但是某些插件的名称使它们在angular和jquery之前加载。解决方法是手动先加载它们。

['vendor/angular.js', 'vendor/jquery.js', 'vendor/**/*.js]

幸运的是,Angular和jQuery在被重复加载时处理得足够好。 编辑:尽管重复加载这样大的库不是最佳实践,会导致压缩文件不必要的膨胀。(感谢@Kano指出这一点!)
另一个问题是客户端JS的顺序很重要,因为它需要最后加载主应用程序文件,在加载所有依赖项之后。解决方法是排除然后包含:
['app/**/*.js', '!app/app.js', 'app/app.js']

这样做可以避免在加载所有文件时同时加载 app.js 文件,只有在最后才会将其包含进来。


1
虽然 Angular 和 jQuery 能够很好地处理被重复加载的情况,但是重复加载这样大型的库并不是最佳实践,会导致你的压缩文件不必要地膨胀。 - kano

3

我遇到了同样的问题。一个快速的解决办法就是更改文件名 - 我使用了1.jquery.min.js2.bootstrap.min.js等。


2
看起来你的第二个问题仍未得到解答。但是让我逐一尝试。
首先,你可以像之前的拼接答案所解释的那样,将大量js文件合并并进行混淆。使用https://github.com/gruntjs/grunt-contrib-uglify似乎也是可行的,因为它似乎有通配符。你可能需要尝试使用“expand=true”选项和通配符。这就解决了你的第一个问题。
至于第二部分,假设你已经将它们合并并混淆成了big-ugly.js
现在在你的html中,你可以添加以下指令:
<!-- build:js:dist big-ugly.js -->
<script src="js1.js"></script>
<script src="js2.js"></script>
<!-- etc etc --> 
<script src="js100.js"></script>
<!-- /build -->

然后将其通过grunt html 预处理器传递,链接为https://www.npmjs.com/package/grunt-processhtml,作为grunt任务的一部分。

该预处理器将用整个块替换。

<script src="big-ugly.js"></script>

这意味着在grunt任务之前和之后,html文件的语义等效。也就是说,如果页面在原始形式下(用于调试)正常工作,则经过grunt转换后,转换后的页面必须正确工作,而不需要手动更改任何标记。

1
这是@1469的答案,但他没有清楚地解释为什么这样做有效。使用concat将所有js文件合并成一个,此模块按文件名顺序执行此操作,因此我根据顺序为文件名添加了前缀。我相信它甚至还有其他排序选项。
concat: {

        js: {
            options: {
                block: true,
                line: true,
                stripBanners: true
            },
            files: {
                'library/dist/js/scripts.js' : 'library/js/*.js',
            }
        }
    },

然后使用uglify创建压缩的丑陋版本:
uglify: {
      dist: {
        files: {
          'library/dist/js/scripts.min.js': [
            'library/js/scripts.js'
          ]

        },
        options: {

        }
      }
    },

0
如果你的问题是需要按顺序加载供应商(比如在任何jquery插件之前加载jquery),我通过将jquery放在自己的文件夹'!jquery'中来解决它,从而有效地将其置于堆栈顶部。 然后我只需像平常一样使用concat:
concat: {
  options: {
    separator: ';',
  },
  build: {
    files: [
      {
        src: ['js/vendor/**/*.js', 'js/main.min.js'],
        dest: 'js/global.min.js'
      }
    ]
  }
},

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