Grunt.js 小试






4.55/5 (7投票s)
一个小演示应用程序,展示如何使用 Grunt 完成一些事情
引言
任何称职的开发人员都会摆弄 Visual Studio 2015,而我们这些对 Web 开发感兴趣的人自然会尝试 ASP vNext 的新功能。
我立即想到的一件事是,微软确实兑现了他们拥抱更多“开源”工作的承诺,这体现在 ASP vNext 中。因此,任何新的 ASP vNext 开发,你都可能需要学习这些东西:
NPM 和 Bower 使用起来非常简单,所以你不应该害怕它们。至于 NuGet,如果你是 .NET 开发人员,你很可能以前用过 NuGet,没什么新鲜的。
我个人以前使用过 Node 和 NPM,但我没用过 Bower,不过一天之内就熟悉了。至于 Gulp / Grunt,我当然听说过这些工具但没用过。我看了两者,实际上更喜欢 Grunt 的语法而不是 Gulp。这两者本质上都在做同样的事情,它们都是任务运行器。任务可能是一些你一直想做的事情,比如监视一些文件,当它们改变时执行某个操作,压缩内容,将内容从一个文件夹移动到另一个文件夹等等。这些都是可以自动化的枯燥任务。这就是 Gulp / Grunt 的全部意义所在,也就是任务运行器的意义。
Gulp 声称做得更好的一点是流式传输文件,从而可以异步完成。话虽如此,我只是觉得用 Grunt 更自在,所以我就决定用它了。值得注意的是,对于 ASP vNext,你可以使用 Grunt 或 Gulp。
因此,我决定学习 Grunt,并想在这里与和我一样(即 JavaScript 任务运行器新手)的任何人分享我的发现。
入门
开始学习 Grunt 的最佳地点是访问 Grunt 网站,那里有许多有用的指南可以帮助你入门。然而,下面的部分应该至少能让你对 Grunt 有更多了解,通过阅读本文和 Grunt 网站上的 Grunt 资源,你应该能够轻松地开始使用 Grunt。
代码在哪里?
照例,你可以从 github 账户获取代码:https://github.com/sachabarber/SmallGruntDemo
必备组件
在继续阅读本文之前,你应该确保执行此操作。
安装 Grunt
要安装 Grunt,有几件事是你必须做的。下面将进行概述。
- 安装 Node(因为我们需要 NPM 来运行 Grunt。所以我们需要 Node,因为 NPM 需要 Node)
- 在“Node.js 命令行”中运行命令“npm install -g grunt”。这会将 Grunt 本身全局安装。
- 在“Node.js 命令行”中运行命令“npm install -g grunt-cli”。这会将 Grunt 客户端全局安装。
Grunt 背后的理念
正如我之前所说,Grunt 是一个任务运行器。它用 JavaScript 编写,并有一个配置文件 (gruntfile.js) 来详细说明需要运行的任务。它有点像 MSBuild / NANT,因为它有一个默认任务。
任务也可以调用其他任务。Grunt 还依赖 NPM 来安装某些 Grunt 插件。
嗯,什么是 Grunt 插件?一个 Grunt 插件,就是一个预先构建好的 Grunt 任务,它是由 Grunt 团队或 Grunt 社区编写的。
官方 Grunt 团队的插件都遵循“grunt-contrib-XXXXXX”的命名约定。这样你就可以区分“官方”包和非官方包了。
这些 Grunt 插件(实际上是通过使用 NPM 安装的)需要被加载到 Grunt 上下文中。这可以通过以下类似的行来完成:
grunt.loadNpmTasks('grunt-contrib-clean');
一旦插件被加载,它就可以被 Grunt 使用了。这是一个使用已加载的 Grunt 插件的示例,其中可以看到插件有一个名称,并且是一个 JSON 对象。每个插件都有自己的 API,所以你需要查阅你选择使用的 Grunt 插件的 API 文档。
// Cleans directories
clean: {
dist: [
'dist/**/*'
],
tmp: [
'tmp/**/*'
]
},
演示应用程序
好的,现在来看看演示应用程序的功能。这个演示应用程序是一个非常简单的 VS2013 解决方案,它执行以下操作:
- 使用 Typescript Greeter(TypeScript 中的“Hello World”示例)
- 将 jQuery 用作外部模块,我们将使用 TypeScript 的 jQuery 外部类型声明,以使 require/export 包按我们所需的方式工作。
- 将使用 grunt-browserify
- 将使用 grunt-contrib-clean 来清理特定目录
- 将使用 grunt-ts 来编译 TypeScript 文件(请注意,Visual Studio 对 TypeScript 编译有极好的支持,但我只是想尽可能地通过 Grunt 来完成所有事情,以向自己证明一些东西)
- 将使用 grunt-browserify 将 JavaScript 源文件打包到一个输出文件中
- 将使用 grunt-contrib-watch 来监视源文件的变化,并在这些文件发生变化时运行任务
- 将使用 grunt-contrib-uglify 来压缩 JavaScript 源文件(如果 Grunt 文件任务是“
prod
”任务),生成一个单一的输出文件
为演示应用程序需要做的额外事情
如果你想使用演示代码,则需要执行这些步骤。
步骤 1:获取缺失的 NPM 模块
与 NuGet 类似,你不会将包本身提交到源代码控制存储库(例如 GitHub)。NPM 也是如此,你会提交一个配置文件 (package.json
),它告诉 NPM 需要哪些包,然后每个最终用户都应该使用 NPM 在本地安装这些包。
现在你应该确保执行此操作。
在 Node.js 命令行中,切换到包含 package.json 的目录,并运行以下命令行:
"npm install"。这将使用 `package.json` 文件中的信息(从互联网获取所有包,因此请确保你有连接)。你应该会看到类似以下内容,其中“node_modules”目录将包含所有下载的包。
项目结构概览
我们为此小型演示应用程序使用的项目结构如下所示:
- src 文件夹包含我们的源文件(对于这个演示应用程序,它们都是 TypeScript)
- tmp 文件夹将包含转译后的 TypeScript -> JavaScript 文件
- dist 文件夹将包含最终的(可能是压缩过的)可分发单个文件 app.js,它将由 Index.html 引用。
还可以看到,NPM 的 `package.json` 和 Grunt 的 `gruntfile.js` 文件就 living 在这里。
Grunt 文件概览 (gruntfile.js)
这是完整的演示应用程序 Grunt `gruntfile.js` 文件。
module.exports = function (grunt) {
'use strict';
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-ts');
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-uglify');
//INSTRUCTIONS
//1. Install Node.js
//2. From Node.js command line run this command "npm install -g grunt"
//3. From Node.js command line run this command "npm install -g grunt-cli"
//3. From Node.js command line, Change to directory which has package.json in it,
// and run the following command lines
// a) "npm install"
// b) "grunt watch"
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// Cleans directories
clean: {
dist: [
'dist/**/*'
],
tmp: [
'tmp/**/*'
]
},
// Typescript transpiler (Typescript to JS)
ts: {
options: {
fast: 'never',
target: 'es5',
module: 'commonjs',
sourceMap: true
},
index: {
src: ['./src/**/*.ts'],
outDir: './tmp'
}
},
// Package
browserify: {
'./dist/app.js': ['./tmp/*.js', './src/Jquery/jquery.js']
},
// Minify
uglify: {
my_target: {
options: {
sourceMap: 'dist/<%= pkg.name %/>.map',
sourceMapRoot: '..',
sourceMappingURL: '/<%= pkg.name %/>.map'
},
files: {
'./dist/app.js': ['./dist/app.js']
}
}
},
// Watch
watch: {
scripts: {
files: ['./src/**/*.ts'],
tasks: ['dev'],
options: {
event: ['all'],
}
}
}
});
// setup default task to run dev task
grunt.registerTask('default', ['dev']);
// +++++++++++++++++++++++++++++++++++++++++++
// Prod task
// +++++++++++++++++++++++++++++++++++++++++++
// 1. Cleans directories
// 2. Runs typescript transpiling task
// 3. Package using Browserify
// 4. Minify using Uglify
grunt.registerTask('prod', [
'clean',
'ts:index',
'browserify',
'uglify'
]);
// +++++++++++++++++++++++++++++++++++++++++++
// Dev task
// +++++++++++++++++++++++++++++++++++++++++++
// 1. Cleans directories
// 2. Runs typescript transpiling task
// 3. Package using Browserify
grunt.registerTask('dev', [
'clean',
'ts:index',
'browserify'
]);
};
正如你所见,`gruntfile.js` 由许多任务组成,每个任务都有自己的部分。我们将在下面逐一进行探讨。
演示任务深入解析
本节将更详细地介绍演示应用程序的每个 Grunt 任务。
“default”任务
“default”任务是在包含 `gruntfile.js` 文件的目录中运行“grunt”命令行时被调用的。它简单地按顺序运行以下其他任务:
- clean
- ts:index
- browserify
稍后我们将详细讨论这些任务。
“dev”任务
“dev”任务是在包含 `gruntfile.js` 文件的目录中运行“grunt dev”命令行时被调用的。它 intended to be run when you are developing an app, as such no minification of the code is performed. It simply runs the following other tasks in order
- clean
- ts:index
- browserify
稍后我们将详细讨论这些任务。
“prod”任务
“prod”任务是在包含 `gruntfile.js` 文件的目录中运行“grunt prod”命令行时被调用的。它 intended to be run when you are about to deploy an app, as such minification of the code IS performed. It simply runs the following other tasks in order
- clean
- ts:index
- browserify
- Uglify
稍后我们将详细讨论这些任务。
“watch”任务
“watch”任务使用以下 NPM 包,其唯一职责是监视一组输入文件的更改,然后运行 x 个其他任务。
- grunt-contrib-watch
要启动对文件的监视,你必须发出以下 Grunt 命令行“grunt watch”。这是 Grunt `gruntfile.js` 的相关部分:
module.exports = function (grunt) {
'use strict';
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// Watch
watch: {
scripts: {
files: ['./src/**/*.ts'],
tasks: ['dev'],
options: {
event: ['all'],
},
},
},
});
};
可以看到,此任务监视 src 文件夹中的文件,并在这些文件的任何事件(例如添加、更改、删除、重命名)发生时运行“dev”任务。
“clean”任务
“clean”任务使用以下 NPM 包,其唯一职责是清理目录。
- grunt-contrib-clean
这是 Grunt `gruntfile.js` 的相关部分:
module.exports = function (grunt) {
'use strict';
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// Cleans directories
clean: {
dist: [
'dist/**/*'
],
tmp: [
'tmp/**/*'
]
},
});
};
可以看到,此任务清理了由 JSON 对象属性指定的 target 中的一组文件。如我所说,**每个** Grunt 任务都会有所不同,所以你**必须**查阅 Grunt 任务的 API 文档。
“ts”任务
正如我所说,演示应用程序使用了 TypeScript。现在 Visual Studio 原生就对 TypeScript 提供了极好的支持。你可以在下面的屏幕截图中看到这一点。
问题是我想尝试 Grunt,所以我决定使用一个 Grunt 任务来处理 TypeScript 的事情。这些 TypeScript 文件非常简单,但这就是我们要实现的:
- 使用 TypeScript
- 使用外部模块(对于演示应用程序来说是 jQuery)。要将 jQuery 用作外部模块,你需要获取它的外部 TypeScript 类型定义。幸运的是,有一个很棒的项目,用于将各种 JavaScript 库(由社区完成)公开 TypeScript 类型。你可以在这里查看: http://definitelytyped.org/
因此,一旦我们有了 TypeScript 并能够像这样使用 jQuery 作为外部模块:
/// <reference path="typings/jquery.d.ts" />
import Foo = require('./foo');
import $ = require('jquery');
$(document).ready(function () {
.....
.....
});
然后我们就可以看看实际的 Grunt 任务了。如我所说,Visual Studio 和 TypeScript 编译器 (tsc.exe) 可以完成你在 Grunt 任务中即将看到的所有事情,但我只是想测试我的 Grunt 技能,所以决定使用 Grunt 任务来完成 TypeScript 的编译。
“ts”任务使用以下 NPM 包,其唯一职责是负责将 TypeScript 转译为 JavaScript。
- grunt-ts
这是 Grunt `gruntfile.js` 的相关部分:
module.exports = function (grunt) {
'use strict';
grunt.loadNpmTasks('grunt-ts');
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// Typescript transpiler (Typescript to JS)
ts: {
options: {
fast: 'never',
target: 'es5',
module: 'commonjs',
sourceMap: true
},
index: {
src: ['./src/**/*.ts'],
outDir: './tmp'
}
},
});
};
可以看到,grunt-ts 模块(实际上是 Grunt 任务)允许我们指定与我们在 Visual Studio 的 TypeScript 选项卡中进行的操作完全相同的内容。例如,上面的 Grunt 任务执行以下操作:
- 告诉 TypeScript 目标是 ecmascript 5
- 告诉 TypeScript 使用 Common.js 风格的包(而不是 AMD)
- 告诉 TypeScript 创建源映射
- 告诉 TypeScript 使用哪个源文件进行转译
- 告诉 TypeScript 将输出文件放置在哪里
“browserify”任务
“browserify”任务使用以下 NPM 包,其唯一职责是负责将源文件打包成可分发包。
- grunt-browserify
这是 Grunt `gruntfile.js` 的相关部分:
module.exports = function (grunt) {
'use strict';
grunt.loadNpmTasks('grunt-browserify');
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// Package
browserify: {
'./dist/app.js': ['./tmp/*.js', './src/Jquery/jquery.js']
},
});
};
可以看到,此任务接收多个不同的源文件,并在 dist 文件夹中生成最终的输出文件。
“uglify”任务
“uglify”任务使用以下 NPM 包,其唯一职责是压缩源文件并生成一个单一的压缩输出文件。
- grunt-contrib-uglify
这是 Grunt `gruntfile.js` 的相关部分:
module.exports = function (grunt) {
'use strict';
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// Minify
uglify: {
my_target: {
options: {
sourceMap: 'dist/<%= pkg.name %>.map',
sourceMapRoot: '..',
sourceMappingURL: '<%= pkg.name %>.map'
},
files: {
'./dist/app.js': ['./dist/app.js']
}
}
},
});
}
<%= pkg.name %>
将被当前文件的名称替换。在这里就是 app。
以下是“uglify”任务完成后生成的 app.js 文件。你看它也被压缩了。
就这些
好了,这就是我这次想说的全部内容。一如既往,如果你喜欢你所看到的,你随时可以留下投票/评论,它们总是很受欢迎。