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

时间:2023-02-13 03:35:12

I am using Grunt to generate a build. I am new to Grunt, javascript, and nodejs, so any new perspectives would be welcomed.

我正在使用Grunt生成一个构建。我对Grunt、javascript和nodejs都不熟悉,因此欢迎任何新的透视图。

Some of the tasks in my Gruntfile rely on plugins (such as 'uglify' for javascript minification), while there are some other workflows that are best suited to just writing javascript functions themselves.

在我的Gruntfile中,有些任务依赖于插件(比如javascript的“uglify”),而还有一些其他的工作流最适合编写javascript函数本身。

I keep running in to situations where I need to do the following: (1) execute a javascript function (2) run a grunt task immediately after that function (3) execute another javascript function. It needs to happen in that order. However, since Grunt is just a task scheduler, it ends up running (1), queuing (2), running (3), and then running (2) as the final step, after (1) and (3) have completed.

我一直在执行以下操作:(1)执行一个javascript函数(2)在该函数(3)执行另一个javascript函数之后立即运行一个grunt任务。它需要按照这个顺序发生。但是,由于Grunt只是一个任务调度器,它结束运行(1)、排队(2)、运行(3),然后运行(2)作为最后的步骤,在(1)和(3)完成之后。

The following is a very simple use case for a hypothetical custom task, to explain the situation better.

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

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

});

The problem is that I need to run these things in the order shown above. However, since grunt is a task scheduler, the following happens instead, when this task is run

问题是我需要按照上面所示的顺序运行这些东西。然而,由于grunt是一个任务调度程序,因此在运行此任务时,将执行以下操作

  1. configureUglify() function will run.
  2. configureUglify()函数将运行。
  3. 'uglify' will get queued - NOT run.
  4. “uglify”将被排队,而不是运行。
  5. updateJsScriptTags() function will RUN.
  6. updateJsScriptTags()函数将运行。
  7. Now that the default task has completed - only now will 'uglify', which was queued up, will run.
  8. 现在,默认任务已经完成——只有现在才会运行排队的“uglify”。

Now, I understand WHY this happens. Grunt is a task scheduler - the tasks are getting queued up. This makes sense and not complaining about it. Rather, I'm asking what is the way to resolve this? So that I can achieve this ordering, when using this combination of functions and tasks? I imagine this must be super simple and common, I just don't know what it is!

现在,我明白为什么会这样。Grunt是一个任务调度程序——任务正在排队。这是有道理的,而不是抱怨。相反,我想问的是如何解决这个问题?这样我在使用函数和任务的组合时就可以实现排序了?我想这一定非常简单和常见,我只是不知道它是什么!

One option I realize is to convert each of those functions in to tasks themselves. However, the problem with this, is that the configureUglifiy() returns a complex data structure, which updateJsScriptTags() then consumes. There does not seem to be a great way to share data between Grunt tasks, other than using the 'options' attribute, which is only available within tasks themselves. I guess I could do this, however, my concern here is that this makes things unreadible and... kind of dangerous, since you now have this task, that is relying on a data structure being modified during another task, and it's not immediately obvious until you start digging in the function.

我意识到的一个选择是将这些函数转换为任务本身。但是,这种方法的问题是,configureUglifiy()返回一个复杂的数据结构,然后updateJsScriptTags()将使用该结构。除了使用“选项”属性(只能在任务本身中使用)之外,似乎没有更好的方法在繁重的任务之间共享数据。我想我可以这么做,但是,我担心这会使事情变得不容易,而且……这很危险,因为你现在有了这个任务,它依赖于在另一个任务中修改的数据结构,直到你开始挖掘这个函数,它才会立即显现出来。

Let me give an example. Which is more readable to you?

我举个例子。哪个对你更有可读性?

/** 
    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);

vs.

vs。

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

I find this makes things more unreadable, as opposed to a simple function that takes in arguments to be consumed, and can impose a particular structure. It also seems easier for things to get buggy the other way where I'm modifying a bunch of attributes in some global param within one task, which gets consumed later in another. Even more bothersome, is the attribute names being shared, are hardcoded.. I know that this is a very simple use case, but start to imagine a more complex set of functions that rely on multiple arguments which could be complex data types, and that is where my concern goes.

我发现这使事情变得更加不可读,而不是一个简单的函数,它接受要使用的参数,并可以强加一个特定的结构。如果我在一个任务的全局参数中修改了一堆属性,然后在另一个任务中使用,那么事情就会变得更容易出错。更麻烦的是,共享的属性名是硬编码的。我知道这是一个非常简单的用例,但是开始想象一组更复杂的函数,它们依赖于多个参数,这些参数可能是复杂的数据类型,这就是我要关注的地方。

So as a quick summary: are there alternatives to achieving a sequential ordering of function/plugin-task/function/plugin-task, without resorting to converting the functions to custom tasks themselves?

简单总结一下:除了实现函数/插件/任务/函数/插件-任务的顺序排序之外,是否还有其他替代方法,而无需将函数转换为自定义任务本身?

1 个解决方案

#1


2  

So as a quick summary: are there alternatives to achieving a sequential ordering of function/plugin-task/function/plugin-task, without resorting to converting the functions to custom tasks themselves?

简单总结一下:除了实现函数/插件/任务/函数/插件-任务的顺序排序之外,是否还有其他替代方法,而无需将函数转换为自定义任务本身?

Short answer: No, to maintain the order of execution you need to define order using grunt Tasks and not plain JavaScript functions.

简短回答:不,为了维护执行顺序,您需要使用grunt任务来定义订单,而不是简单的JavaScript函数。

Sequential ordering is carried out by the order of each alias-task defined in the taskList Array. For example when using grunt.registerTask:

顺序排序由任务列表数组中定义的每个alias-task的顺序执行。例如使用grunt.registerTask:

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

Given the pseudo example above and running the foo Task will run task a first, followed by task b, and so on (I.e. Task b will not run until Task a completes; Task c will not run until Task b completes).

在上面的伪示例中,运行foo任务首先运行Task a,然后是Task b,以此类推(即,Task b直到Task a完成才运行;任务c在任务b完成之前不会运行)。

However that doesn't mean to suggest that it's not possible to utilize plain JavaScript functions in combination with grunt custom Tasks.

然而,这并不意味着不可能将普通的JavaScript函数与繁重的定制任务结合使用。


Long answer with solution:

长回答与解决方案:

There does not seem to be a great way to share data between Grunt tasks...

在繁重的任务之间共享数据似乎并不是一种很好的方式。

You can use an Object to store the data. The pseudo code in your first example implies that you want the configureUglifiy function to:

您可以使用对象来存储数据。您的第一个示例中的pseudo代码意味着您想要对configure - uglifiy函数进行:

  1. Dynamically configure the Uglify Task
  2. 动态配置Uglify任务
  3. return data (an Object) which is generated by configureUglifiy itself.
  4. 返回由configureUglifiy本身生成的数据(对象)。
  5. Then pass the returned data as an argument to the updateJsScriptTags function.
  6. 然后将返回的数据作为参数传递给updateJsScriptTags函数。

So, instead of returning an Object from the configureUglifiy function. You can store it in another Object, which is then subsequently accessed in the updateJsScriptTags function.

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

In the following gist take note of the shared Object, with a property/key named jsFilepathMapping. We'll use this object to store dynamically generated data which can be accessed in another task.

在下面的要点中,注意共享对象,使用名为jsFilepathMapping的属性/键。我们将使用这个对象来存储动态生成的数据,这些数据可以在其他任务中访问。

Gruntfile.js (pseudo code)

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']);
};

Note The comment that reads // <-- Do stuff here to determine configuration of uglify task. will be logic similar to the Gruntfile.js gist that I provided to your other question here.

请注意读// <——在这里做一些事情来确定uglify任务的配置。将是类似于Gruntfile的逻辑。js主旨是我给你的另一个问题。


Summary

总结

  1. Sequential ordering should be defined by adding Tasks to the TaskList Array (as per the short answer section above)
  2. 顺序排序应该通过在TaskList数组中添加任务来定义(根据上面的简短回答部分)
  3. Functions can be invoked via grunt Custom Tasks when necessary. But remember a Custom Tasks is a function so only separate logic out to it's own function when it's really necessary to do so. You'll find your Gruntfile.js reads better.
  4. 在必要时,可以通过grunt自定义任务调用函数。但是请记住,自定义任务是一个函数,所以只有在必要时,才将逻辑独立到它自己的函数中。你会发现你的Gruntfile。js读得更好。
  5. When one task/function dynamically obtains data to share between other tasks/functions consider storing it in a Object instead of returning values from functions using the return keyword.

    当一个任务/函数动态获取数据以在其他任务/函数之间共享时,请考虑将其存储在对象中,而不是使用return关键字从函数返回值。

  6. Where possible separate the logic into individual defined tasks, and try to avoid doing too much in one Task. For example, in the first custom task that you provided in your question named minifyJS you are trying to do two things in one Task. I.e. You're configuring and running your uglify task (which constitutes to one task), and also updating the the JS script tags (which constitutes to another task). This ideally should be two separate distinct tasks.

    在可能的情况下,将逻辑分离到单独定义的任务中,并尽量避免在一个任务中做太多的工作。例如,在问题minifyJS中提供的第一个自定义任务中,您试图在一个任务中做两件事。也就是说,您正在配置和运行您的uglify任务(它构成一个任务),并更新JS脚本标记(它构成了另一个任务)。理想情况下,这应该是两个不同的任务。

The pseudo Gruntfile.js that I provided (above) it not currently adopting my advice given in points 2 and 4. With some refactoring the Gruntfile.js would be more like this (Note the functions no longer exists and instead their logic has been combined with the Custom Task that invoked them):

伪Gruntfile。我提供的js(上面)没有采纳我在第2点和第4点给出的建议。重构垃圾文件。js可能更像这样(注意函数已经不存在了,它们的逻辑已经与调用它们的自定义任务结合):

Gruntfile.js (pseudo code refactored)

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']);
};

#1


2  

So as a quick summary: are there alternatives to achieving a sequential ordering of function/plugin-task/function/plugin-task, without resorting to converting the functions to custom tasks themselves?

简单总结一下:除了实现函数/插件/任务/函数/插件-任务的顺序排序之外,是否还有其他替代方法,而无需将函数转换为自定义任务本身?

Short answer: No, to maintain the order of execution you need to define order using grunt Tasks and not plain JavaScript functions.

简短回答:不,为了维护执行顺序,您需要使用grunt任务来定义订单,而不是简单的JavaScript函数。

Sequential ordering is carried out by the order of each alias-task defined in the taskList Array. For example when using grunt.registerTask:

顺序排序由任务列表数组中定义的每个alias-task的顺序执行。例如使用grunt.registerTask:

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

Given the pseudo example above and running the foo Task will run task a first, followed by task b, and so on (I.e. Task b will not run until Task a completes; Task c will not run until Task b completes).

在上面的伪示例中,运行foo任务首先运行Task a,然后是Task b,以此类推(即,Task b直到Task a完成才运行;任务c在任务b完成之前不会运行)。

However that doesn't mean to suggest that it's not possible to utilize plain JavaScript functions in combination with grunt custom Tasks.

然而,这并不意味着不可能将普通的JavaScript函数与繁重的定制任务结合使用。


Long answer with solution:

长回答与解决方案:

There does not seem to be a great way to share data between Grunt tasks...

在繁重的任务之间共享数据似乎并不是一种很好的方式。

You can use an Object to store the data. The pseudo code in your first example implies that you want the configureUglifiy function to:

您可以使用对象来存储数据。您的第一个示例中的pseudo代码意味着您想要对configure - uglifiy函数进行:

  1. Dynamically configure the Uglify Task
  2. 动态配置Uglify任务
  3. return data (an Object) which is generated by configureUglifiy itself.
  4. 返回由configureUglifiy本身生成的数据(对象)。
  5. Then pass the returned data as an argument to the updateJsScriptTags function.
  6. 然后将返回的数据作为参数传递给updateJsScriptTags函数。

So, instead of returning an Object from the configureUglifiy function. You can store it in another Object, which is then subsequently accessed in the updateJsScriptTags function.

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

In the following gist take note of the shared Object, with a property/key named jsFilepathMapping. We'll use this object to store dynamically generated data which can be accessed in another task.

在下面的要点中,注意共享对象,使用名为jsFilepathMapping的属性/键。我们将使用这个对象来存储动态生成的数据,这些数据可以在其他任务中访问。

Gruntfile.js (pseudo code)

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']);
};

Note The comment that reads // <-- Do stuff here to determine configuration of uglify task. will be logic similar to the Gruntfile.js gist that I provided to your other question here.

请注意读// <——在这里做一些事情来确定uglify任务的配置。将是类似于Gruntfile的逻辑。js主旨是我给你的另一个问题。


Summary

总结

  1. Sequential ordering should be defined by adding Tasks to the TaskList Array (as per the short answer section above)
  2. 顺序排序应该通过在TaskList数组中添加任务来定义(根据上面的简短回答部分)
  3. Functions can be invoked via grunt Custom Tasks when necessary. But remember a Custom Tasks is a function so only separate logic out to it's own function when it's really necessary to do so. You'll find your Gruntfile.js reads better.
  4. 在必要时,可以通过grunt自定义任务调用函数。但是请记住,自定义任务是一个函数,所以只有在必要时,才将逻辑独立到它自己的函数中。你会发现你的Gruntfile。js读得更好。
  5. When one task/function dynamically obtains data to share between other tasks/functions consider storing it in a Object instead of returning values from functions using the return keyword.

    当一个任务/函数动态获取数据以在其他任务/函数之间共享时,请考虑将其存储在对象中,而不是使用return关键字从函数返回值。

  6. Where possible separate the logic into individual defined tasks, and try to avoid doing too much in one Task. For example, in the first custom task that you provided in your question named minifyJS you are trying to do two things in one Task. I.e. You're configuring and running your uglify task (which constitutes to one task), and also updating the the JS script tags (which constitutes to another task). This ideally should be two separate distinct tasks.

    在可能的情况下,将逻辑分离到单独定义的任务中,并尽量避免在一个任务中做太多的工作。例如,在问题minifyJS中提供的第一个自定义任务中,您试图在一个任务中做两件事。也就是说,您正在配置和运行您的uglify任务(它构成一个任务),并更新JS脚本标记(它构成了另一个任务)。理想情况下,这应该是两个不同的任务。

The pseudo Gruntfile.js that I provided (above) it not currently adopting my advice given in points 2 and 4. With some refactoring the Gruntfile.js would be more like this (Note the functions no longer exists and instead their logic has been combined with the Custom Task that invoked them):

伪Gruntfile。我提供的js(上面)没有采纳我在第2点和第4点给出的建议。重构垃圾文件。js可能更像这样(注意函数已经不存在了,它们的逻辑已经与调用它们的自定义任务结合):

Gruntfile.js (pseudo code refactored)

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']);
};