ASP.NET 5 Новый тип проекта и работа с Grunt

В этой статье я опишу новый проект веб-приложения ASP.NET 5, а также расскажу о настройке и использовании Grunt в нем.

Проект ASP.NET 5

Мы будем использовать Visual Studio 2015 Community Edition - она бесплатна для личного использования.

После ее установки и запуска, выберите “File”, “New”, “Project”, а затем в разделе “Templates”, “Visual C#”, “Web” выберите “ASP.NET Web Application”.

Создаем новый Asp.NET 5 проект

Далее выберите “Empty” шаблон в разделе “ASP.NET 5 Preview Templates”.

Выбираем Empty Template

После того, как студия закончит генерацию шаблона, вы получите пустой, готовый к работе, ASP.NET 5 проект.

Empty шаблон в Solution Explorer

Шаблон приложения ASP.NET 5 значительно отличается от предыдущих версий. Теперь solution разделен на две папки, первая “Solution Items”, вторая “src”. Папка src содержит сам проект, в моем случае GetHabitsAspNet5.

ASP.NET 5 проект содержит новую папку “wwwroot”. Назначение этой папки хранить весь статический контент сайта: html, javascript, css файлы, картинки и другие ресурсы, которые должны быть доступны конечным пользователям вашего сайта. Думайте о этой папке как о корне вашего веб-сайта. Доступ к контенту за пределами этой папки теперь запрещен и вам не нужно беспокоиться, что какие-то файлы из папки проекта, могут быть доступны пользователям (как это было раньше). Ну и, соответственно, не следует хранить в ней файлы с исходным кодом, Less файлы, файлы javascript, которые еще планируется объединять и минифицировать - любые не подготовленные к использованию ресурсы.

Вероятно, раньше для объединения и минификации javascript и css файлов вы использовали механизм бандлов из ASP.NET MVC или функции расширения WebEssentials, но теперь, с нативной поддержкой Grunt, мы можем всю подготовку frontend делать с помощью него.

Используем NPM для получения необходимых пакетов

Visual Studio 2015 “из коробки” поддерживает три пакетных менеджера: NuGet, NPM и Bower.

Пакетные менеджеры позволяют легко устанавливать в ваш проект нужные вам ресурсы.

Предыдущие версии Visual Studio поддерживали только NuGet и он использовался как для установки backend (различные .NET библиотеки), так и для frontend (JQuery, Boostrap и т.д.) пакетов. Теперь NuGet будет использоваться только для .NET библиотек. Вы указываете какие NuGet пакеты вам нужны в проекте в файле project.json, этот файл также содержит настройки проекта и в некотором роде заменяет файл web.config.

Проект ASP.NET 5 также поддерживает NPM пакеты. NPM - пакетный менеджер созданный для управления пакетами написанными для Node JS. Для управления пакетами NPM используется файл package.json.

И наконец, ASP.NET 5 поддерживает пакетный менеджер Bower. Bower - это пакетный менеджер созданный Twitter для поддержки frontend разработки. Вы можете использовать его для управления клиентскими ресурсами, такими как: AngularJS, JQuery, Bootstrap и т.д.

Для подготовки ресурсов к публикации на веб-сервер мы будем использовать Grunt. Grunt - это менеджер задач написанный на NodeJS, он имеет множество плагинов, которые упрощают pre-production подготовку frontend ресурсов. Для его установки и установки плагинов для него, мы будем использовать NPM.

Добавим в проект Grunt:

  1. Клик правой кнопкой мыши по заголовку проекта и выберите в контекстном меню “Add”, “New Item”.
  2. Выберите “NPM Configuration” файл в “DNX”, “Client-side”.
  3. Кликните кнопку “Add”.

Файл конфигурации NPM

Проделав эти шаги, вы добавите файл package.json в корень проекта.

Скорее всего, он будет выглядеть примерно вот так:

{
    "version": "1.0.0",
    "name": "ASP.NET",
    "private": true,
    "devDependencies": {
    }
} 

Для того, чтобы сказать NPM какие пакеты нам требуется установить, нужно добавить их в раздел “devDependencies”. Давайте добавим в него grunt и несколько плагинов к нему.

Исправьте ваш файл package.json, чтобы он выглядел вот так:

{
  "version": "1.0.0",
  "name": "ASP.NET",
  "private": true,
  "devDependencies": {
    "grunt": "~0.4.5",
    "grunt-contrib-uglify": "~0.9.1",
    "grunt-contrib-watch": "~0.6.1"
  }
}

Мы указали, что нам требуется три пакета: “grunt”, “grunt-contrib-uglify”, “grunt-contrib-watch”, после имени каждого пакета мы указали, версию пакета, которую хотели бы использовать. Обратите внимание, что после того как вы наберете название пакета и поставите двоеточие, студия будет подсказывать вам возможные варианты для выбора версии пакета.

После того как вы сохраните изменения в package.json, в папке “Dependencies” появится новая под директория “NPM” и студия начнет загрузку выбранных пакетов (физически они будут находится в папке “node-modules” в корне проекта - эта папка не отображается в Solution Explorer). Их можно будет увидеть, раскрыв папку “NPM”. Если загрузка не начнется автоматически, вы можете щелкнуть правой кнопкой мыши по ней и выбрать пункт “Restore Packages”.

Установленные NPM пакеты

Зачем нужен Bower

Вы можете использовать Bower для управления пакетами используемыми во frontend разработке. Например, вы можете использовать Bower для установки в ваш проект AngularJS, JQuery, Bootstrap и т.д.

Но я рекомендую использовать загруженные через Bower ресурсы только во время разработки (вы можете использовать не минифицированные, удобные для отладки исходники), а при публикации в production среду переключаться на какой-либо общедоступный CDN, например на Google CDN.

Преимущество использование общедоступных CDN заключается в том, что позволяет браузеру пользователя не загружать одни и те же библиотеки при каждом посещении нового сайта, а загрузить их один раз, а затем просто доставать из кэша, так как они находятся по одному и тому же адресу (естественно, это относится к популярным библиотекам и популярным CDN, так как так будет больше вероятность, что кто-то еще использует их на каком-то другом сайте).

Настроим Grunt для обработки Javascript файлов

Grunt отлично подходит для подготовки любых frontend ресурсов: компиляции Less и Saas файлов в CSS, сжатия изображений, склеивания и минификации Javascript файлов и т.д.

В этот посте мы настроим Grunt для автоматического склеивания и минификации Javascript файлов из Script директории (не забудьте создать ее в корне проекта) в один итоговый файл app.js в папке wwwroot.

Настроим Grunt:

  1. Клик правой кнопкой мыши по проекту и выберите “Add”, “New Item”.
  2. В “DNX”, “Client-side” выберите “Grunt Configuration file”.
  3. Нажмите кнопку Add.

Grunt Configuration file

После завершения этих шагов в проекте появится новый файл с именем Gruntfile.js - это файл конфигурации Grunt в проекте. Это обычный javascript файл.

Модифицируйте контент этого файла, чтобы он выглядел вот так:

module.exports = function (grunt) {
    //1 секция: загружаем плагины Grunt
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-watch');

    //2 секция: настраиваем плагины
    grunt.initConfig({
        uglify: {
            scripts: {
                files: { 'wwwroot/app.js': ['Scripts/app.js', 'Scripts/**/*.js'] }
            }
        },
        watch: {
            scripts: {
                files: ['Scripts/**/*.js'],
                tasks: ['uglify']
            }
        }
    });

    //3 секция: настроим запуск задач
    grunt.registerTask('default', ['uglify', 'watch']);
};

Давайте разберемся, что мы добавили в него.

Наш Gruntfile.js содержит три секции:

Первая секция указывает какие NPM плагины Grunt мы будем использовать. Мы указываем два плагина: “grunt-contrib-uglify” и “grunt-contrib-watch” (мы можем использовать их, так как предварительно загрузили, указав в блоке devDependencies файла package.json). Плагины, в контексте Grunt, называются Задачами, так как они производят некоторые действия, после того как Grunt из запускает.

Вторая секция производит настройку загруженных задач.

Uglify задача настроена так, что она будет склеивать и минифицировать все javascript файлы из директории Scripts и результирующий файл с названием app.js размещать в директории “wwwroot”.

Изучим более подробно его настройку:

uglify: {
    scripts: {
        files: { 'wwwroot/app.js': ['Scripts/app.js', 'Scripts/**/*.js'] }
    }
}

uglify: - название настраиваемой задачи, как правило, оно совпадает с названием npm пакета (grunt - в названии пакета grunt-contrib-uglify - это указание, что это плагин для Grunt, а contrib - то что пакет создан Grunt Team), но вы всегда можете уточнить эту информацию на странице пакета. Вот, например, страница Uglify: https://www.npmjs.com/package/grunt-contrib-uglify.

scripts: - именованный блок настроек для задачи, в контексте Grunt он называется Target (можно понимать как совокупность настроек выполнения задачи и файлов на которые она будет нацелена) - это может быть любое имя, какое вы захотите использовать. При конфигурировании мы можем указать несколько разных target, я выбрал имя “scripts”, потому что с помощью этого target мы будем склеивать и минифицировать javascript файлы, мы могли бы определить еще один target, который бы склеивал css файлы и назвать его, например, “css”.

uglify: {
    scripts: {
        files: { 'wwwroot/app.js': ['Scripts/app.js', 'Scripts/**/*.js'] }
    },
    css: {
        files: { 'wwwroot/style.css': ['Content/**/*.css'] }
    }
}

При запуске задачи мы можем запускать ее как для всех target, так и для какого-то определенного. В случае запуска для всех target задачи Uglify - сначала будет выполнен target “scripts” и склеены и минифицированы все javascript файлы, а затем target “css” и склеены и минифицированы все css файлы. В случае запуска для какого-то конкретного target - будет произведена обработка либо только javascript, либо только css файлов.

files: - блок, в котором мы указываем, к каким файлам будет применен target. Синтаксис который я использовал для задания списка файлов, называется “Files Object Format”, подробнее про него можно почитать здесь: http://gruntjs.com/configuring-tasks#files-object-format. В данном синтаксисе слева мы указываем путь до файла с результатами, а справа массив исходных файлов.

Существует несколько альтернативных способов задания списка используемых задачей файлов, прочитать о них всех можно здесь: http://gruntjs.com/configuring-tasks#files.

Также мы можем использоваться блок options, в котором указываются настройки для запуска задачи. Мы пока его не использовали, так как нас устраивают настройки по-умолчанию.

Вот так могла бы выглядеть настройка Uglify с использованием блока options:

grunt.initConfig({
  uglify: {
    options: {
      mangle: false
    },
    my_target: {
      files: {
        'dest/output.min.js': ['src/input.js']
      }
    }
  }
});

В данном случае options определяет настройки для всех target задачи, но он может и задаваться индивидуально, для каждого target:

grunt.initConfig({
  uglify: {
    my_target: {
      options: {
        mangle: false
      },
      files: {
        'dest/output.min.js': ['src/input.js']
      }
    }
  }
});

Блоки “target”, “file”, “options” - стандартные и, как правило, используются всеми задачами Grunt. Так что умение пользоваться ими здорово поможет при работе с Grunt.

Watch задача настроена так, что она отслеживает любые изменения в Javascript файлах в директории Scripts и при их изменении запускает Uglify:

watch: {
    scripts: {
        files: ['Scripts/**/*.js'],
        tasks: ['uglify']
    }
}

Третья секция: grunt.registerTask('default', ['uglify', 'watch']);, объявляет наши собственные задачи, в ней мы объявляем задачу ‘default’, которая будет последовательно запускать сначала ‘uglify’ - для минификации и склейки файлов, а затем ‘watch’ - для начала отслеживания изменений в файлах.

Для задания маски, по которой мы фильтруем javascript файлы (Scripts/**/*.js), мы использовали, так называемый, Globbing pattern.

** в нем задают любую вложенность директорий с любыми файлами, а * - любые символы в любом количестве, в названии файла.

Теперь давайте запустим настроенные задачи:

  1. Выберите Меню “View”, “Other Windows”, “Task Runner Explorer”, чтобы открыть окно менеджера задач Grunt.
  2. Для того, чтобы студия нашла какие задачи есть в проекте, кликните кнопку “Refresh”.
  3. Для запуска задачи кликните правой кнопкой мыши по задаче “default” и нажмите “Run” - вы только что запустили задачу “default”.

Запуск задачи default

В списке задач вы видите все доступные для запуска задачи, запуск задачи “uglify” запустит ее для всех ее target, вы также можете запустить и отдельный target: Кликните правой кнопкой мыши по “uglify:scripts” и выберите “Run” - вы запустили задачу для target “scripts”.

Теперь давайте назначим запуск задачи “default” на событие открытия проекта:

  1. Щелкните правой кнопкой мыши по задаче “default”, пункт меню “Bindings”.
  2. Выберите Open Project.

Теперь при каждом открытии проекта Task Runner будет запускать задачу “default”.

Как учиться работать с Grunt

Если вы никогда раньше не сталкивались с миром Node JS и не использовали Grunt, то может потребоваться некоторое время чтобы освоиться с ним.

Во-первых, прочтите Grunt Getting Started.

Во-вторых, изучите синтаксис конфигурирования Gruntfile.js.

Выбрать нужные вам плагины вы можете здесь: http://gruntjs.com/plugins - каталог содержит несколько тысяч плагинов, но в большинстве случаев вы будете использовать только несколько плагинов и они наверняка будут самыми первыми в списке.

Каждый плагин может иметь свои специфичные настройки и вам нет необходимости знать их наизусть, вы просто переходите на страницу плагина и читаете как он настраивается и смотрите примеры настройки, а затем настраиваете по аналогии.

Вот, например, страницы плагинов watch и uglify.

Также пример настройки Grunt можно прочитать в документации ASP.NET 5.

Ну и наконец статья на frontender.info описывающая настройку нескольких плагинов Grunt.

Заключение

В этой статье мы научились работать с NPM и устанавливать с помощью него Node JS пакеты, настраивать Grunt для автоматической склейки и минификации javascript файлов в ASP.NET 5 проекте, а также запускать Grunt задачи из Visual Studio.

Написано на основе поста Stephen Walther

Written on December 1, 2015