Grunt 是一种 JavaScript 任务运行程序,可自动执行脚本缩减、TypeScript 编译、代码质量“lint”工具、CSS 预处理器以及支持客户端开发所需的所有重复性工作。 Visual Studio 完全支持 Grunt。
此示例使用空的 ASP.NET Core 项目作为其起始点,演示如何从头开始自动执行客户端生成过程。
完成的示例可清理目标部署目录、合并 JavaScript 文件和检查代码质量,将浓缩后的 JavaScript 文件内容部署至 Web 应用程序的根目录。 我们将使用以下包:
grunt:Grunt 任务运行器包。
grunt-contrib-clean:一个用于删除文件或目录的插件。
grunt-contrib-jshint:一个用于检查 JavaScript 代码质量的插件。
grunt-contrib-concat:一个用于将多个文件合并为一个文件的插件。
grunt-contrib-uglify:一个用于压缩 JavaScript 以减小体积的插件。
grunt-contrib-watch:一个用于监听文件活动的插件。
准备应用程序
若要开始,请设置一个新的空 Web 应用程序并添加 TypeScript 示例文件。 TypeScript 文件采用默认的 Visual Studio 设置自动编译为 JavaScript,并且将是 Grunt 处理的原始材料。
在 Visual Studio 中,新建一个
ASP.NET Web Application。在“新建 ASP.NET 项目”对话框中,选择 ASP.NET Core“空”模板,然后单击“确定”按钮。
在“解决方案资源管理器”中,查看项目结构。
\src文件夹包含空的wwwroot和Dependencies节点。
将名为
TypeScript的新文件夹添加到项目目录中。在添加任何文件之前,请确保 Visual Studio 已为 TypeScript 文件勾选“在保存时编译”选项。 导航到“工具”>“选项”>“文本编辑器”>“Typescript”>“项目”:
右键单击
TypeScript目录,然后从上下文菜单中选择“添加”>“新建项”。 选择“JavaScript 文件”项,并将文件命名为 (注意 *.ts 扩展名)Tastes.ts。 将下面这行 TypeScript 代码复制到文件中,保存后,将显示一个新的Tastes.js文件,其中包含 JavaScript 源。enum Tastes { Sweet, Sour, Salty, Bitter }将第二个文件添加到 TypeScript 目录,并将其命名为
Food.ts。 将下面的代码复制到该文件中。class Food { constructor(name: string, calories: number) { this._name = name; this._calories = calories; } private _name: string; get Name() { return this._name; } private _calories: number; get Calories() { return this._calories; } private _taste: Tastes; get Taste(): Tastes { return this._taste } set Taste(value: Tastes) { this._taste = value; } }
配置 npm
接下来,将 npm 配置为下载 grunt 和 grunt-tasks。
在解决方案资源管理器中,右键单击项目,然后从上下文菜单中选择“ 添加新>项 ”。 选择 npm 配置文件 项,保留默认名称,
package.json然后单击“ 添加 ”按钮。在
package.json文件中的devDependencies对象大括号内输入“grunt”。 从 Intellisense 列表中选择grunt并按下 Enter 键。 Visual Studio 将引用 grunt 包名称,并添加一个冒号。 在冒号右侧,从 Intellisense 列表顶部选择包的最新稳定版本(如果 Intellisense 列表未显示,请按Ctrl-Space)。
注意
npm 使用 语义版本控制 来组织依赖项。 语义化版本控制(也称为 SemVer)使用编号方案 <major>.<minor>.<patch> 识别包。 Intellisense 只显示几个常见选项,从而简化语义化版本控制。 Intellisense 列表的第一项(在上面的示例中为 0.4.5)被视为程序包的最新稳定版本。 脱字号 (^) 匹配最新的主版本,波形符 (~) 匹配最新的次版本。 参阅 npm semver 版本解析器参考,以了解 SemVer 提供的完整表达能力。
添加更多依赖项以加载 clean、jshint、concat、uglify 和 watch 的 grunt-contrib-* 包,如以下示例中所示。 版本不需要与示例匹配。
"devDependencies": { "grunt": "0.4.5", "grunt-contrib-clean": "0.6.0", "grunt-contrib-jshint": "0.11.0", "grunt-contrib-concat": "0.5.1", "grunt-contrib-uglify": "0.8.0", "grunt-contrib-watch": "0.6.1" }保存
package.json文件。
每个 devDependencies 项的包将随每个包需要的所有文件一起下载。 在解决方案资源管理器中启用“显示所有文件”按钮,可以找到 node_modules 目录中的包文件。
注意
若有需要,可以在解决方案资源管理器中手动还原依赖项,方法是右键单击 并选择“还原包”菜单选项Dependencies\npm。
配置 Grunt
使用名为 Gruntfile.js 的清单配置 Grunt,该清单定义、加载和注册了多个任务,这些任务可手动运行或配置为基于 Visual Studio 中的事件自动运行。
右键单击项目并选择“添加”>“新项目”。 选择“JavaScript 文件”项模板,将名称更改为 ,然后单击“添加”按钮
Gruntfile.js。将以下代码添加到
Gruntfile.js。initConfig函数设置每个包的选项,模块的其余部分加载并注册任务。module.exports = function (grunt) { grunt.initConfig({ }); };在
initConfig函数中,添加clean任务的选项,如下面的示例Gruntfile.js中所示。clean任务接受目录字符串的数组。 此任务从 wwwroot/lib 删除文件,并删除整个 /temp 目录。module.exports = function (grunt) { grunt.initConfig({ clean: ["wwwroot/lib/*", "temp/"], }); };在
initConfig函数下,添加对grunt.loadNpmTasks的调用。 这使得任务可从 Visual Studio 中运行。grunt.loadNpmTasks("grunt-contrib-clean");保存
Gruntfile.js。 该文件应类似于下面的屏幕截图所示。
右键单击
Gruntfile.js并从上下文菜单中选择“任务运行程序资源管理器”。 随即打开任务运行程序浏览器窗口。
验证任务运行程序资源管理器中的“任务”下是否显示
clean。
右键单击清理任务并在上下文菜单中选择“运行”。 显示任务进度的命令窗口。
注意
尚无需要清理的文件或目录。 如果需要,可以在解决方案资源管理器中手动创建文件或目录,然后运行清理任务来进行测试。
在
initConfig函数中,使用下面的代码添加concat的条目。src属性数组按合并顺序列出要合并的文件。dest属性指定生成的合并文件的路径。concat: { all: { src: ['TypeScript/Tastes.js', 'TypeScript/Food.js'], dest: 'temp/combined.js' } },注意
以上代码中的
all属性是目标的名称。 某些 Grunt 任务会使用目标以允许多个生成环境。 可以使用 IntelliSense 查看内置目标,或自行分配。使用以下代码添加
jshint任务。系统会对在 temp 目录中找到的每个 JavaScript 文件运行 jshint
code-quality实用工具。jshint: { files: ['temp/*.js'], options: { '-W069': false, } },注意
选项 “-W069” 是 jshint 报告的错误,当 JavaScript 使用括号语法(obj["prop"])而不是点语法(即
Tastes["Sweet"]而非Tastes.Sweet)赋值属性时触发。 该选项关闭警告,以允许进程的其他部分继续。使用以下代码添加
uglify任务。该任务将临时目录中找到
combined.js文件最小化,并按照标准命名约定在 wwwroot/lib 中创建结果文件,<文件名>.min.js。uglify: { all: { src: ['temp/combined.js'], dest: 'wwwroot/lib/combined.min.js' } },在对
grunt.loadNpmTasks发起的加载grunt-contrib-clean的调用下,使用以下代码包括 jshint、concat 和 uglify 的相同调用。grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-uglify');保存
Gruntfile.js。 该文件应类似于以下示例所示。
请注意,任务运行程序资源管理器的“任务”列表包括 、
clean、concat和jshint任务uglify。 按顺序运行每个任务,并在解决方案资源管理器中观察结果。 每个任务都应正常运行,不会出错。
concat 任务会创建一个新的
combined.js文件,并将其放入 temp 目录。jshint任务仅运行,并不生成输出。uglify任务创建新的combined.min.js文件,并将其放入 wwwroot/lib 中。 完成后,解决方案应类似于下面的屏幕截图所示:
注意
若要详细了解每个包的选项,请访问 https://www.npmjs.com/ 并在主页上的搜索框中搜索包名称。 例如,可以查找 grunt-contrib-clean 包,以获取介绍其所有参数的文档链接。
综述
使用 Grunt registerTask() 方法,按特定顺序运行一系列任务。 例如,若要按 clean -> concat -> jshint -> uglify 的顺序中运行上述示例步骤,请将以下代码添加到模块。 请将代码添加到与 loadNpmTasks() 调用相同的级别,在 initConfig 之外。
grunt.registerTask("all", ['clean', 'concat', 'jshint', 'uglify']);
在任务运行程序资源管理器中,新任务显示在“别名任务”下。 可以右键单击并运行它,就像执行其他任务一样。
all 任务将按顺序运行 clean、concat、jshint 和 uglify。
监视更改
watch 任务用于监视文件和目录。 如果检测到更改,监视会自动触发任务。 将下面的代码添加到 initConfig,以监视对 TypeScript 目录中的 *.js 文件的更改。 如果 JavaScript 文件发生了更改,watch 将运行 all 任务。
watch: {
files: ["TypeScript/*.js"],
tasks: ["all"]
}
添加对 loadNpmTasks() 的调用,以在任务运行程序资源管理器中显示 watch 任务。
grunt.loadNpmTasks('grunt-contrib-watch');
右键单击任务运行程序资源管理器中的监视任务,然后从上下文菜单中选择“运行”。 显示正在运行的监视任务的命令窗口将显示一条“正在等待…”消息。 打开其中一个 TypeScript 文件,添加一个空格,然后保存该文件。 这会触发监视任务,并触发要按顺序运行的其他任务。 下面的屏幕截图展示的是一个示例运行。
绑定到 Visual Studio 事件
请将任务绑定到“生成前”、“生成后”、“清理”和“项目打开”事件,除非想在每次在 Visual Studio 中工作时都手动启动任务。
绑定 watch,使其在每次打开 Visual Studio 时运行。 在 Task Runner Explorer 中,右键单击 watch 任务并从上下文菜单中选择“捆绑项”>“项目打开”。
卸载并重载项目。 再次加载项目时,监视任务会自动开始运行。
总结
Grunt 是一个功能强大的任务运行程序,可用于自动执行大多数客户端生成任务。 Grunt 通过 npm 来分发其包,并提供与 Visual Studio 的工具集成功能。 Visual Studio 的任务运行程序资源管理器检测对配置文件所做的更改,并提供便捷的界面,用于运行任务、查看运行中的任务以及将任务绑定到 Visual Studio 事件。