Использование Grunt

Noel Rice

Grunt - это инструмент JavaScript по запуску задач, который автоматизирует минимизацию скриптов, компиляцию TypeScript, работу с инструментарием, отвечающим за качество кода, CSS препроцессоры, и многие другие вещи, которые поддерживают разработку на клиентской стороне. Grunt полностью поддерживается Visual Studio, а шаблоны ASP.NET используют Gulp по умолчанию (см. Использование Gulp).

В этом примере используется шаблон Empty ASP.NET Core, чтобы вы увидели, как автоматизировать процесс сборки с нуля.

В законченном примере будет очищена целевая директория развертывания, будут скомбинированы JavaScript файлы, будет проверено качество кода, будет сжат контекст JavaScript файлов, и приложение будет развернуто. Мы станем использовать следующие пакеты:

  • grunt: Пакет Grunt для запуска задач.
  • grunt-contrib-clean: Плагин, который удаляет файлы или директории.
  • grunt-contrib-jshint: Плагин, который просматривает качество JavaScript кода.
  • grunt-contrib-concat: Плагин, который объединяет файлы в один.
  • grunt-contrib-uglify: Плагин, который минимизирует JavaScript.
  • grunt-contrib-watch: Плагин, с помощью которого можно просмотреть активность файлов.

Подготовка приложения

Для начала настройте новое empty веб приложение и добавьте файлы примера TypeScript. TypeScript автоматически компилируются в JavaScript с помощью настроек по умолчанию Visual Studio, и это будет наш стартовый материал для работы с Grunt.

  1. В Visual Studio создайте новое ASP.NET Web Application.
  2. В диалоговом окне New ASP.NET Project выберите шаблон ASP.NET Core Empty и нажмите кнопку OK.
  3. В Solution Explorer просмотрите структуру проекта. Папка \src включает в себя пустые wwwroot и Dependencies.
../_images/grunt-solution-explorer.png
  1. Добавьте новую папку TypeScript в директорию проекта.
  2. Перед добавлением файлов давайте убедимся, что в Visual Studio есть опция ‘compile on save’ для TypeScript файлов. Tools > Options > Text Editor > Typescript > Project
../_images/typescript-options.png
  1. Кликните правой клавишей мышки по директории TypeScript и выберите из контекстного меню Add > New Item. Выберите элемент JavaScript file и назовите файл Tastes.ts (расширение именно *.ts). Скопируйте в файл строку TypeScript (после сохранения появится новый файл Tastes.js с исходником JavaScript).
enum Tastes { Sweet, Sour, Salty, Bitter }
  1. Добавьте в директорию 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.

  1. В Solution Explorer кликните правой клавишей мышки по проекту и выберите из контекстного меню Add > New Item. Выберите элемент NPM configuration file, оставьте имя по умолчанию, package.json, и нажмите кнопку Add.
  2. В файле package.json внутри объектных фигурных скобок devDependencies введите “grunt”. Выберите grunt из списка Intellisense и нажмите клавишу Enter. Visual Studio выделит кавычками имя пакета grunt и добавьте двоеточие. Справа от двоеточия выберите последнюю стабильную версию пакета вверху списка Intellisense (нажмите Ctrl-Space, если Intellisense не появился).
../_images/devdependencies-grunt.png

Примечание

NPM использует семантическое версирование для организации зависимостей. Семантическое версирование, также известное как SemVer, определяет пакеты с помощью схемы <major>.<minor>.<patch>. Intellisense упрощает семантическое версирование, показывая только несколько подходящих вариантов. Самый верхний элемент в списке Intellisense (0.4.5 в примере выше) считается последней стабильной версией пакета. Символ ^ соответствует самым последним главным версиям, а знак ~ соответствует самым последним второстепенным версиям. См. NPM semver.

  1. Добавьте несколько зависимостей в пакеты grunt-contrib* для clean, jshint, concat, uglify и watch, как показано в примере ниже. Версии не обязательно должны соответствовать версиям в примере.
"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"
}
  1. Сохраните файл package.json.

Будут скачаны пакеты для каждого элемента devDependencies наряду с любыми файлами, которые требуются для каждого пакета. Вы можете найти файлы пакетов в директории node_modules, нажав на кнопку Show All Files в Solution Explorer.

../_images/node-modules.png

Примечание

При необходимости вы можете вручную восстановить зависимости в Solution Explorer, если кликните правой клавишей мышки по Dependencies\NPM и выберите опцию Restore Packages.

../_images/restore-packages.png

Настройка Grunt

Grunt использует Gruntfile.js, который определяет, загружает и регистрирует задачи, которые могут запускаться автоматически или вручную, основываясь на событиях Visual Studio.

  1. Кликните правой клавишей мышки по проекту и выберите Add > New Item. Выберите опцию Grunt Configuration file, оставьте имя по умолчанию, Gruntfile.js, и нажмите на кнопку Add.

Начальный код включает в себя определение модуля и метод grunt.initConfig(). initConfig() настраивает опции для каждого пакета, а в остальной части модуля загружаются и регистрируются задачи.

module.exports = function (grunt) {
        grunt.initConfig({
        });
};
  1. Внутри метода initConfig() добавьте опции для задачи clean, как показано в Gruntfile.js ниже. Задача clean принимает массив строк директории. Эта задача удаляет файлы из wwwroot/lib, а также убирает всю директорию /temp.
module.exports = function (grunt) {
        grunt.initConfig({
                clean: ["wwwroot/lib/*", "temp/"],
        });
};
  1. Дальше идет вызов grunt.loadNpmTasks(). Тогда задачу можно запустить из Visual Studio.
grunt.loadNpmTasks("grunt-contrib-clean");
  1. Сохраните Gruntfile.js. Файл должен выглядеть примерно так.
../_images/gruntfile-js-initial.png
  1. Кликните правой клавишей по Gruntfile.js и выберите из контекстного меню Task Runner Explorer. Откроется окно Task Runner Explorer.
../_images/task-runner-explorer-menu.png
  1. Убедитесь, что clean показана под Tasks в Task Runner Explorer.
../_images/task-runner-explorer-tasks.png
  1. Кликните правой клавишей мышки по задаче clean и выберите из контекстного меню Run. В командном окне отобразится процесс выполнения задачи.
../_images/task-runner-explorer-run-clean.png

Примечание

Больше не осталось файлов и директорий, которые нужно очистить. Кроме того, вы можете вручную создать их в Solution Explorer, а затем запустить задачу clean для тестирования.

  1. В методе initConfig() добавьте запись для concat.

В массиве свойств src перечисляются файлы, которые нужно скомбинировать, чтобы их можно было объединить. Свойство dest присваивает скомбинированным файлам путь.

concat: {
        all: {
                src: ['TypeScript/Tastes.js', 'TypeScript/Food.js'],
                dest: 'temp/combined.js'
        }
},

Примечание

Свойство all - это имя цели. Цели используются в нескольких Grunt задачах, чтобы можно было использовать несколько сред. Вы можете просмотреть встроенные цели с помощью Intellisense или присвоить собственные.

  1. Добавьте задачу jshint.

Задача jshint запускается для каждого JavaScript файла, найденного в директории temp.

jshint: {
        files: ['temp/*.js'],
        options: {
                '-W069': false,
        }
},

Примечание

Опция “-W069” - это ошибка, которая создается jshint, если JavaScript при присвоении свойства использует квадратные скобки вместо точечной нотации, например, Tastes["Sweet"] вместо Tastes.Sweet. Опция возвращает предупреждение, чтобы позволить процессу продолжить работу.

  1. Добавьте задачу uglify.

Задача минимизирует файл combined.js, найденный в директории temp, и создает результирующий файл в wwwroot/lib, следуя стандартному соглашению по именованию <file name>.min.js.

uglify: {
        all: {
                src: ['temp/combined.js'],
                dest: 'wwwroot/lib/combined.min.js'
        }
},
  1. После вызова grunt.loadNpmTasks(), который загружает grunt-contrib-clean, включите этот же вызов для jshint, concat и uglify.
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
  1. Сохраните Gruntfile.js. Файл должен выглядеть приблизительно так.
../_images/gruntfile-js-complete.png
  1. Обратите внимание, что список задач Task Runner Explorer включает в себя clean, concat, jshint и uglify. Запустите каждую задачу по порядку и просмотрите результат в Solution Explorer. Все задачи должны пройти без ошибок.
../_images/task-runner-explorer-run-each-task.png

Задача concat создает новый файл combined.js и размещает его в директорию temp. Задача jshint просто запускается, но не создает результат. Задача uglify создает новый файл combined.min.js и размещает его в wwwroot/lib. По завершению решение должно выглядеть так:

../_images/solution-explorer-after-all-tasks.png

Примечание

Зайдите на https://www.npmjs.com/ и введите имя пакета в поле поиска. Например, вы можете поискать пакет grunt-contrib-clean, чтобы получить ссылку на документацию, которая объясняет все его параметры.

Теперь все вместе

Используйте метод Grunt registerTask(), чтобы запустить серию задач в конкретной последовательности. Например, чтобы запустить представленные выше задачи в последовательности clean -> concat -> jshint -> uglify, добавьте в модуль данный код. Код должен быть добавлен на том же уровне, что и вызовы loadNpmTasks(), вне initConfig.

grunt.registerTask("all", ['clean', 'concat', 'jshint', 'uglify']);

Новая задача представлена в Task Runner Explorer под Alias Tasks. Вы можете кликнуть правой клавишей мышки и запустить ее, как и любую другую задачу. Задача all запустит по порядку clean, concat, jshint и uglify.

../_images/alias-tasks.png

Просмотр изменений

Задача watch следит за файлами и директориями. watch запускает задачи автоматически, если изменение найдено. Добавьте данный код в initConfig, чтобы просмотреть изменения в файлах *.js директории TypeScript. Если JavaScript файл меняется, watch запустит задачу all.

watch: {
        files: ["TypeScript/*.js"],
        tasks: ["all"]
}

Добавьте вызов loadNpmTasks(), чтобы просмотреть задачу watch в Task Runner Explorer.

grunt.loadNpmTasks('grunt-contrib-watch');

Кликните правой клавишей мышки по задаче watch в Task Runner Explorer и выберите из контекстного меню Run. В командном окне вы увидите сообщение “Waiting…”. Откройте один из файлов TypeScript, добавьте пробел, а затем сохраните файл. Это запустит задачу watch и по порядку другие задачи.

../_images/watch-running.png

Связывание с событиями Visual Studio

Вместо того чтобы постоянно вручную запускать задачи, вы можете связать задачи с событиями Before Build, After Build, Clean и Project Open.

Давайте привяжем watch, чтобы она запускалась каждый раз, когда открывается Visual Studio. В Task Runner Explorer кликните правой клавишей мышки по watch и выберите из контекстного меню Bindings > Project Open.

../_images/bindings-project-open.png

Перезагрузите проект. После этого задача watch начнет выполняться автоматически.

Резюме

Grunt - это мощный инструмент загрузки задач, который можно использовать для автоматизации большинства клиентских задач. Grunt использует NPM, чтобы получить пакеты для интеграции с Visual Studio. Visual Studio Task Runner Explorer определяет изменения в конфигурационных файлах и предлагает удобный интерфейс для запуска, просмотра и связывания задач с событиями Visual Studio.

Поделись хорошей новостью с друзьями!
Следи за новостями!