Grunt:当函数不能被分割到不同的任务时,如何依次运行一个函数,然后是一个任务,然后是另一个函数?

我正在使用Grunt来生成一个构build。 我是Grunt,javascript和nodejs的新手,所以任何新的观点都会受到欢迎。

我的Gruntfile中的一些任务依赖于插件(比如用于javascript缩小的“uglify”),而另外一些工作stream程最适合于自己编写javascript函数。

(1)执行一个javascript函数(2)在该函数之后立即执行一个grunt任务(3)执行另一个javascript函数。 它需要按照这个顺序发生。 但是,由于Grunt只是一个任务调度程序,所以在(1)和(3)完成之后,它将作为最后一步运行(1),排队(2),运行(3),然后运行(2)。

以下是一个非常简单的假设自定义任务的用例,以更好地解释情况。

grunt.task.registerTask('minifyJS', function() { jsFilepathMapping = configureUglifiy(); /** note - configureUglify is needed because the minification filepaths are generated on the fly, I do not know them before the script runs and more than that, there are so many that it would be really bulky to create init targets for each single minification file that needs to be generated.*/ grunt.task.run('uglify'); updateJsScriptTags(jsFilepathMapping); // update the <script> tags in my HTML }); 

问题是我需要按照上面所示的顺序运行这些东西。 但是,由于grunt是一个任务调度程序,所以在执行此任务时会发生以下情况

  1. configureUglify()函数将运行
  2. 'uglify'排队 – 不运行。
  3. updateJsScriptTags()函数将运行
  4. 现在默认任务已经完成 – 现在只有排队的“uglify”才会运行

现在,我明白为什么会发生这种情况。 Grunt是一个任务调度程序 – 任务正在排队。 这是有道理的,而不是抱怨。 相反,我在问什么是解决这个问题的方法? 这样,我可以实现这个顺序,当使用这个function和任务的组合? 我想这一定是超级简单而通用的,我只是不知道它是什么!

我意识到的一个select是将这些function中的每一个转换成任务本身。 然而,这个问题是,configureUglifiy()返回一个复杂的数据结构,然后updateJsScriptTags()会消耗。 在Grunt任务之间共享数据似乎并不是一个好方法,除了使用仅在任务本身中可用的“选项”属性之外。 我想我可以做到这一点,然而,我担心的是,这使得事情变得不可见,而且…有点危险,因为你现在有这个任务,那就是依赖于另一个任务期间修改的数据结构,而不是立即直到你开始挖掘function。

让我举个例子。 哪个更可读?

 /** documentation which defines what the argment filepathMapping is Furthermore, since the function takes an argument, the context is immediately clearer just looking at the function declaration. */ function updateJsScriptTags(filepathMapping) { // do stuff ... for ( key in filepathMapping ) { // oh cool! i know what this arg does, my documentation nicely explains it, and its structure too // lots of stuff } } ... // and elsewhere in the script, where it's being invoked: var aMapping = someFunc(); updateJsScriptTags(aMapping); 

 grunt.task.registerTask('updateJsScriptTags', 'update js tags', function() { // do stuff. ... // many lines later: grunt.options('filepathMapping') // Oh, what is this attribute? Let me go look around the rest of the script to find out where it comes from } ... // and ultimately, where it's being invoked. grunt.task.run('someTask'); // global options param gets modified somewhere in here, but you'd never know it looking at this line of code grunt.task.run('updateJsScriptTags'); // this task will depend on that modification 

我发现这使得事情变得更加难以理解,而不是一个简单的函数,需要消耗参数,并且可以强加一个特定的结构。 在一些任务中,我正在修改一些全局参数中的一些属性,而在另一个任务中被稍后使用,这些东西也变得更容易了。 更麻烦的是,属性名称被共享,硬编码..我知道这是一个非常简单的用例,但开始想象一个更复杂的函数集依靠多个参数可能是复杂的数据types,是我关心的地方

所以作为一个快速的总结:有没有办法实现function/插件任务/function/插件任务顺序sorting,而不诉诸转换function自定义任务本身?

所以作为一个快速的总结:有没有办法实现function/插件任务/function/插件任务顺序sorting,而不诉诸转换function自定义任务本身?

简单的回答:不,维持执行顺序,您需要使用grunt Tasks而不是普通的JavaScript函数来定义顺序。

顺序sorting按照taskList Array中定义的每个别名任务的顺序执行 。 例如,当使用grunt.registerTask

 grunt.registerTask('foo', [ 'a', 'b', 'c' ]); 

给定上面的伪例子并运行foo Task将首先运行任务,然后是任务b ,依此类推(即任务b不会运行,直到任务a完成;任务c将不运行,直到任务b完成)

但是,这并不意味着build议不要将普通的JavaScript函数与grunt 自定义任务结合使用。


解决scheme长答案:

似乎没有一种很好的方式来分享Grunt任务之间的数据…

您可以使用Object来存储数据。 第一个例子中的伪代码意味着你想要configureUglifiy函数来:

  1. dynamicconfigurationUglify任务
  2. 返回由configureUglifiy本身生成的数据(一个Object )。
  3. 然后将返回的数据作为parameter passing给updateJsScriptTags函数。

所以,而不是从configureUglifiy函数返回一个Object 。 您可以将其存储在另一个Object ,然后在updateJsScriptTags函数中访问该updateJsScriptTags

在下面的要点中注意shared对象,其中有一个名为jsFilepathMapping的属性/键。 我们将使用这个对象来存储可以在另一个任务中访问的dynamic生成的数据。

Gruntfile.js (伪代码)

 module.exports = function (grunt) { 'use strict'; var shared = { jsFilepathMapping: {} // <-- Intentionally empty. The object will // be defined via `configureUglify` function, // and consumed by `updateJsScriptTags` Task. }; grunt.initConfig({ uglify: { // <-- Intentionally empty, will be dynamically generated. } }); //--------------------------------------------------------------------------- // Functions //--------------------------------------------------------------------------- /** * Helper function to dynamically configure the uglify task. */ function configureUglify() { // <-- Do stuff here to determine configuration of uglify task. grunt.config('uglify', config); // Store object (for referencing later) instead of returning. shared.jsFilepathMapping = config; }; /** * Helper function to update the js script tags in html. */ function updateJsScriptTags(filepathMapping) { // `filepathMapping` object now available in this function. for ( var key in filepathMapping ) { console.log(filepathMapping[key]) } } //--------------------------------------------------------------------------- // Tasks //--------------------------------------------------------------------------- grunt.task.registerTask('updateJsScriptTags', 'Updates tags', function () { // Invoke the function passing in the values which were previously // ascertained and set via the `configureUglify` function. updateJsScriptTags(shared.jsFilepathMapping) }); grunt.task.registerTask('minifyJS', function() { configureUglify(); grunt.task.run(['uglify']); }); grunt.loadNpmTasks('grunt-contrib-uglify'); // Define sequential ordering of each Task in the taskList Array. grunt.registerTask('default', ['minifyJS', 'updateJsScriptTags']); }; 

注意读取// <-- Do stuff here to determine configuration of uglify task.的评论// <-- Do stuff here to determine configuration of uglify task. 将是逻辑类似于Gruntfile.js的要点,我提供给你的其他问题在这里 。


概要

  1. 顺序sorting应该通过添加任务到TaskList数组来定义(按照上面的简短回答部分)
  2. 必要时可以通过grunt自定义任务调用函数。 但是记住一个自定义任务是一个函数,所以只有在真正需要的时候,才能将自己的function分离出来。 你会发现你的Gruntfile.js读得更好。
  3. 当一个任务/函数dynamic地获取数据以在其他任务/函数之间共享时,考虑将其存储在Object而不是使用return关键字从函数返回值。

  4. 在可能的情况下,将逻辑分解成单独定义的任务,并尽量避免在一个任务中做太多。 例如,在你的问题minifyJS提供的第一个自定义任务中,你试图在一个任务中做两件事情。 即你正在configuration和运行你的uglify任务(这构成一个任务),并且还更新JS脚本标签(这构成了另一个任务)。 理想情况下,这应该是两个不同的任务。

我提供的伪Gruntfile.js (上面)它目前不采用我的build议在第2和第4点给出。一些重构Gruntfile.js会更像这样(注意函数不再存在,而是他们的逻辑已经合并与调用它们的自定义任务):

Gruntfile.js (伪代码重构)

 module.exports = function (grunt) { 'use strict'; var shared = { jsFilepathMapping: {} // <-- Intentionally empty. The object will // be defined via `minifyJS` Task, // and consumed by `updateJsScriptTags` Task. }; grunt.initConfig({ uglify: { // <-- Intentionally empty, will be dynamically generated. } }); //--------------------------------------------------------------------------- // Tasks //--------------------------------------------------------------------------- grunt.task.registerTask('updateJsScriptTags', 'Updates tags', function () { // `filepathMapping` object now available in this task. for ( var key in shared.jsFilepathMapping ) { console.log(shared.jsFilepathMapping[key]) } }); grunt.task.registerTask('minifyJS', function() { // <-- Do stuff here to determine configuration of uglify task. // Save object (for referencing later) instead of returning. shared.jsFilepathMapping = config; grunt.config('uglify', config); grunt.task.run(['uglify']); }); grunt.loadNpmTasks('grunt-contrib-uglify'); // Define sequential ordering of each Task in the taskList Array. grunt.registerTask('default', ['minifyJS', 'updateJsScriptTags']); };