在《Grunt教程——初涉Grunt》和《Grunt教程——安装Grunt》中得知,Grunt项目中都是通过Gruntfile.js
来配置任务。可以说,Gruntfile.js
文件在任何一个Grunt项目中都是必不可少的,从这也可以看出这个文件的重要性。欲要搞清楚Grunt怎么工作,就必须要先了解Grunt是如何通过Gruntfile.js
配置任务,帮助你实现你需要的功能。如果你还不知道Gruntfile.js
是什么,建议您先阅读Grunt了解一下,如果你觉得这样麻烦,也不必过于担心,你也可以通过下面将要介绍的内容,深入的了解Gruntfile.js
,并学习如何配置。
Gruntfile.js创建
假设您已经在你的系统中创建了一个Grunt项目“my-grunt”。要正常的运行这个Grunt项目,其中package.json
和Gruntfile.js
是必不可少的,而且它们都必须位于Grunt项目的根目录之下。
你可以通过在你的终端下执行下面的命令(注,这里是Mac环境下操作的):
$ mkdir my-grunt
$ touch package.json Gruntfile.js
此时,在“my-grunt”项目的根目录下就创建了两个空文件:package.json
和Gruntfile.js
:
+my-grunt
|----Gruntfile.js
|----package.json
这样就完成了Gruntfile.js
文件创建,这也是创建Gruntfile.js
文件最简单的方法。除了这种方法之外,我们还可以通过grunt-init方法来创建。接下来我们简单的一起来过一下如何使用grunt-init
创建Gruntfile.js
文件。
首先你需要先安装grunt-init,如果还没有安装,在终端执行下面的命令:
$ npm install -g grunt-init
有的系统上面的命令安装会失败,如果失败了不用太过担心,你只要加上一个sudo
就OK了,
$ sudo npm install -g grunt-init
这个时候,在命令终端你会看到一串这样的信息结尾,就表示你安装grunt-init
:
npm http 200 https://registry.npmjs.org/request
/usr/local/bin/grunt-init -> /usr/local/lib/node_modules/grunt-init/bin/grunt-init
grunt-init@0.2.1 /usr/local/lib/node_modules/grunt-init
├── semver@1.0.14
├── colors@0.6.2
├── hooker@0.2.3
├── grunt@0.4.1 (which@1.0.5, dateformat@1.0.2-1.2.3, eventemitter2@0.4.13, async@0.1.22, lodash@0.9.2, coffee-script@1.3.3, underscore.string@2.2.1, iconv-lite@0.2.11, minimatch@0.2.12, glob@3.1.21, rimraf@2.0.3, findup-sync@0.1.2, nopt@1.0.10, js-yaml@2.0.5)
└── prompt@0.1.12 (async@0.1.22, pkginfo@0.3.0, winston@0.5.11)
至于这一大串信息是什么意思,我也不懂,先不管了,知道安装成功就行了。一旦grunt-init
安装成功,你的系统下~/.grunt-init
目录来放置模板地方。所以建议你使用git
将Gruntfile
文件模板克隆到这个目录下:
git clone https://github.com/gruntjs/grunt-init-gruntfile.git ~/.grunt-init/gruntfile
如果使用的系统是Windows,那么你可以参考这里的文档来创建。
执行完上面的命令之后,你可以在~/.grunt-init
目录下多了一个gruntfile
目录:
$ cd ~/.grunt-init
gruntfile
$ cd gruntfile
README.md root template.js
这个时候在终端上进入到项目“my-grunt”目录下,执行:
$ grunt-init gruntfile
终端会开始以提问的方式与你交互创建Gruntfile.js
文件:
Running "init:gruntfile" (init) task
This task will create one or more files in the current directory, based on the
environment and the answers to a few questions. Note that answering "?" to any
question will show question-specific help and answering "none" to most questions
will leave its value blank.
"gruntfile" template notes:
This template tries to guess file and directory paths, but you will most likely
need to edit the generated Gruntfile.js file before running grunt. If you run
grunt after generating the Gruntfile, and it exits with errors, edit the file!
Please answer the following:
[?] Is the DOM involved in ANY way? (Y/n)
我一路下来:
Please answer the following:
[?] Is the DOM involved in ANY way? (Y/n) n
[?] Will files be concatenated or minified? (Y/n) n
[?] Will you have a package.json file? (Y/n) y
[?] Do you need to make any changes to the above before continuing? (y/N) n
Writing Gruntfile.js...OK
Initialized from template "gruntfile".
Done, without errors.
这样就完成了Gruntfile.js
文件创建,不过这种方式创建出的文件和手工创建的不一样,文件中有一定的内容:
/*global module:false*/
module.exports = function(grunt) {
// 项目配置.
grunt.initConfig({
// 任务配置.
jshint: {
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: true,
newcap: true,
noarg: true,
sub: true,
undef: true,
unused: true,
boss: true,
eqnull: true,
globals: {}
},
gruntfile: {
src: 'Gruntfile.js'
},
lib_test: {
src: ['lib/**/*.js', 'test/**/*.js']
}
},
nodeunit: {
files: ['test/**/*_test.js']
},
watch: {
gruntfile: {
files: '<%= jshint.gruntfile.src %>',
tasks: ['jshint:gruntfile']
},
lib_test: {
files: '<%= jshint.lib_test.src %>',
tasks: ['jshint:lib_test', 'nodeunit']
}
}
});
// 加载任务.
grunt.loadNpmTasks('grunt-contrib-nodeunit');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
// 默认执行的任务.
grunt.registerTask('default', ['jshint', 'nodeunit']);
};
简单化一下,Gruntfiles.js
文件将长成这屌样子:
//Wrapper函数
module.exports = function(grunt) {
// 配置项目
grunt.initConfig({
// 配置任务
});
// 加载任务
grunt.loadNpmTasks('grunt任务插件名');
// 默认任务.
grunt.registerTask('default', ['任务名']);
};
文件搞定后,我们更关心的是怎么在这个文件中配置我们需要的任务。接下来,我们一起来了解他的配置。
“Wrapper”函数
每个Gruntfile.js
和Grunt插件都使用这个基本格式,前且所有你的Grunt代码都必须指定在这个函数里面:
module.exports = function(grunt){
}
Grunt配置
Grunt的任务配置都是在你的Gruntfile.js
文件中的grunt.initConfig({})
方法中指定。这个配置主要都是一些命名任务属性(通常任务都被定义为一个对象给grunt.initConfig({})
方法,而任务都是作为这个对象的属性定义的),也可以包含任意其他数据。但这些属性(其他属性)不能与你的任务所需要的属性相冲突,否则它将被忽略(一般情况下任务中的属性名都是约定俗成的)。
此外,由于这本身就是JavaScript,因此你不仅限于使用JSON;你可以在这里使用任何有效的JavaScript。必要的情况下,你甚至可以以编程的方式生成配置(比如通过其他的程序生成一个或多个任务配置)。
grunt.initConfig({
concat: {
//这里是concat任务的配置信息
},
uglify: {
//这里是uglify任务的配置信息
},
//任意非任务特定属性
my_property: 'whatever',
my_src_file: ['foo/*.js', 'bar/*.js']
});
项目和任务配置
前面说过,大多数Grunt任务所依赖的配置数据都被定义在传递给grunt.initConfig方法的一个对象中。
我们先来看一个简单的例子,为了实现示例中的功能,需要先完善package.json
文件的内容,如下所示:
{
"name": "my-grunt",
"version": "0.1.0",
"description": "this is test project with grunt.",
"author": "airen",
"license": "BSD",
"devDependencies": {
"grunt": "~0.4.1",
"grunt-contrib-uglify": "*"
}
}
如果你不知道如何创建package.json
文件,建议你先阅读前面的《Grunt教程——初涉Grunt》一文,你将会有一个初步的了解,在下一节中,我们将会对package.json
文件做一个较为详细的阐述。
在这个示例中,grunt.file.readJSON('package.json')
会把存储在package.json
中的JSON元素数据导入到Grunt配置中。由于<% %>
模板字符串可以引用任意的配置属性,因此可以通过这种方式来指定诸如文件路径和文件列表类型的配置数据,从而减少一些重复的工作(比如我们通常需要通过复制粘贴的方式来在不同的地方引用同一个属性,使用<% %>
的方式可以简单的理解为将某些特定的数据存储在变量中,然后在其他地方像使用变量一样就可以使用这些数据属性)。
与大多数任务一样,grunt-contrib-uglify插件的uglify
任务要求它的配置被指定在一个同名属性中。在这里有一个例子,我们指定了一个banner
选项(用于在文件顶部生成一个注释),紧接都会是一个单一的名为build
的uglify目录,用于将一个js文件压缩为一个目标文件(比如将src/js
目录下的jquery-1.9.0.js
压缩为jquery-1.9.min.js
,然后存储到dest/js
目录)。
// 项目配置
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/js/<%= pkg.name %>.js',
dest: 'build/js/<%= pkg.name %>.min.js'
}
}
});
任务配置和目标
当运行一个任务时,Grunt会自动查找配置对象中的同名属性。多个任务可以有多个配置,每个任务可以使用任意的命名“targets”来自定义多个任务目标。在上面的示例中,添加Sass任务:
//Wrapper函数
module.exports = function(grunt) {
// 配置项目
grunt.initConfig({
// 配置任务
pkg:grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/js/<%= pkg.name %>.js',
dest: 'build/js/<%= pkg.name %>.min.js'
}
},
sass: {
dist: {
options: {
style: 'compressed'
},
expand: true,
cwd: 'src/styles/sass/',
src: ['*.scss'],
dest: 'build/styles/css',
ext: '.css'
},
dev: {
options: {
style: 'expanded',
debugInfo: true,
lineNumbers: true
},
expand: true,
cwd: 'src/styles/sass/',
src: ['*.scss'],
dest: 'build/styles/css',
ext: '.css'
}
}
});
};
在示例当中,sass
任务有两个字任务dist
和dev
。在命令行中执行grunt sass:dist
或者grunt sass:dev
的任务和目标只会处理指定的任务目标配置,而运行grunt sass
将遍历所有的(定义在sass
任务中的)目标并依次处理。注意,如果一个任务使用grunt.renameTask重命过,Grunt将在配置对象中查找新的任务名称属性。
加载Grunt插件和任务
许多常用的任务像concatenation,minification和linting都被作为grunt插件来使用。只要一个插件被作为一个依赖指定在项目的package.json
文件中,并且已经通过npm install
安装好,都可以在你的Gruntfile.js
文件中使用下面这个简单的命令启用它(所依赖的任务)。
//加载提供“uglify”任务的插件
grunt.loadNpmTasks('grunt-contrib-uglify');
//加载提供"sass"任务插件
grunt.loadNpmTasks('grunt-contrib-sass');
设置默认任务
在Gruntfile.js
文件中,你可以通过定义一个default
任务来配置Grunt,让它默认运行一个或者多个任务。在下面的例子中,在命令中运行grunt
而不指定特定的任务将自动运行uglify
、sass
任务。这个功能与显示的运行grunt uglify
和grunt sass
或者等价运行grunt default
一样。你可以在任务参数数组指定任意数量的任务(这些任务可以带参数,也可以不带参数)。
// 默认任务
grunt.registerTask('default', ['uglify','sass']);
自定义任务
如果你的项目所需的任务没有对应的Grunt插件提供相应的功能,你可以在Grunt
内定义自定义的任务。例如,下面的Gruntfile就定义了一个完整的自定义的default
任务,它甚至没有利用任务配置(没有使用grunt.initConfig({})
方法):
module.exports = function(grunt){
//一个非常基础的default任务
grunt.registerTask('default','Log some stuff.',function(){
grunt.log.write('Logging some stuff...').ok();
});
};
自定义的项目特定的任务可以不定义在Gruntfile.js
中;它可以定义在一个外部.js
文件中,然后通过grunt.loadTasks方法来加载。
options
在一个任务配置中,options
属性可以用来指定覆盖内置属性的默认值。此外,每一个任务目标中更具体的目标都可以拥有一个options
属性。目标级的选项将会覆盖任务级的选项(就近原则——options
离目标越近,其优先级越高)。
options
对象是可选,如果不需要,可以省略。
grunt.initConfig({
concat: {
options: {
// 这里是任务级的Options,覆盖任务的默认值
},
foo: {
options: {
// 这里是'foo'目标的options,它会覆盖任务级的options.
}
},
bar: {
// 没有指定options,这个目标将使用任务级的options
}
}
});
文件
由于大多的任务都是执行文件操作,Grunt有一个强大的抽象声明说明任务应该操作哪些文件。这里有好几种定义src-dest(源文件——目标文件)文件映射的方式,都提供了不同程度的描述和控制操作方式。任何一种多任务(包含多个任务目标的任务)都能理解下面的格式,所以你只需要选择满足你需要的格式就行。
所有的文件格式都支持src
和dest
属性。
简洁格式
这种形式允许每个目标对应一个src-dest文件映射。通常情况下它用于只读任务,比如grunt-contrib-jshint,它就只需要一个单一的src
属性,而不需要关联的dest
选项。这种格式还支持给每个src-dest
文件映射指定附加属性。
grunt.initConfig({
jshint: {
src: ['src/js/a.js','src/js/aa.js']
}
});
文件对象格式
这种形式支持每个任务目标对就多个src-dest
形式的文件映射,属性名就是目标文件,源文件就是它的值(源文件列表则使用数组格式声明)。可以使用这种方式指定数个src-dest
文件映射,但是不能够给每个映射指定附加的属性。
grunt.initConfig({
concat: {
foo: {
files: {
'dest/a.js': ['src/aa.js', 'src/aaa.js'],
'dest/a1.js': ['src/aa1.js', 'src/aaa1.js']
}
},
bar: {
files: {
'dest/b.js': ['src/bb.js', 'src/bbb.js'],
'dest/b1.js': ['src/bb1.js', 'src/bbb1.js']
}
}
}
});
文件数组格式
这种形式支持每个任务目标对应多个src-dest
文件映射,同时也允许每个映射拥有附加属性:
grunt.initConfig({
concat: {
foo: {
files: [
{src: ['src/aa.js', 'src/aaa.js'], dest: 'dest/a.js'},
{src: ['src/aa1.js', 'src/aaa1.js'], dest: 'dest/a1.js'}
]
},
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'}
]
}
}
});
较老的格式
dest-as-target文件格式在多任务和目标形式出现之前是一个过渡形式,目标文件路径实际上就是目标名称。遗憾的是, 由于目标名称是文件路径,那么运行grunt task:target
可能不合适。此外,你也不能指定一个目标级的options
或者给每个src-dest
文件映射指定附加属性。
grunt.initConfig({
concat: {
'dest/a.js': ['src/aa.js', 'src/aaa.js'],
'dest/b.js': ['src/bb.js', 'src/bbb.js']
}
});
自定义过滤函数
filter
属性可以给你的目标文件提供一个更高级的详细帮助信息。只需要使用一个有效的fs.Stats方法名。下面的配置仅仅清理一个与模式匹配的真实的文件:
grunt.initConfig({
clean: {
foo: {
src: ['temp/**/*'],
filter: 'isFile'
}
}
});
或者创建你自己的filter
函数根据文件是否匹配来返回true
或者false
。下面的例子将仅仅清理一个空目录:
grunt.initConfig({
clean: {
foo: {
src: ['temp/**/*'],
filter: function(filepath){
return (grunt.file.isDir(filepath) && require('fs').readdirSync(filepath).length === 0);
}
}
}
});
通配符模式
原文档标题为Globbing patterns,大意是指使用一些通配符形式的匹配模式快速的匹配文件。
通常分别指定所有源文件路径的是不切实际的(也就是将源文件-目标文件一一对应的关系列出来),因此Grunt支持通过内置的node-glob和minimatch库来匹配文件名(又叫作globbing
)。
当然这并不是一个综合的匹配模式方面的教程,你只需要知道如何在文件路径匹配过程中使用它们即可:
-
*
匹配任意数量的字符,但不匹配/
-
?
匹配单个字符,但不匹配/
-
**
匹配任意数量的字符,包括/
,只要它是路径中唯一的一部分 -
{}
允许使用一个逗号分割的列表或者表达式 -
!
在模式的开头用于否定一个匹配模式(即排除与模式匹配的信息)
大多数的人都知道foo/*.js
将匹配位于foo/
目录下的所有的.js
结尾的文件, 而foo/**/*js
将匹配foo/
目录以及其子目录中所有以.js
结尾的文件。
此外, 为了简化原本复杂的通配符模式,Grunt允许指定一个数组形式的文件路径或者一个通配符模式。模式处理的过程中,带有!
前缀模式不包含结果集中与模式相配的文件。 而且其结果集也是唯一的。
示例:
//可以指定单个文件
{src: 'foo/this.js', dest: …}
//或者指定一个文件数组
{src: ['foo/this.js', 'foo/that.js', 'foo/the-other.js'], dest: …}
//或者使用一个匹配模式
{src: 'foo/th*.js', dest: …}
//一个独立的node-glob模式
{src: 'foo/{a,b}*.js', dest: …}
//也可以这样编写
{src: ['foo/a*.js', 'foo/b*.js'], dest: …}
//foo目录中所有的.js文件,按字母排序
{src: ['foo/*js'], dest: …}
//这里首先是bar.js,接着是剩下的.js文件按字母排序
{src: ['foo/bar.js', 'foo/*.js'], dest: …}
//除bar.js之外的所有的.js文件,按字母排序
{src: ['foo/*.js', '!foo/bar.js'], dest: …}
//所有.js文件按字母排序, 但是bar.js在最后.
{src: ['foo/*.js', '!foo/bar.js', 'foo/bar.js'], dest: …}
//模板也可以用于文件路径或者匹配模式中
{src: ['src/<%= basename %>.js'], dest: 'build/<%= basename %>.min.js'}
//它们也可以引用在配置中定义的其他文件列表
{src: ['foo/*.js', '<%= jshint.all.src %>'], dest: …}
可以在node-glob和minimatch文档中查看更多的关于通配符模式的语法。
构建动态文件对象
当你希望处理大量的单个文件时,这里有一些附加的属性可以用来动态的构建一个文件. 这些属性都可以指定在Compact
和Files Array
映射格式中(这两种格式都可以使用)。
-
expand
设置true
用于启用下面的选项: -
cwd
相对于当前路径所匹配的所有src
路径(但不包括当前路径。) -
src
相对于cwd
路径的匹配模式。 -
dest
目标文件路径前缀。 -
ext
使用这个属性值替换生成的dest
路径中所有实际存在文件的扩展名(比如我们通常将压缩后的文件命名为.min.js
)。 -
flatten
从生成的dest
路径中移除所有的路径部分。 -
rename
对每个匹配的src
文件调用这个函数(在执行ext
和flatten
之后)。传递dest
和匹配的src
路径给它,这个函数应该返回一个新的dest
值。 如果相同的dest
返回不止一次,每个使用它的src
来源都将被添加到一个数组中。
在下面的例子中,minify
任务将在static_mappings
和dynamic_mappings
两个目标中查看相同的src-dest
文件映射列表, 这是因为任务运行时Grunt
会自动展开dynamic_mappings
文件对象为4个单独的静态src-dest
文件映射--假设这4个文件能够找到。
可以指定任意结合的静态src-dest
和动态的src-dest
文件映射。
grunt.initConfig({
minify: {
static_mappings: {
//由于这里的src-dest文件映射时手动指定的, 每一次新的文件添加或者删除文件时,Gruntfile都需要更新
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: {
//当'minify'任务运行时Grunt将自动在"lib/"下搜索"**/*.js", 然后构建适当的src-dest文件映射,因此你不需要在文件添加或者移除时更新Gruntfile
files: [
{
expand: true, //启用动态扩展
cwd: 'lib/', //批匹配相对lib目录的src来源
src: '**/*.js', //实际的匹配模式
dest: 'build/', //目标路径前缀
ext: '.min.js' //目标文件路径中文件的扩展名.
}
]
}
}
});
模板
使用<% %>
分隔符指定的模会在任务从它们的配置中读取相应的数据时将自动扩展扫描。模板会被递归的展开,直到配置中不再存在遗留的模板相关的信息(与模板匹配的)。
整个配置对象决定了属性上下文(模板中的属性)。此外,在模板中使用grunt
以及它的方法都是有效的,例如:<%= grunt.template.today('yyyy-mm-dd') %>
。
-
<%= prop.subprop %>
将会自动展开配置信息中的prop.subprop
的值,不管是什么类型。像这样的模板不仅可以用来引用字符串值,还可以引用数组或者其他对象类型的值。 -
<% %>
执行任意内联的JavaScript代码,对于控制流或者循环来说是非常有用的。
下面提供了一个concat
任务配置示例,运行grunt concat:sample
时将通过banner中的/* abcde */
连同foo/*.js+bar/*.js+bar/*.js
匹配的所有文件来生成一个名为build/abcde.js
的文件。
grunt.initConfig({
concat: {
sample: {
options: {
banner: '/* <%= baz %> */\n' // '/* abcde */\n'
},
src: ['<%= qux %>', 'baz/*.js'], // [['foo/*js', 'bar/*.js'], 'baz/*.js']
dest: 'build/<%= baz %>.js'
}
},
//用于任务配置模板的任意属性
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
任务被配置用于压缩一个源文件以及使用该元数据动态的生成一个banner
注释。
Grunt有grunt.file.readJSON
和grunt.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的任务配置相关的知识都已涉及到了,但不同的Grunt的任务其配置都略有不同,不过针对不同的插件任务配置,可以查阅每个Grunt插件的说明文档。
对于初学者来说,或许上面的内容有很多都理解不过来,其实我也一样,不过不用担心,当你看得多,做得多,到一定的时候自然就整得明白是怎么一回事。现在我们返回到示例中。根据上面的内容,给"my-grunt"项目创建了grunt-contrib-uglify
和grunt-contrib-sass
两个任务,并且完成了一些简单的配置:
//Wrapper函数
module.exports = function(grunt) {
// 配置项目
grunt.initConfig({
// 配置任务
pkg:grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/js/<%= pkg.name %>.js',
dest: 'build/js/<%= pkg.name %>.min.js'
}
},
sass: {
dist: {
options: {
style: 'compressed'
},
expand: true,
cwd: 'src/styles/sass/',
src: ['*.scss'],
dest: 'build/styles/css',
ext: '.css'
},
dev: {
options: {
style: 'expanded',
debugInfo: true,
lineNumbers: true
},
expand: true,
cwd: 'src/styles/sass/',
src: ['*.scss'],
dest: 'build/styles/css',
ext: '.css'
}
}
});
// 加载任务
//加载提供“uglify”任务的插件
grunt.loadNpmTasks('grunt-contrib-uglify');
//加载提供"sass"任务插件
grunt.loadNpmTasks('grunt-contrib-sass');
// 默认任务.
grunt.registerTask('default', ['uglify','sass']);
};
仅这样Grunt不会自动执行任何任务,因为我们只完成了配置,但没有安装Grunt里面的任务。如果你需要安装,只需要在命令终端执行:
$ npm install
在终端中,看到下面这样的结束信息,表示安装成功:
...
grunt-contrib-uglify@0.2.5 node_modules/grunt-contrib-uglify
├── grunt-lib-contrib@0.6.1 (zlib-browserify@0.0.1)
└── uglify-js@2.4.3 (uglify-to-browserify@1.0.1, async@0.2.9, optimist@0.3.7, source-map@0.1.31)
同时在项目的根目录下会自生成一个node_modules
目录,而且在Grunt中配置的相关的任务都放在这个目录中。
+my-grunt
|----Gruntfile.js
+----node_modules
|----+----grunt
|----+----grunt-contrib-sass
|----+----grunt-contrib-uglify
|----package.json
在整个项目中,uglify
任务是用来压缩项目中.js
文件,sass
任务是用来编译项目中的.scss
文件。为了证明我们创建的任务是正确的,我们在项目中创建一个js
文件和.scss
文件来检验是否成功。并且将.js
文件放置在src/js
目录下,.scss
文件放置在src/styles/sass
目录下。同时我们创建两测试文件:my-grunt.js
和main.scss
。
//my-grunt.js
(function($){
var str = "<p>hello grunt!</p>";
$("body").append(str);
})(jQuery)
//main.scss
$color: #f36;
$bgColor: #fff;
$unit: 30px;
body {
background-color: $bgColor;
}
.container {
margin: ($unit / 3) auto;
width: $unit * 3;
}
注:因为我们uglify
任务中使用是<% pk.name %>
模板,而这里面的name
对应的就是package.json
中的name
参数。因此需要创建一个my-grunt.js
。
你要执行Grunt任务的时候,只需要在命令行中执行:
$ grunt
这个时候,你在终端可以看到对应的信息:
Running "uglify:build" (uglify) task
File "build/js/my-grunt.min.js" created.
Running "sass:dist" (sass) task
Running "sass:dev" (sass) task
Done, without errors.
同时项目自动会生成一个名叫build
的目录,而压缩的.min.js
文件对应放置在build/js
目录下;编译出来的.css
放置在build/style/css
下:
+my-grunt
+----build
+----+----js
+----+----|----my-grunt.min.js
+----+----styles
+----+----+----css
+----+----+----|----main.css
|----Gruntfile.js
+----node_modules
|----package.json
+----src
+----+----js
+----+----|----my-grunt.js
+----+----styles
+----+----+----sass
+----+----+----|----main.scss
因为我们在Grunt中配置了默认任务,如果没有设置默认任务,必须需要单独执行:
$ grunt uglify
$ grunt sass
结论
本文主要向大家介绍了Grunt项目中Gruntfile.js
文件的配置以及如何在这个文件中配置对应的任务。并且详细介绍了配置任务中的一些参数与细节的使用。最后通过一个简单示例,向大家介绍了如何使用Grunt配置任务。虽然文章中有很多部分对于初学者来说理解有一定难度,但我相信,随着时间的推移,会慢慢搞懂文章中介绍的所有知识。最后希望本文对初学者有所帮助。
相关推荐
前端项目构建工具的使用官网:Gurnt、Gulp、Webpack
generator-pat-marionette generatorGetting Started全局安装 yeoman、grunt-cli、bowernpm install -g yonpm install -g grunt-clinpm install -g bower由于当前generator没有注册到gurnt上去,所以你要先把源文件...
中小企业数字化转型指南(2025年) 2025-1
移动开发_安卓_键盘面板布局冲突处理_微信解决方案_1744035957
华为OD机考辅导材料50题
软考网络工程师考试是计算机技术与软件专业技术资格(水平)考试(简称“软考”)中的一个重要科目,属于中级资格。考试分为上午和下午两部分,上午为选择题,下午为案例分析题。软考网络工程师考试需要系统学习和充分准备。通过合理利用官方教材、历年真题、复习笔记、视频教程等资源,结合科学的备考计划和技巧,考生可以有效提升备考效率,顺利通过考试。
内容概要:本文详细介绍了如何利用Matlab Simulink平台实现事件触发控制的仿真,并探讨了其稳定性的实现方法。文章首先解释了事件触发控制的基本原理,即仅在系统状态满足特定条件时进行控制动作,从而减少不必要的计算和资源消耗。接着,通过具体步骤指导读者搭建一个二阶系统的事件触发控制仿真模型,包括创建基础模型、添加事件触发模块以及构建控制回路。文中还提供了关键的Matlab代码片段用于实现事件触发逻辑,并详细讲解了各个模块的功能和参数设置。此外,文章强调了仿真参数的选择对结果的影响,并给出了具体的设置建议。最后,通过对仿真结果的分析,展示了事件触发控制的有效性和优越性。 适合人群:具有一定Matlab和Simulink基础的科研人员、工程师及学生。 使用场景及目标:①帮助用户深入了解事件触发控制的工作原理;②掌握使用Matlab Simulink进行复杂控制系统仿真的技能;③提高实际控制系统设计中的效率和性能。 其他说明:文章不仅提供了理论知识,还包括大量实战经验和技巧分享,有助于读者更好地理解和应用事件触发控制技术。
内容概要:本文详细介绍了C#编程语言的学习资源,涵盖了从基础到高级的不同层次。首先,对于初学者,推荐了多本经典书籍如《C# 8.0 and .NET Core 3.0 - Modern Cross-Platform Development》、《C# 9.0 in a Nutshell》等,以及在线课程和视频教程,帮助理解C#的基本语法、面向对象编程等概念。接着,针对进阶开发者,提供了深入研究C#特性的书籍和课程,如《C# 9 and .NET 5 – Modern Cross-Platform Development》,并推荐了一些实战项目来巩固所学知识。最后,为高级开发者准备了深入探讨C#内部机制和技术细节的书籍,如《CLR via C#》,以及高级课程和开源项目。此外,还介绍了常用的开发工具(如Visual Studio、Visual Studio Code)、框架(如ASP.NET Core、Entity Framework Core),并列出了活跃的开发社区和论坛供交流学习。; 适合人群:所有对C#编程感兴趣的开发者,无论是初学者还是有经验的专业人士。; 使用场景及目标:①初学者可以通过基础资源快速入门C#编程,掌握基本语法和面向对象编程;②进阶开发者可以利用提供的资源深入理解C#特性,如并发编程、异步编程等;③高级开发者则可以通过高级资源和实战项目提升技术水平,解决复杂问题。; 其他说明:文中提到的资源不仅限于理论学习,还包括大量实战项目和开源项目,鼓励开发者在实践中不断积累经验。同时,活跃的社区和论坛也为开发者提供了良好的交流平台,有助于解决问题和获取最新资讯。
内容概要:本文档详细介绍了Systick定时器的基础知识及其应用。Systick定时器是一个24位倒计数定时器,广泛用于延时或作为实时系统的心跳时钟。它能在计数到0后自动从RELOAD寄存器重载初始值,并可在睡眠模式下持续运行。文档重点解析了四个关键寄存器(CTRL、LOAD、VAL、CALIB)的功能与操作方式,并提供了基于这些寄存器实现的延时函数(delay_init、delay_us、delay_ms)。此外,还介绍了Systick库函数如SysTick_CLKSourceConfig()用于配置时钟源以及SysTick_Config()用于初始化Systick并设置中断优先级,最后给出了Systick中断服务函数SysTick_Handler()的定义。; 适合人群:对嵌入式系统开发有一定了解,特别是ARM架构的开发者。; 使用场景及目标:①理解Systick定时器的工作原理及内部寄存器的作用;②掌握如何利用Systick实现精确延时;③学会配置Systick定时器以适应不同的应用场景,如低功耗模式下的定时任务。; 阅读建议:建议读者结合实际硬件平台进行实验,深入理解各个寄存器的功能及延时函数的具体实现细节,同时关注Systick在不同工作模式下的行为特性。
基于JAVAWEB校园订餐系统项目源码
可以把安装在电脑上的Windows 10企业版、教育版、专业版系统改装成Windows 10家庭版操作系统。
# 基于 React 框架的前端技术示例项目 ## 项目简介 本项目是一个基于 React 框架开发的前端示例项目,涵盖了 React 多种核心技术和开发模式,包含状态管理、路由懒加载、Hooks 使用、组件优化等多个方面,旨在帮助开发者深入理解和掌握 React 相关知识,可作为学习和实践 React 开发的参考。 ## 项目的主要特性和功能 1. 状态管理介绍了 setState 更新状态的两种写法(对象式和函数式),并说明了不同场景下的使用原则。同时,运用 React Hooks 中的 useState 让函数组件也能拥有状态并进行读写操作。 2. 路由懒加载通过 React 的 lazy 函数配合 import() 函数实现路由组件的动态加载,利用 <Suspense> 组件在加载路由打包文件前显示自定义的 loading 界面。
《医院挂号小程序源码》简介 该资源为医院挂号小程序源码,是一份极具实用价值的学习资源。在当今数字化医疗蓬勃发展的时代,小程序凭借其便捷性、轻量化等优势,成为医疗服务的重要载体。此挂号小程序源码蕴含丰富功能,涵盖用户端与管理端。 从用户端来看,患者可轻松注册登录,快速查询各科室医生排班信息,根据自身需求精准预约挂号,还能实时查看挂号进度与就诊提醒,极大提升就医体验。管理端则方便医院工作人员高效处理挂号业务,包括号源管理、预约审核、患者信息统计等,助力医院实现挂号流程的信息化与规范化。 对于开发者而言,研究这份源码能深入理解小程序开发架构、前后端交互逻辑以及医疗行业特定需求下的功能实现方式,从中汲取项目实战经验,拓宽技术视野,无论是用于个人技能提升,还是作为商业项目开发的参考范例,都有不可多得的学习价值,但需明确此资源仅作学习交流使用。
# 基于HTML、CSS和JavaScript的TomMaixs studio网站 ## 项目简介 此项目是TomMaixs studio的开源项目仓库,用于存储和维护TomMaixs studio的网站源代码。包含网站的基础架构、页面设计及必要配置信息,采用Apache License 2.0许可证开源,可供开发者免费使用和学习。 ## 项目的主要特性和功能 ### 主要特性 基于HTML、CSS和JavaScript构建,具备良好的跨平台兼容性。 有基础的网站架构和页面设计,便于开发者二次开发。 采用Apache License 2.0许可证,保证项目开放性和自由性。 ### 功能介绍 提供基础的网页浏览功能,涵盖主页、项目页面和关于页面等。 具备基础的网站导航和页面交互功能。 ## 安装使用步骤 1. 你已下载本项目的源码文件。 2. 确保开发环境安装了HTML、CSS和JavaScript的编辑器或IDE。
内容概要:本文详细介绍了如何构建一维声子晶体结构带隙的仿真模型。首先解释了什么是声子晶体以及带隙的概念,接着阐述了平面波展开法和传递矩阵法这两种常用的一维声子晶体建模方法。文中提供了具体的Python代码示例,展示了如何通过有限元方法和传递矩阵法进行带隙计算,并通过Matplotlib库进行了可视化展示。此外,还讨论了一些常见的数值仿真陷阱及解决方案,如频率分辨率设置不当导致的锯齿状跳变等问题。 适合人群:对材料科学、物理学感兴趣的科研人员和技术爱好者,尤其是那些希望深入了解声子晶体带隙特性和仿真的读者。 使用场景及目标:适用于需要理解和研究声子晶体带隙特性的场合,如隔音材料设计、滤波器开发等。通过本文的学习,读者可以掌握一维声子晶体的基本理论和仿真技巧,为进一步的实际应用打下坚实的基础。 其他说明:本文提供的模型虽然相对简化,但对于初学者来说非常友好,有助于快速入门并激发更深层次的研究兴趣。同时,文中提到的一些优化建议和常见错误提示也非常有价值,可以帮助避免不必要的弯路。
《基于 uni-app 开发的菜谱小程序新版源码》简介 这是一份极具实用价值的学习资源——基于 uni-app 开发的菜谱小程序新版源码。uni-app 凭借其多端适配的强大特性,一次开发就能在多个平台运行,极大地提升了开发效率与应用覆盖范围。 此菜谱小程序源码功能丰富。它拥有精美且简洁直观的界面设计,方便用户快速上手操作。在内容呈现上,涵盖了海量的菜谱资源,无论是家常小菜、地方特色菜肴,还是异国料理,都能在其中找到详细的做法教程,步骤清晰,图文并茂,甚至部分还搭配了视频讲解,让烹饪新手也能轻松学会。 对于开发者而言,这份源码是学习 uni-app 开发的绝佳范例。可以深入研究其代码架构、页面布局、数据交互以及各种功能的实现逻辑,从中汲取经验,提升自己在跨平台移动应用开发方面的能力,更好地理解 uni-app 框架的优势与应用场景,为后续的开发项目奠定坚实的基础,助力开发出更多优质且高效的应用程序。
内容概要:本文详细探讨了不平衡电网对整流电路和逆变电路的影响,并提出了通过Simulink仿真的解决方案。主要内容包括:不平衡电网对整流电路直流侧电压和逆变电路交流侧电流的影响机制;正负序分离方法及其在Simulink中的实现步骤;通过Simulink搭建模型验证控制策略的效果。文中还提供了具体的Matlab代码片段用于实现正负序分离和控制算法,展示了如何通过双闭环结构和PI调节器优化系统性能。 适合人群:从事电力电子、电力系统分析的研究人员和技术人员,尤其是那些关注电网不平衡条件下电力设备性能优化的人群。 使用场景及目标:适用于研究和开发不平衡电网条件下的电力电子设备控制系统,旨在提高电力系统的稳定性和电能质量。主要目标是减少直流侧电压的二倍频率波动和交流侧电流的负序分量,提升设备运行效率。 其他说明:文章不仅介绍了理论背景,还提供了详细的仿真模型构建指导和代码实现,有助于读者快速理解和应用所提出的控制策略。此外,文中提到的实际案例和调试技巧也为实际工程项目提供了宝贵的实践经验。
在本案例“HowToBuildAMultibodyInSimulink”中,我们探索了如何使用MATLAB Simscape构建曲柄摇块机构的仿真模型。通过这个实例,你将学习到从零开始创建多体动力学模型的基本步骤,包括组件的选择与连接、参数设置以及仿真的运行。这是一个深入了解Simscape功能及其在机械系统建模中应用的绝佳入门教程,适合初学者提升技能并激发进一步学习的兴趣。
Win11任务栏大小调整工具.exe
软著 (2)(1)(1).docx