晴明的博客园 GitHub      CodePen      CodeWars     

[web] grunt base

安装

npm install -g grunt-cli

npm install grunt -D

//npm i grunt-contrib-uglify -D

大多数Grunt任务依赖于传递给grunt.initConfig方法的对象中定义的配置数据。

grunt --help命令将列出所有可用的任务。

Gruntfile通过grunt.initConfig方法指定任务配置。此配置将主要位于任务命名属性下,但可能包含任意数据。只要属性与任务需要的属性不冲突,它们将被忽略。

此外,由于这是JavaScript,将不限于JSON; 可以在这里使用任何有效的JavaScript。如果需要,甚至可以以编程方式生成配置。

当一个任务运行时,Grunt在同名的属性下查找其配置。多任务可以有多个配置,使用任意命名的“目标”进行定义。

在任务配置中,options可以指定一个属性来覆盖内置的默认值。此外,每个目标可能具有options特定于该目标的属性。目标级选项将覆盖任务级选项。
options对象是可选的,如果不需要,也可以省略。

包裹Grunt配置。

module.exports = function(grunt) {
};

初始化配置对象:

grunt.initConfig({
});

将package.json文件中的项目设置存储到pkg属性中

pkg: grunt.file.readJSON('package.json')

插件

grunt-contrib-concat配置

concat: {
  options: {
    // define a string to put between each file in the concatenated output
    separator: ';'
  },
  dist: {
    // the files to concatenate
    src: ['src/**/*.js'],
    // the location of the resulting JS file
    //读取package.json的name
    dest: 'dist/<%= pkg.name %>.js'
  }
}

grunt-contrib-uglify配置

uglify: {
  options: {
    // the banner is inserted at the top of the output
    banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
  },
  dist: {
    files: {
      'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
    }
  }
}

grunt-contrib-qunit配置

qunit: {
  //报告输出文件
  files: ['test/**/*.html']
}

grunt-contrib-jshint配置

jshint: {
  // define the files to lint
  files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
  // configure JSHint (documented at http://www.jshint.com/docs/)
  options: {
    // more options here if you want to override JSHint defaults
    globals: {
      jQuery: true,
      console: true,
      module: true
    }
  }
}

grunt-contrib-watch配置

watch: {
  files: ['<%= jshint.files %>'],
  tasks: ['jshint', 'qunit']
}

加载插件

grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');

设定任务

// this would be run by typing "grunt test" on the command line
grunt.registerTask('test', ['jshint', 'qunit']);

// the default task can be run just by typing "grunt" on the command line
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);

配置

concat任务有foo和bar目标,而该uglify任务只有一个bar目标。
同时指定任务和目标,grunt concat:foogrunt concat:bar将只处理指定目标的配置,而运行时grunt concat将遍历所有目标,依次对其进行处理。

grunt.initConfig({
  concat: {
    foo: {
      // concat task "foo" target options and files go here.
    },
    bar: {
      // concat task "bar" target options and files go here.
    },
  },
  uglify: {
    bar: {
      // uglify task "bar" target options and files go here.
    },
  },
});

定义输入输出文件的几种方式:

紧凑格式:

grunt.initConfig({
  concat: {
    bar: {
      src: ['src/bb.js', 'src/bbb.js'],
      dest: 'dest/b.js',
    }
  }
});

文件对象格式:

grunt.initConfig({
  concat: {
    bar: {
      files: {
        'dest/b.js': ['src/bb.js', 'src/bbb.js'],
        'dest/b1.js': ['src/bb1.js', 'src/bbb1.js'],
      }
    }
  }
});

文件数组格式:

grunt.initConfig({
  concat: {
    bar: {
      files: [
        {src: ['src/bb.js', 'src/bbb.js'], dest: 'dest/b/', nonull: true},
        {src: ['src/bb1.js', 'src/bbb1.js'], dest: 'dest/b1/', filter: 'isFile'},
      ]
    }
  }
});

较旧的格式:
尽量避免用这种方式

grunt.initConfig({
  concat: {
    'dest/a.js': ['src/aa.js', 'src/aaa.js'],
    'dest/b.js': ['src/bb.js', 'src/bbb.js'],
  }
});

自定义过滤功能

这里的isFile实际上是nodejs里fs.Stats里的方法

grunt.initConfig({
  clean: {
    foo: {
      src: ['tmp/**/*'],
      filter: 'isFile',
    },
  },
});

或创建自己的filter函数并返回true或false文件是否应该匹配。
例如,以下内容只会清除为空的文件夹:

grunt.initConfig({
  clean: {
    foo: {
      src: ['tmp/**/*'],
      filter: function(filepath) {
        return (grunt.file.isDir(filepath) && require('fs').readdirSync(filepath).length === 0);
      },
    },
  },
});

利用了globbingexpand:true features - 可以避免覆盖目标中已经存在的文件,需要注意当检查目的地是否存在时,不会考虑重命名属性。

grunt.initConfig({
  copy: {
    templates: {
      files: [{
        expand: true,
        cwd: ['templates/css/'],     // Parent folder of original CSS templates
        src: '**/*.css',             // Collects all `*.css` files within the parent folder (and its subfolders)
        dest: 'src/css/',            // Stores the collected `*.css` files in your `src/css/` folder
        filter: function (dest) {    // `dest`, in this instance, is the filepath of each matched `src`
          var cwd = this.cwd,        // Configures variables (these are documented for your convenience only)
              src = dest.replace(new RegExp('^' + cwd), '');
              dest = grunt.task.current.data.files[0].dest;
          return (!grunt.file.exists(dest + src));    // Copies `src` files ONLY if their destinations are unoccupied
        }
      }]
    }
  }
});

globbing 模式

// You can specify single files:
{src: 'foo/this.js', dest: ...}
// Or arrays of files:
{src: ['foo/this.js', 'foo/that.js', 'foo/the-other.js'], dest: ...}
// Or you can generalize with a glob pattern:
{src: 'foo/th*.js', dest: ...}

// This single node-glob pattern:
{src: 'foo/{a,b}*.js', dest: ...}
// Could also be written like this:
{src: ['foo/a*.js', 'foo/b*.js'], dest: ...}

// All .js files, in foo/, in alpha order:
{src: ['foo/*.js'], dest: ...}
// Here, bar.js is first, followed by the remaining files, in alpha order:
{src: ['foo/bar.js', 'foo/*.js'], dest: ...}

// All files except for bar.js, in alpha order:
{src: ['foo/*.js', '!foo/bar.js'], dest: ...}
// All files in alpha order, but with bar.js at the end.
{src: ['foo/*.js', '!foo/bar.js', 'foo/bar.js'], dest: ...}

// Templates may be used in filepaths or glob patterns:
{src: ['src/<%= basename %>.js'], dest: 'build/<%= basename %>.min.js'}
// But they may also reference file lists defined elsewhere in the config:
{src: ['foo/*.js', '<%= jshint.all.src %>'], dest: ...}

动态构建文件对象

grunt.initConfig({
  uglify: {
    static_mappings: {
      // Because these src-dest file mappings are manually specified, every
      // time a new file is added or removed, the Gruntfile has to be updated.
      files: [
        {src: 'lib/a.js', dest: 'build/a.min.js'},
        {src: 'lib/b.js', dest: 'build/b.min.js'},
        {src: 'lib/subdir/c.js', dest: 'build/subdir/c.min.js'},
        {src: 'lib/subdir/d.js', dest: 'build/subdir/d.min.js'},
      ],
    },
    dynamic_mappings: {
      // Grunt will search for "**/*.js" under "lib/" when the "uglify" task
      // runs and build the appropriate src-dest file mappings then, so you
      // don't need to update the Gruntfile when files are added or removed.
      files: [
        {
          expand: true,     // Enable dynamic expansion.
          cwd: 'lib/',      // Src matches are relative to this path.
          src: ['**/*.js'], // Actual pattern(s) to match.
          dest: 'build/',   // Destination path prefix.
          ext: '.min.js',   // Dest filepaths will have this extension.
          extDot: 'first'   // Extensions in filenames begin after the first dot
        },
      ],
    },
  },
});

重命名属性

grunt.initConfig({
  copy: {
    backup: {
      files: [{
        expand: true,
        src: ['docs/README.md'],    // The README.md file has been specified for backup
        rename: function () {       // The value for rename must be a function
          return 'docs/BACKUP.txt'; // The function must return a string with the complete destination
        }
      }]
    }
  }
});

将文件从dev文件夹复制到dist文件夹,并重命名为删除单词“beta”。

grunt.initConfig({
  copy: {
    production: {
      files: [{
        expand: true,
        cwd: 'dev/',
        src: ['*'],
        dest: 'dist/',
        rename: function (dest, src) {          // The `dest` and `src` values can be passed into the function
          return dest + src.replace('beta',''); // The `src` is being renamed; the `dest` remains the same
        }
      }]
    }
  }
});

如果将多个匹配的src路径重命名为相同的目的地(即如果两个不同的文件被重命名为同一个文件),则每个输出将被添加到一个源的数组。

模板

<% %>当任务从配置中读取时,使用分隔符指定的模板将自动扩展。模板将递归展开,直到不再存在。

grunt.initConfig({
  concat: {
    sample: {
      options: {
        banner: '/* <%= baz %> */\n',   // '/* abcde */\n'
      },
      src: ['<%= qux %>', 'baz/*.js'],  // [['foo/*.js', 'bar/*.js'], 'baz/*.js']
      dest: 'build/<%= baz %>.js',      // 'build/abcde.js'
    },
  },
  // Arbitrary properties used in task configuration templates.
  foo: 'c',
  bar: 'b<%= foo %>d', // 'bcd'
  baz: 'a<%= bar %>e', // 'abcde'
  qux: ['foo/*.js', 'bar/*.js'],
});

导入外部数据

在Gruntfile中,项目元数据从package.json文件导入到Grunt配置中,grunt-contrib-uglify插件 uglify任务被配置为使源文件缩小,并使用该元数据动态生成横幅注释。

Grunt自带grunt.file.readJSONgrunt.file.readYAML方法帮助导入JSON和YAML数据。

grunt.initConfig({
  pkg: grunt.file.readJSON('package.json'),
  uglify: {
    options: {
      banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
    },
    dist: {
      src: 'src/<%= pkg.name %>.js',
      dest: 'dist/<%= pkg.name %>.min.js'
    }
  }
});

任务

任务别名

grunt.registerTask(taskName, [description, ] taskList)

例如定义了一个“默认”任务,如果在不指定任何任务的情况下执行Grunt,“jshint”,“qunit”,“concat”和“uglify”任务将自动运行:

grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);

也可以指定任务参数。

别名“dist”运行“concat”和“uglify”任务,每个任务都有一个“dist”参数:

grunt.registerTask('dist', ['concat:dist', 'uglify:dist']);

多任务

grunt.registerMultiTask(taskName, [description, ] taskFunction)

当运行多任务时,Grunt在Grunt配置中查找同名的属性。多任务可以有多个配置,使用任意命名的“目标”进行定义。

同时指定任务和目标,grunt concat:foogrunt concat:bar将只处理指定目标的配置,而运行时grunt concat将遍历所有目标,依次对其进行处理.

如果执行log,会执行3个子任务。也可以通过grunt log:foo这种形式执行单个任务。

grunt.initConfig({
  log: {
    foo: [1, 2, 3],       //foo: 1,2,3
    bar: 'hello world',   //bar: hello world
    baz: false            //baz: false
  }
});

grunt.registerMultiTask('log', 'Log stuff.', function() {
  grunt.log.writeln(this.target + ': ' + this.data);
});

“基本”任务

运行基本任务时,Grunt不会识别配置或环境 - 它只运行指定的任务函数,将任何指定的冒号分隔的参数作为函数参数传递。

grunt.registerTask(taskName, [description, ] taskFunction)

执行grunt foo:testing:123则输出foo, testing 123;
直接执行grunt foo则输出foo, no args.

grunt.registerTask('foo', 'A sample task that logs stuff.', 
function(arg1, arg2) {
  if (arguments.length === 0) {
    grunt.log.writeln(this.name + ", no args");
  } else {
    grunt.log.writeln(this.name + ", " + arg1 + " " + arg2);
  }
});

自定义任务

在任务中执行其他任务。

grunt.registerTask('bar', 'My "foo" task.', function() {
  // Enqueue "bar" and "baz" tasks, to run after "foo" finishes, in-order.
  grunt.task.run('bar', 'baz');
  // Or:
  //grunt.task.run(['bar', 'baz']);
});

异步任务

grunt.registerTask('asyncfoo', 'My "asyncfoo" task.', function() {
  // Force task into async mode and grab a handle to the "done" function.
  var done = this.async();
  // Run some sync stuff.
  grunt.log.writeln('Processing task...');
  // And some async stuff.
  setTimeout(function() {
    grunt.log.writeln('All done!');
    done();
  }, 1000);
});

//Running "asyncfoo" task
//Processing task...
//All done!

访问自己的名称与参数

grunt.registerTask('foo', 'My "foo" task.', function(a, b) {
  grunt.log.writeln(this.name, a, b);
});

// Usage:
// grunt foo
//   logs: "foo", undefined, undefined
// grunt foo:bar
//   logs: "foo", "bar", undefined
// grunt foo:bar:baz
//   logs: "foo", "bar", "baz"

错误捕捉

grunt.registerTask('foo', 'My "foo" task.', function() {
  if (failureOfSomeKind) {
    grunt.log.error('This is an error message.');
  }

  // Fail by returning false if this task had errors
  if (ifErrors) { return false; }

  grunt.log.writeln('This is the success message');
});

当任务失败时,除非--force指定,所有后续任务将被中止。

grunt.registerTask('foo', 'My "foo" task.', function() {
  // Fail synchronously.
  return false;
});

grunt.registerTask('bar', 'My "bar" task.', function() {
  var done = this.async();
  setTimeout(function() {
    // Fail asynchronously.
    done(false);
  }, 1000);
});

grunt.task.requires不会运行其他任务。它会检查它是否已经运行。

grunt.registerTask('foo', 'My "foo" task.', function() {
  return false;
});

grunt.registerTask('bar', 'My "bar" task.', function() {
  // Fail task if "foo" task failed or never ran.
  grunt.task.requires('foo');
  // This code executes if the "foo" task ran successfully.
  grunt.log.writeln('Hello, world.');
});

// Usage:
// grunt foo bar
//   doesn't log, because foo failed.
//   ***Note: This is an example of space-separated sequential commands,
//   (similar to executing two lines of code: `grunt foo` then `grunt bar`)
// grunt bar
//   doesn't log, because foo never ran.

如果需要的配置属性不存在,任务可能会失败。

grunt.registerTask('foo', 'My "foo" task.', function() {
  // Fail task if "meta.name" config prop is missing
  // Format 1: String
  grunt.config.requires('meta.name');

  // or Format 2: Array
  //grunt.config.requires(['meta', 'name']);

  // Log... conditionally.
  grunt.log.writeln('This will only log if meta.name is defined in the config.');
});

任务可以访问配置属性。

grunt.registerTask('foo', 'My "foo" task.', function() {
  // Log the property value. Returns null if the property is undefined.
  grunt.log.writeln('The meta.name property is: ' + grunt.config('meta.name'));
  
  // Also logs the property value. Returns null if the property is undefined.
  grunt.log.writeln('The meta.name property is: ' + grunt.config(['meta', 'name']));
});

可以通过this.async()在任务体内调用来切换到异步。
传递false给该done()功能告知Grunt该任务已失败。

grunt.registerTask('asyncme', 'My asynchronous task.', function() {
  var done = this.async();
  doSomethingAsync(done);
});

命令

Grunt默认隐藏错误堆栈跟踪,可以使用--stack选项显示堆栈追踪。

posted @ 2017-07-24 19:47  晴明桑  阅读(122)  评论(0编辑  收藏  举报