Статьи

Интеграция процесса сборки JS в MSBuild в Visual Studio 2012 Express

Я работаю с ASP и ASP.NET уже около десяти лет, начиная с ASP classic и остановившись на .NET 2.0 в качестве своего любимого. В этом году (в 2013 году) я решил обновить свою работу .NET до .NET 4.0 с помощью Visual Studio 2012 Express и по-настоящему освоить MSBuild, чтобы я мог объединять и минимизировать свои файлы JavaScript в рамках обычного процесса сборки. проекта .NET, в Visual Studio.

Моя первая любовь — использовать Ant в NetBeans с платформой PHP или JSP для такой работы, но основной веб-сайт моей компании работает на платформе .NET, и пришло время его обновить, поэтому я решил откусить пулю и погрузиться обратно в к серьезному изучению создания полностью интегрированного процесса сборки с использованием MSBuild.

Из этого туториала Вы узнаете, как отредактировать файл проекта Visual Studio 2012 Express, чтобы включить собственный отдельный файл сборки, который будет выполнять широко знакомый теперь процесс объединения и минимизации набора модулей JavaScript в один файл, готовый к развертыванию.


Я хотел, чтобы нетривиальный проект продемонстрировал этот процесс, потому что я обнаружил, что дьявол кроется в деталях. Я часто добросовестно следовал слишком простому учебнику или введению в незнакомую тему, а затем обнаружил, что учебник не подготовил меня к тому, когда вы хотите сделать что-то даже немного сложное. Итак, в этом уроке мы попытаемся объединить Knockout.js и jQuery UI. Мы также будем использовать файл JSON с иерархией данных для определения меню. Мы будем использовать шаблон Knockout.js с привязкой foreach которая перебирает данные JSON для создания иерархии элементов ul выступающей в качестве разметки HTML для строки меню, совместимой с пользовательским интерфейсом jQuery .

К сожалению, строка меню еще не доступна (версии 1.9 или 1.10) в комплекте с пользовательским интерфейсом jQuery, поэтому вам необходимо загрузить файлы меню из ветви Menubar пользовательского интерфейса jQuery. Вам также понадобится YUI Compressor для минимизации ваших исходных файлов JavaScript. Для этого руководства вам понадобится Visual Studio 2012 Express для Web . Вам также необходимо скачать:

Если вы не привыкли к JSON, стоит посетить веб-сайт JSON .


Если вы читаете мой последний учебник Использование Ant для создания библиотеки JavaScript , вы можете удивиться, почему этот учебник не о NAnt. Итак, с моей новой блестящей установкой Visual Studio 2012 Express я хотел бы попытаться объединить мою разработку под одной крышей. На протяжении многих лет моей любимой IDE для разработки на C # Assembly был SharpDevelop . Они перешли несколько лет назад из NAnt в MSBuild для SharpDevelop версии три. Настало время для меня последовать их примеру.

Мы больше не используем NAnt в процессе сборки, мы полностью перешли на MSBuild / CruiseControl.NET. И мы не рассматриваем возможность зависеть от доминирующей операционной системы как шаг назад: это помогает уменьшить количество движущихся частей, различные конфигурации, различные пользовательские настройки.

#D 3.0 — Отказ от поддержки NAnt: почему?

В течение многих лет для разработки .NET я работал с тремя разными IDE одновременно:

  1. Sharp Разработка для моей сборки на C #, но я также подстроил конкатенацию JavaScript и CSS и минимизировал процесс сборки в этой среде с помощью специально установленной копии NAnt.
  2. Visual Studio (2005 и т. Д.) Для главных страниц, страниц контента.
  3. Внешний редактор, такой как Aptana, для управления разработкой JavaScript.

Использование трех IDE, как это было утомительно (и удивительно обременительно для моего процессора и оперативной памяти), поэтому еще одно новогоднее решение — собрать все вместе в Visual Studio. Отсюда необходимость понять, как интегрировать мой процесс сборки JavaScript в общую сборку проекта.

Одним из основных преимуществ MSBuild для меня (на платформах Windows) является то, что он входит в состав самого .NET. Это означает, что на любой машине с Windows, на которой установлено обновление Windows, будет доступен MSBuild.

Сравнение NAnt и MSBuild на StackOverflow.

Откройте новый проект в Visual Studio 2012 Express. Я назвал его NetTutsMSBuildJs и создал его в своей папке NetTuts здесь: C:\NetTuts\MSBuildJs .

Диалоговое окно «Новый проект»

Как вы можете видеть на скриншоте, я создал несколько папок следующим образом:

js_folders
скоросшиватель содержание
CSS Производственные версии CSS-файлов jQuery UI. Для этого урока мы используем тему плавности.
отлаживать Различные версии страницы веб-формы Default.aspx для целей отладки.
отлаживать-JS Три папки: concat , min и src .
JS Производственные версии JQuery, JQuery UI и Knockout.
jsbuild Файл сборки XML со всеми задачами, необходимыми для сборки JavaScript, и копия компрессора YUI.
JSON Ключевой JSON-файл menubar-data.json котором menubar-data.json данные, необходимые для построения меню. Также файлы JSON используются для заполнения страницы в соответствии с выбором меню пользователя.

Обратите внимание, что некоторые папки неактивны. Это потому, что я исключил их из проекта. Вы можете переключить эту настройку из контекстного меню:

exclude_from_project

В процессе сборки легко удалять и создавать каталоги, но нет способа программно включать или исключать элементы из проекта. Папки concat и min в debug-js являются одноразовыми и автоматически генерируются процессом сборки из всего, что вы создали в папке src , поэтому целесообразно исключить их из проекта. Обратите внимание, что вы не можете исключить папку debug из проекта, поскольку она содержит страницы веб-форм .NET, содержащие файлы с выделенным кодом. Если вы исключите эту папку, страницы веб-форм выдают ошибки, говорящие о том, что классы, определенные в файлах code-behind, не могут быть найдены.

show_all_files

Вы можете включить или отключить отображение этих исключенных объектов, перейдя к значку Показать все файлы в верхней части обозревателя решений и нажав. Я всегда хочу видеть их.

Для этого проекта нам нужен еще один ключевой элемент конфигурации. IIS и встроенный IIS Express по умолчанию не включают MIME-тип JSON, и мы будем широко использовать файлы JSON для доставки содержимого, поэтому мы должны добавить его в файл Web.config . В элемент configuration добавьте элемент system.webServer например так:

1
2
3
4
5
<system.webServer>
    <staticContent>
        <mimeMap fileExtension=».json» mimeType=»application/json» />
    </staticContent>
</system.webServer>

Основное внимание в этом руководстве уделяется тому, как построить проект JavaScript в рамках проекта .NET, но мы не можем идти дальше, пока у нас не будет чего-то строить, поэтому теперь позвольте мне объяснить слегка амбициозный проект, который я имею в виду.

Вот диаграмма компонентов UML, показывающая все части, которые понадобятся проекту. Обратите внимание, что это комплексная диаграмма компонентов с точки зрения разработчика, показывающая все виды одноразовых артефактов, которые важны, например, только для отладки. Это не компонентная диаграмма только ключевых артефактов, необходимых для целевой системы.

Диаграмма компонентов

Диаграмма компонентов определяет состав компонентов и артефактов в системе.
IBM: диаграммы компонентов

В UML 2.0 «компонент» используется для описания более абстрактной идеи: автономные инкапсулированные блоки; «Артефакт» используется для описания того, что я показываю на этой диаграмме: файлы и библиотеки. Это идеальная диаграмма, показывающая, как различные файлы зависят друг от друга. Например, все страницы веб-форм зависят от главной главной страницы. Файл js.build не будет работать, если отсутствует файл jar компрессора. К js.build файл проекта и файл js.build взаимозависимы. Если файл js.build отсутствует, проект не будет загружен; js.build не может запускаться один, определенные там задачи запускаются событием AfterBuild в общей сборке проекта.

В этом уроке я хочу отобразить горизонтальную строку меню с помощью ветви меню JQuery UI. Для этого у меня есть JSON-файл с иерархическими данными для меню и шаблон Knockout.js, проходящий по этим данным для визуализации
HTML-разметка, необходимая для jQuery menubar. Я добавил функцию обратного вызова renderMenu которая afterRender событием afterRender в шаблоне Knockout. renderMenu просто вызывает renderMenu чтобы наконец отобразить menubar со всеми замечательными блестящими функциями пользовательского интерфейса jQuery.


Загрузите полный комплект из пользовательского интерфейса jQuery, включая тему по вашему выбору. Разархивировав загрузку, просмотрите папку css где вы найдете папку с названием выбранной вами темы. В моем случае я выбрал гладкость. Откройте эту папку, и вы увидите нужные вам файлы:

jquery_ui_css

Скопируйте всю папку темы (плавность) и вставьте ее в папку css в проекте. Вернитесь в Visual Studio, щелкните значок обновления в верхней части обозревателя решений, и папка плавности должна появиться в папке css . Вы должны также включить папку в проект.

В дополнение к пользовательскому интерфейсу jQuery и определенной теме вам также понадобится небольшой CSS-файл специально для меню. После загрузки проекта jquery.ui.menubar.css из github jquery.ui.menubar.css файл jquery.ui.menubar.css следующему пути: \jquery-ui-menubar\themes\base\jquery.ui.menubar.css . Скопируйте это в папку css вашего проекта.

Загрузите последние версии производственных выпусков jQuery, jQuery UI и Knockout. Я использую 1.8.2 для jQuery, 1.9.2 для jQuery UI и 2.1.0 для Knockout. Скопируйте их в папку js в вашем проекте.

Вам также потребуется последняя jquery.ui.menubar.js версия jquery.ui.menubar.js , загруженная из ветви jquery.ui.menubar.js проекта jQuery UI. Скопируйте это в папку debug-js\src в вашем проекте.

Мы создаем несколько версий одной и той же страницы, чтобы помочь отладить и протестировать наш JavaScript. Главная страница, конечно, может помочь предотвратить дублирование кода. Назовите эту главную страницу Main.Master .

add_new_master_page

Оставьте элемент title пустым (мы определим заголовок для каждой страницы, использующей этот мастер) и дадим ссылку на все таблицы стилей, которые нам нужны для пользовательского интерфейса jQuery и меню:

1
2
3
4
<title></title>
<link rel=»stylesheet» type=»text/css» href=»/css/smoothness/jquery-ui-1.9.2.custom.css»>
<link rel=»stylesheet» type=»text/css» href=»/css/smoothness/jquery-ui-1.9.2.custom.min.css»>
<link rel=»stylesheet» type=»text/css» href=»/css/jquery.ui.menubar.css»>

Добавьте ContentPlaceHolder непосредственно перед концом тела, где каждая страница будет ссылаться на соответствующие файлы JavaScript

1
<asp:ContentPlaceHolder ID=»JsScripts» runat=»server»/>

Вот объект JSON, определяющий строку меню, которую мы могли бы использовать для веб-сайта английских инструкторов. Создайте файл JSON с именем menubar-data.json в папке json и заполните его следующим JSON.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
{
    «nodes»:[{
        «text»: «For Students»,
        «nodes»: [
 
        {
            «text»: «Listening Practice»,
            «url»:»listening-practice.json»
        },
        {
            «text»: «Grammar»,
            «url»:»grammar.json»,
            «nodes»: [
 
            {
                «text»: «Verb Forms»,
                «url»:»verb-forms.json»,
                «nodes»: [
 
                {
                    «text»: «Verb Tense and Aspect»,
                    «url»:»verb-tense-and-aspect.json»
                },
                {
                    «text»: «Modal Auxiliary Verbs»,
                    «url»:»modal-auxiliary-verbs.json»
                }
                ]
            },
            {
                «text»: «Verb Patterns»,
                «url»:»verb-patterns.json»
            },
            {
                «text»: «Noun phrases»,
                «url»:»noun-phrases.json»
            },
            {
                «text»: «Complex sentences»,
                «url»:»complex-sentences.json»
            }
            ]
        }
        ]
    },
    {
        «text»: «For Teachers»,
        «nodes»: [
        {
            «text»: «Teaching Materials»,
            «url»:»teaching-materials.json»
        },
        {
            «text»: «Tests and evaluation grids»,
            «url»:»tests-and-evaluation.json»
        },
        {
            «text»: «Media»,
            «url»:»media.json»
        }
        ]
    }
    ]
}

Узлы верхнего уровня не имеют определенного свойства URL, поэтому при щелчке они будут отображать только элементы подменю. Подменю содержат узлы с определенным свойством URL. Если щелкнуть один из этих узлов, система извлечет данные JSON из файла по этому URL-адресу.

Каждый связанный с JSON-файл в строке меню содержит некоторый контент в простой структуре, определяющей заголовок и некоторый текст:

1
2
3
4
{
    «header»: «Grammar»,
    «text»: «A series of exercises helping you to improve your grammar.»
}

Мы определяем это в Main.Master . Не существует очевидного способа минимизации или улучшения его для развертывания, поэтому я хочу повторно использовать его с каждой версией страниц, которые ссылаются на главную страницу.

Я хотел иметь только один шаблон Knockout для рендеринга HTML-разметки (набор вложенных элементов ul ) для меню, но неудивительно, что событие afterRender связанное с привязкой foreach запускается с каждым циклом, а не в конце всего процесса рендеринга. , Итак, мне нужно было создать observableArray только с одним элементом ul , связать его с шаблоном Menu, который отображает самый внешний элемент ul , и вложить в него шаблон menubar. Затем я могу обработать это единственное событие foreach с помощью моей функции renderMenu , которая вызывает конструктор меню jQuery и отображает меню во всей своей красе. Я получил много помощи по этой теме: nested-templates-with-knockoutjs-and-mvc-3-0 .

Вот шаблон меню:

1
2
3
<script type=»text/html» id=»MenuTemplate»>
    <ul class=»ui-widget-header» id=»menu» data-bind=»template: { name: ‘MenuNodeTemplate’, foreach: $data.root.nodes}»></ul>
</script>

И вот шаблон узла для каждого узла меню:

1
2
3
4
5
6
7
8
<script id=»MenuNodeTemplate» type=»text/html»>
    <li data-bind=»addData: $data.url»>
        <a data-bind=»attr: {href: (‘#’ + $data.url)}»><span data-bind=»text: $data.text»>
        <!— ko if: $data.nodes —>
        <ul data-bind=»template: { name: ‘MenuNodeTemplate’, foreach: $data.nodes}»></ul>
        <!— /ko —>
    </li>
</script>

Затем вам нужен элемент div который вы привязываете к MenuTemplate:

1
<div data-bind=»template: {name: ‘MenuTemplate’ , foreach: masters, afterRender: renderMenu}»></div>

Обратите внимание, что шаблон узла использует синтаксис потока управления без контейнеров , основанный на тегах комментариев. Здесь происходит несколько вещей, поэтому позвольте мне объяснить

В полностью визуализированной строке меню jQuery я хочу прикрепить обработчик к событию select . Обработчик имеет event, ui подписи event, ui . Когда вы щелкаете элемент меню, обработчику передается объект события и объект jQuery, представляющий элемент. Чтобы получить текст из объекта пользовательского ui , мы можем вызвать метод text ( ui.item.text() ). Но как мы получаем свойство url из базового JSON? Это немного сложнее, и я объясню это позже, когда мы рассмотрим функцию выбора, запускаемую событием click для каждого элемента подменю, и пользовательскую привязку addData прикрепленную к элементу li в шаблоне Knockout.

Наконец, вам просто нужен элемент div где мы можем отображать содержимое, извлеченное из файлов данных JSON:

1
2
3
4
<div id=»show-result» class=»ui-widget»>
    <h1 data-bind=»text: header» class=»ui-widget-header ui-corner-all»></h1>
    <div data-bind=»html: text» class=»ui-widget-content ui-corner-all»></div>
</div>

Создайте веб-форму с помощью Default-src.aspx страницы в папке отладки с именем Default-src.aspx .

add_page_using_master_page

Это оказывается к счастью короткий файл. Это одно из больших преимуществ подхода .NET к мастер-страницам. На главной странице есть только два ContentPlaceHolders. Добавьте ссылки на ваши файлы JavaScript следующим образом на элемент Content, связанный с JsScripts ContentPlaceHolder:

01
02
03
04
05
06
07
08
09
10
<%@ Page Title=»Default src» Language=»C#» MasterPageFile=»~/Main.Master» AutoEventWireup=»true» CodeBehind=»Default-src.aspx.cs» Inherits=»NetTutsMsBuildJs.debug.Default_src» %>
<asp:Content ID=»Content1″ ContentPlaceHolderID=»head» runat=»server»>
</asp:Content>
<asp:Content ID=»Content2″ ContentPlaceHolderID=»JsScripts» runat=»server»>
    <script src=»/js/jquery-1.8.2.min.js»></script>
    <script src=»/js/jquery-ui-1.9.2.custom.min.js»></script>
    <script src=»/debug-js/src/jquery.ui.menubar.js»></script>
    <script src=»/js/knockout-2.1.0.js»></script>
    <script src=»/debug-js/src/default-src.js»></script>
</asp:Content>

Создайте новый файл JavaScript с именем default-src.js в debug-js\src folder .

Все в вызове мы заключаем в обычную функцию jQuery $ которая гарантирует, что страница полностью загружена, прежде чем что-либо запускать.

1
2
3
$(function () {
 
});

Начиная с jQuery 1.4, если файл JSON содержит синтаксическую ошибку, запрос обычно завершается с ошибкой. Смотрите: jQuery.getJSON () .

Нам нужны три основных элемента функциональности здесь:

  1. Вызов метода jQuery getJSON для получения данных JSON для строки меню. Если это удается, мы создаем модель представления Knockout и вызываем ko.applyBindings(viewModel) чтобы активировать ее.
  2. Функция renderMenu которая будет вызываться событием afterRender объекта MenuTemplate. Эта функция вызывает конструктор menubar для отображения меню.
  3. Функция select которая вызывается, когда пользователь щелкает элемент меню. Эта функция извлекает данные JSON из соответствующего файла содержимого и отображает их на странице.

Обратите внимание, что функция select должна иметь возможность извлекать URL из базовых данных JSON. Это самая сложная часть объединения функциональности меню jQuery с шаблоном Knockout. JQuery позволяет добавлять данные и извлекать данные из элемента HTML. Чтобы добавить данные из нашего шаблона Knockout, нам нужно использовать пользовательскую привязку, которая имеет доступ к элементу HTML, с которым она связана. Связывание, которое я создал, называется addData и просто присоединяется к ko.bindingHandlers обычным способом Knockout с помощью метода init метода update .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
ko.bindingHandlers.addData = {
    init: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        if (value) {
            $.data(element, «url», value);
        }
    },
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        if (value) {
            $.data(element, «url», value);
        }
    }
};

Возможно, шаблон узла теперь имеет больше смысла. Объект jQuery, переданный как пользовательский интерфейс в обработчике select представляет верхний элемент li каждого элемента меню, поэтому мы добавляем пользовательскую привязку к этому элементу списка: data-bind="addData: $data.url" . Теперь, когда к каждому элементу прикреплены некоторые данные, мы можем извлечь его из обработчика select с помощью следующего синтаксиса: ui.item.data("url") , используя метод data jQuery.

Элемент link более прост и использует только стандартные attr и text :

1
2
3
<a data-bind=»attr: {href: (‘#’ + $data.url)}»>
    <span data-bind=»text: $data.text»>
</a>

Просто отметьте, что я поставил перед href символ хеша. Таким образом, когда вы нажимаете на элемент меню, вы не переходите по ссылке на другую страницу. Вместо этого происходит событие select и обработчик, извините, обрабатывает его.

Вот полная функция выбора, использующая этот подход для извлечения данных из объекта jQuery, представляющего элемент, представленный Knockout:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
function select(event, ui) {
    var url = «/json/» + ui.item.data(«url»);
    $.getJSON(url, function (data) {
        viewModel.header(data.header);
        viewModel.text(data.text);
    })
    .error(function (errorData) {
        viewModel.header(«Error»);
        if (errorData.status === 404) {
            viewModel.text(«Could not find » + ui.item.text() + » at » + url);
        } else {
            viewModel.text(«There has been an error, probably a JSON syntax error. Check the JSON syntax in the file <code>» + url + «</code>»);
            console.log(errorData);
        }
    });
}

Я добавил дополнительную ловушку ошибок, потому что jQuery теперь хранит молчание об ошибках синтаксиса JSON. Я не хочу, чтобы пользователь был обременен деталями ошибок синтаксиса JSON, но я хочу дать некоторое представление о том, что могло пойти не так.

Вот модель представления Knockout, определенная в функции, связанной с методом getJSON() :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$.getJSON(‘/json/menubar-data.json’, function (data) {
    viewModel = {
        header: ko.observable(),
        text: ko.observable(),
        masters: ko.observableArray([
        {
            name: «Master1»,
            root: data
        }
        ]),
        renderMenu: function () {
            $(«#menu»).menubar({
                autoExpand: true,
                menuIcon: true,
                buttons: true,
                select: select
            });
        }
    };
    ko.applyBindings(viewModel);
    viewModel.header(«Welcome»);
    viewModel.text(«The English Resource Page»);
 
})
.error(function (errorData) {
    console.log({ «errorData»: errorData });
    console.log(errorData.error());
});

Default-src.aspx в окне IDE, щелкните запустить (зеленая стрелка под меню IDE) в режиме отладки.

отлаживать

После процесса Default-src.aspx в вашем браузере должен появиться Default-src.aspx . В среде IDE в фоновом режиме выполняется экспресс-версия веб-сервера IIS. В моем случае проект использует порт 54713 на локальном хосте для запуска страницы:
http://localhost:54713/debug/Default-src.aspx

По умолчанию-Src

Теперь мы готовы работать над процессом сборки JavaScript.


Этот проект автоматизирует два ключевых шага, которые нам нужны для создания сложного проекта JavaScript:

  • Объединить: собрать все исходные файлы, необходимые для конкретной страницы, и объединить их в один файл. MSBuild не имеет встроенной задачи Concat, такой как Ant или NAnt, поэтому нам придется развернуть свою собственную на основе этого превосходного блога. Практическое руководство. Объединение файлов с помощью задач MSBuild .
  • Minify: Минимизируйте наши собственные исходные файлы и объедините их с файлами производственного выпуска, такими как файл jQuery, в один сжатый файл.

Папка, в которой вы создали свой проект .NET, будет содержать файлы, которые выглядят так:

project_file

Файл NetTutsMSBuildJs.csproj — это просто файл XML, специально настроенный для обработки процесса MSBuild для этого проекта. Вполне законно создать один из них вручную или отредактировать его в соответствии с вашим проектом. Очевидно, что для чисто .NET целей гораздо лучше использовать графический интерфейс Visual Studio для автоматической настройки этого файла, но цель этого руководства — показать, как добавить сборку JavaScript, которая не является частью стандарта. NET сборка.

В Visual Studio вы не можете редактировать этот файл проекта, если не выгрузите проект, и вы не можете загрузить проект, если в файле есть синтаксическая ошибка! Поэтому попробуйте выгрузить и загрузить проект, чтобы вы могли редактировать этот ключевой файл. Чтобы выгрузить проект, щелкните его правой кнопкой мыши и выберите пункт « Выгрузить проект» .

unload_project

После выгрузки проекта все папки и файлы исчезают, и у вас остаются только решения и проекты в обозревателе решений. Щелкните правой кнопкой мыши по проекту, и на этот раз контекстное меню очень короткое. Выберите Edit NetTutsMSBuildJs.csproj, и откроется файл конфигурации проекта.

edit_proj_file

Теперь, просто чтобы укрепить свою уверенность и привыкнуть иметь дело с теми случаями, когда вы не можете загрузить проект из-за синтаксической ошибки в файлах проекта, введите намеренную ошибку в начале файла проекта: просто введите букву перед первый тег вне структуры документа XML. Сохраните и закройте файл. Попробуйте загрузить проект из контекстного меню, и вы получите сообщение об ошибке, в котором говорится, что проект не может быть загружен. Да, Visual Studio очень строг, как это.

project_load_error

Заново откройте файл проекта, исправьте ошибку, сохраните и снова закройте. Когда вы перезагружаете проект, он должен загружаться плавно. Теперь пришло время редактировать по-настоящему. Мы только вручную изменим одну вещь в файле проекта, и это будет добавление элемента Import, который будет импортировать файл для выполнения сборки JavaScript.


Если вы добавите элемент Import в файл проекта для файла, который не существует, вы не сможете загрузить проект, поэтому создайте новый текстовый файл с именем js.build в папке jsbuild. После ввода необходимого кода XML среда IDE распознает этот файл как файл XML. Нет необходимости связывать расширение .build с редактором XML. Введите этот стартовый код в jsbuild\js.build , сохраните и закройте.

1
2
<Project ToolsVersion=»4.0″ xmlns=»http://schemas.microsoft.com/developer/msbuild/2003″>
</Project>

Теперь выгрузите проект и отредактируйте файл проекта, добавив эту строку в конец файла непосредственно перед закрывающим тегом.

1
<Import Project=»jsbuild\js.build» />

Теперь вы сможете перезагрузить проект.


Пять восклицательных знаков — верный признак безумного ума. — Терри Пратчетт, Жнец Человек

Мне немного скучно говорить «Hello World» в начале каждого нового урока по ИТ. Так что на этот раз я собираюсь поздороваться с удивительным Discworld Терри Пратчетта.

Откройте js.build . Среда IDE должна автоматически заметить, что это файл XML. Если нет, возможно, у вас неверный XML. После добавления следующего кода для настройки сообщения Hello Discworld в среде IDE наконец-то должно быть понятно, что это XML. Убедитесь, что файл js.build теперь содержит следующий XML. Не забудьте пять восклицательных знаков, чтобы получить правильный вкус безумия для Discworld !!!!!

1
2
3
4
5
6
7
8
<Project ToolsVersion=»4.0″ xmlns=»http://schemas.microsoft.com/developer/msbuild/2003″>
  <Target Name=»HelloDiscworld»>
    <Message Text=»Hello Discworld!!!!!»
  </<Target>
  <Target Name=»AfterBuild»>
    <CallTarget Targets=»HelloDiscworld»></CallTarget>
  <Target>
</Project>

Когда вы щелкаете правой кнопкой мыши по проекту и запускаете сборку, вы должны увидеть сообщение в окне вывода.

hello_discworld

Как и Ant, MSBuild использует идею целей для выполнения групп задач. Цель AfterBuild автоматически запускается MSBuild после того, как все остальное было успешно построено. Я добавляю сборку JavaScript в конец сборки .NET, поэтому точка расширения AfterBuild кажется лучшим местом для этого. Обратите внимание, как AfterBuild запускается автоматически, и в AfterBuild мы называем наш Target HelloDiscworld. Я установил значение сообщения как высокое, потому что в противном случае оно может не появиться в окне вывода.


Правильно. Мы немного сошли с ума в Discworld со слишком многими восклицательными знаками, но, по крайней мере, наш файл сборки JavaScript работает! OK. Не говоря уже о шутках, теперь мы должны получить самую важную вещь в рутине сборки: правильные пути.

Как и в случае с Ant, у меня всегда были проблемы с пониманием абсолютных и относительных путей в этих файлах конфигурации, поэтому я хочу осторожно идти по пути. Добавьте элемент PropertyGroup в js.build файла js.build , чуть ниже тега Project, и добавьте два таких свойства, как это.

1
2
3
4
<PropertyGroup>
  <ConcatDir>debug-js\concat</ConcatDir>
  <MinDir>debug-js\min</MinDir>
</PropertyGroup>

Теперь измените сообщение, чтобы мы могли видеть, что содержат эти свойства:

1
<Message Text=»Hello Discworld!!!!! from $(ConcatDir)» Importance=»high»></Message>

Теперь очистите и соберите проект снова или просто выберите rebuild. Сообщение появляется в выводе так:

Hello Discworld!!!!! from debug-js\concat


Прекрасный. У нас есть среда, исходные файлы и свойства в файле сборки, содержащие относительные пути, указывающие на каталоги, с которыми нам нужно работать. Теперь мы можем добавить цель CleanJs и цель InitJs, чтобы удалить и сделать каталоги concat и min. У меня есть привычка помещать небольшие «привет» сообщения в эти цели при разработке этих файлов, просто чтобы убедиться, что они на самом деле работают или проверяют значения свойств. Я обнаружил, что увеличение выходной детализации в MSBuild дает мне поток информации, которая мне не нужна, хотя это здорово, когда я не могу понять, где я допустил ошибку.

MSBuild использует простые относительные пути из корневой папки всего проекта. Если в вашем проекте есть папка с именем js, вы можете использовать значение js в именованном свойстве в PropertyGroup без каких-либо дополнительных сложностей.

01
02
03
04
05
06
07
08
09
10
11
12
<Target Name=»CleanJs»>
  <Message Text=»Hello from CleanJs» Importance=»high»></Message>
  <RemoveDir Directories=»$(ConcatDir)» Condition=»Exists(‘$(ConcatDir)’)»>
    <Output PropertyName=»ConcatDirRemoved» TaskParameter=»RemovedDirectories»/>
  </RemoveDir>
  <RemoveDir Directories=»$(MinDir)» Condition=»Exists(‘$(MinDir)’)»></RemoveDir>
  <Message Text=»Hello from removed dirs $(ConcatDirRemoved)» Importance=»high»></Message>
</Target>
<Target Name=»InitJs»>
  <MakeDir Directories=»$(ConcatDir)» Condition=»!Exists(‘$(ConcatDir)’)»></MakeDir>
  <MakeDir Directories=»$(MinDir)» Condition=»!Exists(‘$(MinDir)’)»></MakeDir>
</Target>

Для запуска этих целей добавьте элементы AfterBuild цели AfterBuild .

1
2
<CallTarget Targets=»CleanJs»></CallTarget>
<CallTarget Targets=»InitJs»></CallTarget>

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

invalid_child_element

Это досадная ошибка в Visual Studio, которая существует уже довольно давно. Элементы PropertyGroup и ItemGroup могут быть заполнены любым значением, которое вам нравится. Проблема заключается в том, что Visual Studio неправильно сообщает об ошибке для первого свойства или элемента, определенных в одной из этих групп. Как вы уже видели, ConcatDir работает при сборке проекта, и нет проблем с загрузкой проекта. Просто игнорируйте эти отвлекающие недопустимые ошибки дочернего элемента.

Наконец, настоящая сборка. Мы добавляем новую цель для объединения файлов, которые мы хотим. В отличие от Ant и NAnt, здесь нет встроенной задачи Concat, поэтому нам нужно выполнить собственную задачу ReadLinesFromFile.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<Target Name=»ConcatenateJsFiles»>
  <ItemGroup>
  <ConcatFiles Include=»
               js\jquery-1.8.2.min.js;
               js\jquery-ui-1.9.2.custom.min.js;
               debug-js\src\jquery.ui.menubar.js;
               js\knockout-2.1.0.js;
               debug-js\src\default-src.js
               «/>
  </ItemGroup>
  <ReadLinesFromFile File=»%(ConcatFiles.Identity)»>
    <Output TaskParameter=»Lines» ItemName=»ConcatLines»/>
  </ReadLinesFromFile>
  <WriteLinesToFile File=»debug-js\concat\default-concat.js» Lines=»@(ConcatLines)» Overwrite=»true» />
</Target>

Добавьте новый элемент AfterBuild цели js.build в js.build вызвав ConcatenateJsFiles . Перестройте проект как обычно, и вот, файл с именем default-concat.js волшебным образом создается в каталоге debug-js\concat . Вам, вероятно, придется обновить обозреватель решений, чтобы увидеть его.

Теперь добавьте новую страницу веб-формы с именем Default-concat.aspx в папку debug , связав ее со страницей Main.Master . Это очень коротко и немного отличается от страницы Default-src.aspx . На этот раз весь необходимый нам JavaScript был объединен в один файл, поэтому вам нужна только одна ссылка на скрипт в default-concat.js .

1
2
3
4
5
6
<%@ Page Title=»Default concat» Language=»C#» MasterPageFile=»~/Main.Master» AutoEventWireup=»true» CodeBehind=»Default-src.aspx.cs» Inherits=»NetTutsMsBuildJs.debug.Default_src» %>
<asp:Content ID=»Content1″ ContentPlaceHolderID=»head» runat=»server»>
</asp:Content>
<asp:Content ID=»Content2″ ContentPlaceHolderID=»JsScripts» runat=»server»>
    <script src=»/debug-js/concat/default-concat.js»></script>
</asp:Content>

Чтобы попробовать это, откройте страницу Default-concat.aspx в окне IDE и снова запустите проект в режиме отладки. Вы должны увидеть полностью функционирующую строку меню в вашем браузере с заголовком «Debug concat», появляющимся в строке заголовка браузера.


Конечная цель, цель !!!!!

Кажется, что наша строка меню работает, и когда мы объединяем файлы, мы, кажется, имеем правильный порядок, и все идет гладко на странице Debug-concat.aspx . Наконец пришло время минимизировать исходные файлы default-src.js и jquery.ui.menubar.js и jquery.ui.menubar.js их с файлами профессионального выпуска в правильном порядке. Это немного сложнее, потому что теперь нам нужно ввести внешнюю зависимость, которая нам пока не нужна: компрессор YUI. Разрабатывается порт .NET , но я настолько привык к версии Java, что предпочитаю использовать свой старый любимый. Создайте новую цель с именем MinifyJsFiles следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
<Target Name=»MinifyJsFiles»>
  <ItemGroup>
    <MinFiles Include=»
               debug-js\src\jquery.ui.menubar.js;
               debug-js\src\default-src.js
         «/>
    <Compressor Include=»jsbuild\yuicompressor-2.4.7.jar»></Compressor>
  </ItemGroup>
  <Message Text=»Hello Compressor.Fullpath: %(Compressor.Fullpath)» Importance=»high»></Message>
  <Exec Command=»java -jar %(Compressor.Fullpath) debug-js\src\default-src.js —type js -o debug-js\min\default-min.js»/>
  <Exec Command=»java -jar %(Compressor.Fullpath) debug-js\src\jquery.ui.menubar.js —type js -o debug-js\min\jquery.ui.menubar-min.js»/>
 
</Target>

Обратите внимание на свойство Compressor. Здесь вам просто нужно определить относительный путь из папки project , но для файла jar, запускаемого процессом Java, потребуется полный путь. К счастью, MSBuild предоставляет простой способ преобразовать относительный путь в полный путь. Вы используете синтаксис % и вызываете свойство Fullpath. Это пример общеизвестных метаданных элемента MSBuild .

Добавьте еще один элемент CallTarget элементу AfterBuild чтобы вызвать цель MinifyJsFiles .

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<Target Name="ConcatenateMinFiles">
  <ItemGroup>
  <ConcatMinFiles Include="
                    js\jquery-1.8.2.min.js;
                    js\jquery-ui-1.9.0.custom.min.js;
                    debug-js\min\jquery.ui.menubar-min.js;
                    js\knockout-2.1.0.js; 
                    debug-js\min\default-min.js
                    "/>             
  </ItemGroup>
  <ReadLinesFromFile File="%(ConcatMinFiles.Identity)" >
    <Output TaskParameter="Lines" ItemName="ConcatMinFilesLines"/>
  </ReadLinesFromFile>
  <Message Text="We are concatenating these minified files %(ConcatMinFiles.Identity)" Importance="high"></Message>
  <WriteLinesToFile File="debug-js\min\default.js" Lines="@(ConcatMinFilesLines)" Overwrite="true" />
 </Target>

Вы должны быть осторожны с этим свойством ItemName в файлах сборки. Экземпляры свойств и элементов хранятся в глобальном контексте в MSBuild. Если вы используете одно и то же имя для ItemNameдвух разных объединенных целей, вы в конечном итоге объедините все файлы из обеих целей.

Перестройте проект, и вы должны увидеть два новых файла в debug-js\minпапке: default-min.jsи jquery.ui.menubar-min.js. debug-jsПапка должна выглядеть следующим образом после повторного строительства и освежать в Solution Explorer:

отлаживать-JS

Создайте новую страницу веб-формы, которая называется Default-min.aspxсвязанной со Main.Masterстраницей, и поместите ее в debugпапку.

1
2
3
4
5
6
<%@ Page Title="Default min" Language="C#" MasterPageFile="~/Main.Master" AutoEventWireup="true" CodeBehind="Default-src.aspx.cs" Inherits="NetTutsMsBuildJs.debug.Default_src" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="JsScripts" runat="server">
    <script src="/debug-js/min/default-min.js"></script>
</asp:Content>

Мы прошли через шаги, необходимые в Visual Studio Express 2012 для Web, чтобы создать проект JavaScript, который объединяет Knockout с jQuery UI для создания меню, а затем интегрирует сборку JavaScript в общую сборку проекта в Visual Studio.

В этом руководстве мы рассмотрели шаги, необходимые в Visual Studio Express 2012 для Web, чтобы создать проект JavaScript, который объединяет Knockout с jQuery UI для создания меню из файла определения JSON, а затем интегрирует сборку JavaScript исходных файлов в. NET MSBuild процесс. В итоге мы получили веб-страницу с одним тегом скрипта, содержащим весь сложный JavaScript, необходимый для запуска страницы.

Я думаю, вы видите, как легко было бы адаптировать этот пример к очень большой и сложной библиотеке JavaScript, работающей в проекте .NET. Также должно быть достаточно просто разработать эти идеи, чтобы включить задачи, подходящие для версии выпуска. Следующий очевидный шаг — скопировать полностью минимизированный и сцепленный default.jsфайл в jsпапку, а затем включить его в окончательный Default.aspxфайл в корневом каталоге. Используя этот пример в качестве отправной точки, вы сможете изучить документацию MSBuild и разработать полностью рабочий файл сборки, чтобы автоматизировать каждую часть процесса сборки.

Я также использую этот подход для файлов CSS. В этом конкретном случае CSS-файлы jQuery UI настолько хорошо оптимизированы, что вряд ли стоит их минимизировать, но в других проектах это может быть важно для производительности. Более сложный следующий шаг для вас хрюкать ERS там будет создать js.buildфайл , который запускает черновой файл с задачей MSBuild Exec. Таким образом, вы можете легко включить linting и тестирование в процесс сборки.

Для дальнейшего изучения Visual Studio, этот превосходный Nettuts + Visual Studio: Web Dev Bliss поможет вам интегрировать Web Essentials и добавить проверку кода в процесс сборки, но, к сожалению, Web Essentials недоступен для Express Edition. См. Ответ Мадса Кристенсена здесь : «… к сожалению, Express не позволяет устанавливать сторонние расширения». Это руководство предназначено для пользователей редакции Express, и я надеюсь, что оно послужило отправной точкой для создания собственной интегрированной сборки JavaScript в среде Visual Studio Express.