Введение
Этот туториал я пишу прежде всего для себя, для того чтобы иметь возможность быстро на основе начального шаблона ASP.NET Core приложения создать минимальное приложение с поддержкой npm, Webpack и TypeScript (у которого будет работать отладка из Visual Studio).
Мотивация
Я довольно плохо знаком с веб и в частности JavaScript. Однако работаю с .Net постоянно. Также я полагаю, что знать JavaScript и всякие разные фреймворки на нем, которых сейчас как грязи это стильно, модно и молодежно. Но чтобы пощупать очередную либу обычно нужно кучу всякого устанавливать, чтобы она впринципе заработала.
Обычно требуется сервер (через node.js), npm, и что-то типа Webpack-а (Для работы Less или какого-нибудь шаблонизатора/минификатора). Я немного ковырял ASP.NET (Не Core, а обычный) и примерно представляю как быть с серверной частью, но фронтенд для меня темный лес.
Пробема в том, что технологии в вебе обычно не ориентируются на ASP.NET, а больше на node.js, php и прочий мейнстрим. И примеры в документации тоже всегда на этом всем базируются, и для человека непосвященного не так то быстро получается это все с нуля настроить под ASP.NET.
В этом туториале я показываю как шаг за шагом из шаблонного нового проекта на ASP.NET Core сделать минимальное рабочее приложение, в котором будет
- npm — Нужен для установки различных библиотек в JavaScript, требуется повсеместно.
- Webpack — Нужен если JavaScript и другой контент нужно упаковывать, минифицировать, использовать Less вместо ванильного CSS, шаблонизаторы HTML, использовать транспиляторы JavaScript и тому подобное.
- TypeScript — Его можно использовать как транспайлер из новых версий JavaScript в старые. Также можно использовать сам язык TypeScript. Два в одном получается, плюс грех не использовать так как в Visual Studio сделана неплохая его поддержка.
Умея быстро создать такое приложение дальше уже можно смело изучать любые веб-технологии (включая сам npm, Webpack и TypeScript), при этом сервер будет на родном дотнете.
На данный момент в ASP.NET и так уже встроены механизмы похожие на те, что есть в Webpack, а также студия из коробки (Во всяком случае 2017-ая) работает с TypeScript и компилирует его автоматически, если создать файл конфига для компилятора TypeScript-а.
Но все эти вещи поддерживаются разработчиками ASP.NET, а не веб-сообществом.
Стандартный компилятор TypeScript в данном туториале отключается, и используется тот, который является модулем для npm.
Подготовка
Что понадобится скачать и установить для начала работы
- Visual Studio 2017 Community — При установке компонентов выбрать все что связано с ASP.NET и Core
- Node.js и npm — Node.js требуется для работы npm и для запуска Webpack скрипта при построении проекта в Visual Studio. Скачиваем и устанавливаем последнюю версию node.js, npm устанавливается вместе с ним.
Создание проекта
Открываем Visual Studio, создаем новый проект по стандартному шаблону ASP.NET Core Web Application (.NET Core). Шаблон находится в Templates — Visual C# — Web. Если такого шаблона нет, возможно не установлены какие-либо компоненты (Нужно опять запустить Visual Studio installer и установить их).
Теперь у нас есть стартовый проект, он должен компилироваться и запускаться.
В случае, если используется git, то сразу добавляем .gitignore файл в папку с solution-ом (Там же инициализируем git репозиторий). Файл .gitignore для solution-ов в Visual Studio можно взять отсюда.
Упрощаем типовой проект
Далее изменим проект так, чтобы он получился максимально простым.
Сделаем из него Hello World.
Удаляем следующие файлы
- bower.json (и вложенный в него .bowerrc), bundleconfig.json
- Все содержимое папки wwwroot. Можно оставить favicon.ico.
- Папка Views — удаляем все внутренности, кроме Views/Home/Index.cshtml
В файле Controllers/HomeController.cs в классе HomeController
заменяем внутренности на следующий код
public IActionResult Index()
=> View();
public string Error()
=> "Error";
По сути тут убирается все кроме методов Index()
и Error()
, при этом Error()
теперь возвращает строку "Error" вместо вьюшки (так как мы ранее удалили файл с этой вьюшкой).
Последний штрих — заменяем содержимое файла Views/Home/Index.cshtml на следующее
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Index</title>
</head>
<body>
<h1>Hello world</h1>
</body>
</html>
Все, теперь у нас есть базовый максимально упрощенный проект. Его можно скомпилировать и запустить, на странице будет отображаться текст "Hello world"
Добавляем npm
Добавляем в проект файл package.json
{
"version": "1.0.0",
"name": "yourappname",
"private": true
}
Вместо "yourappname" вписываем название проекта маленькими буквами.
npm при своей работе может писать в лог с названием npm-debug.log. Стоит исключить его из проекта, чтобы не мешал. Это можно сделать либо вызвав в студии на нем контекстное меню и выбрав Exclude From Project. Либо, в случае если он еще не создан (Скорее всего, так оно и есть), можно отредактировать файл проекта .csproj, добавив в корневой тег <Project>
следующее
<ItemGroup>
<None Remove="npm-debug.log" />
</ItemGroup>
Все, теперь у нас есть поддержка npm, в зависимостях проекта студия теперь показывает узел "npm".
Если построить проект (А так же при открытии проекта, изменении файла package.json и т.д) начнется процесс восстановления зависимостей npm, в случае если они указаны (Сейчас их нет). Зависимости скачиваются в папку node_modules в папке проекта (Студия не воспринимает эту папку как часть проекта).
Добавляем Webpack
Webpack является зависимостью npm, по этому его нужно добавить в package.json как зависимость. Так как он не будет использоваться в клиентском JavaScript коде, он объявляется как dev dependency. Для нас это значит что он будет работать как дополнительный инструмент компиляции для Visual Studio, ни на сервере, ни на клиенте его не будет, на клиенте же будет тот контент (включая JavaScript код), который Webpack сгенерирует.
Кроме самого Webpack-а понадобятся также две вещи, облегчающие работу (Тем не менее они не обязательны)
- Плагин к Webpack-у, который будет очищать папку, в которой Webpack будет создавать файлы (Эта папка в данном туториале будет названа wwwroot/bundle).
- Расширение Visual Studio, позволяющее выполнять работу Webpack-а каждый раз при построении ASP.NET приложения из студии автоматически.
Сначала скачаем и установим расширение для Visual Studio, его можно найти по названию "NPM Task Runner", либо скачать по этой ссылке.
Теперь добавим в package.json следующие строки (добавляем внутрь корневых фигурных скобок)
"devDependencies": {
"webpack": "^2.5.1",
"clean-webpack-plugin": "^0.1.16"
},
"scripts": {
"webpack-script": "webpack"
},
"-vs-binding": {
"BeforeBuild": [
"webpack-script"
]
}
В "devDependencies"
мы указали сам Webpack и плагин для очистки того, что он генерирует.
В "scripts"
мы указали скрипт с названием "webpack-script"
, который будет запускать Webpack (В этот момент он будет генерировать контент, транспилировать код и тд). Расширение Visual Studio, которое мы установили ранее делает так, что этот скрипт будет виден студии как задача, которую можно выполнить, таким образом мы можем запланировать эту задачу на выполнение при построении приложения.
В "-vs-binding"
мы указали, что Visual Studio должна вызывать задачу "webpack-script"
(Которую студия теперь видит, благодаря установленному расширению) каждый раз перед построением проекта.
Теперь нужно настроить сам Webpack. Он настраивается в с помощью JavaScript скрипта webpack.config.js, который будет выполняться через node.js в момент вызова задачи webpack-script
. Добавим файл webpack.config.js в проект и заполним его содержимое следующим кодом
"use strict"
{
// Требуется для формирования полного output пути
let path = require('path');
// Плагин для очистки выходной папки (bundle) перед созданием новой
const CleanWebpackPlugin = require('clean-webpack-plugin');
// Путь к выходной папке
const bundleFolder = "wwwroot/bundle/";
module.exports = {
// Точка входа в приложение
entry: "./Scripts/main.js",
// Выходной файл
output: {
filename: 'script.js',
path: path.resolve(__dirname, bundleFolder)
},
plugins: [
new CleanWebpackPlugin([bundleFolder])
]
};
}
Тут мы настроили входной и выходной пути к JavaScript файлам, а также прописали ранее добавленный плагин для очистки выходной папки.
Выходной файл webpack генерирует на основе входного. Входного файла у нас еще нет, по этому его надо создать. Создадим файл Scripts/main.js со следующим содержимым
document.getElementById("helloworld").innerText = "Hello world from script";
Входной файл не будет доступен пользователю, так как он находится в папке Scripts, а не wwwroot, выходной же файл, сгенерированный Webpack-ом попадет в папку wwwroot/bundle/ и будет доступен для клиента.
Со стороны клиента выходной файл будет находится по адресу ~/bundle/script.js, где ~ это адрес сайта, на котором работает веб-приложение.
Изменим файл Views/Home/Index.cshtml так, чтобы он включал выходной файл, и, чтобы скрипт мог менять текст на странице (по id элемента "helloworld").
Для этого заменим внутренности тега <body>
на следующее
<h1 id="helloworld"></h1>
<script src="~/bundle/script.js"></script>
В случае, если используется git, также следует исключить генерируемые Webpack-ом файлы. Для этого создадим еще один файл .gitignore, но теперь уже в папке проекта (Не путать с папкой solution-а)
# сгенерированные webpack-ом файлы
wwwroot/bundle/
Студия при этом будет показывать .gitignore файл в проекте, чтобы этого не происходило, в в файле проекта .csproj надо добавить следующие строки внутри корневого тега <Project>
(Ну или через контекстное меню по файлу -> Exclude From Project)
<ItemGroup>
<None Remove=".gitignore" />
</ItemGroup>
Все, теперь проект полностью настроен под использование npm и Webpack. Если скомпилировать и запустить приложение, на странице должен отобразиться текст "Hello world from script"
На этом этапе уже можно устанавливать JavaScript библиотеки, объявляя их в разделе "dependencies"
в package.json и использовать их в Scripts/main.js, подключая эти библиотеки как модули через функцию require("название библиотеки")
. Например, если установить таким образом библиотеку "jquery", то файл Scripts/main.js можно переписать следующим образом (Примечание: это просто пример, для продолжения не обязательно ни устанавливать jquery, ни изменять main.js)
var $ = require('jquery');
$("#helloworld").text("Hello world");
Единственное что отладка по входным файлам в Visual Studio работать не будет. Однако это можно исправить, добавив поддержку TypeScript.
Добавляем TypeScript
Прежде всего, нужно отключить стандартную транспиляцию TypeScript. Для этого нужно в файле проекта .csproj добавить следующие строки внутри корневого тега <Project>
<PropertyGroup>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
</PropertyGroup>
Далее, нужно добавить TypeScript как dev dependency для npm. Для этого в файл package.json в разделе "devDependencies"
добавляем следующее
"typescript": "^2.3.2",
"ts-loader": "^2.0.3"
Теперь нужно создать файл конфигурации для компилятора TypeScript. Создаем файл tsconfig.json следующего содержания
{
"compilerOptions": {
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": false,
"alwaysStrict": true,
"allowSyntheticDefaultImports": true,
"lib": [
"dom",
"es5",
"es2015.promise"
],
"allowJs": true,
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"sourceMap": true
},
"include": [ "./Scripts/*" ],
"compileOnSave": false
}
Описание некоторых указанных в этом файле значений
"allowJs": true
— разрешаем транспиляцию из JavaScript в JavaScript (Из новой версии языка в старую. Расширение файлов .js)"target": "es5"
— выходная версия JavaScript, в которую будет транспилиться TypeScript и входной JavaScrpt"module": "es2015"
— синтакс для работы с модулями"moduleResolution": "node"
— стратегия разрешения модулей — такая же как в node.js"sourceMap": true
— включаем генерацию данных для отладки TypeScript"include": [ "./Scripts/*" ]
— указываем, откуда брать исходники .ts и .js, которые надо транспилировать в выходной JavaScript
Далее нужно подружить TypeScript с Webpack-ом. Для этого заменяем файл скрипта для настройки конфигурации Webpack-а webpack.config.js на следующий (По сути мы меняем некоторые места, но чтобы не писать все изменения, выкладываю полный файл)
"use strict"
{
// Требуется для формирования полного output пути
let path = require('path');
// Плагин для очистки выходной папки (bundle) перед созданием новой
const CleanWebpackPlugin = require('clean-webpack-plugin');
// Путь к выходной папке
const bundleFolder = "wwwroot/bundle/";
module.exports = {
// Точка входа в приложение
entry: "./Scripts/main.ts",
// Выходной файл
output: {
filename: 'script.js',
path: path.resolve(__dirname, bundleFolder)
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
exclude: /node_modules/,
},
]
},
resolve: {
extensions: [".tsx", ".ts", ".js"]
},
plugins: [
new CleanWebpackPlugin([bundleFolder])
],
// Включаем генерацию отладочной информации внутри выходного файла
// (Нужно для работы отладки клиентских скриптов)
devtool: "inline-source-map"
};
}
Тут мы поменяли расширение входного скрипта с .js на .ts, указали, что входной скрипт нужно пропускать через загрузчик TypeScript (loader: "ts-loader"
) и сделали некоторые другие вещи.
И последний шаг — переименовываем файл входного скрипта с Scripts/main.js на Scripts/main.ts, внутренности можно оставить прежними.
Итог
Все! Теперь мы имеем рабочий проект на ASP.NET Core, у которого есть npm, Webpack и TypeScript и все это работает в одной связке.
Так же теперь доступна отладка TypeScript из Visual Studio, можно во входном .ts файле поставить точку останова, запустить проект в режиме отладки (Запускать при этом надо в браузерах chrome либо internet explorer, иначе отладка работать не будет, также в chrome у меня оно работает только когда я после загрузки страницы явно нажимаю refresh страницы, видимо отладчик к chrome подсоединяется не сразу). При этом обратите внимание что точки останова ставятся во входных файлах, но реално код работает выходной (В выходном коде в виде комментария webpack записывает информацию нужную для маппинга с выходного на входные файлы).
Можно создавать и другие входные файлы в папке Scripts, в виде .ts или .js и те и другие будут полностью поддерживать новый стандарт EcmaScript (Выходной файл же будет es5 стандарта). Для подключения дополнительных входных файлов нужно оформить их в виде модулей и подключать в main.ts через оператор import
либо функцию require()
.
Еще небольшое замечание — выходные файлы (те, что в папке wwwroot/bundle/) лучше не исключать из проекта через .csproj файл, так как если студия их не видит, отладка входных файлов работать перестает.
Комментарии (8)
mrpostrock
15.05.2017 08:04Для сборки можно еще установить webpack глобально и запустить отдельно в консоли с ключем w и тогда он будет сам смотреть за изменением файлов и собирать сразу. webpack -w
NickAb
15.05.2017 14:15+3Для asp.net core есть https://github.com/aspnet/JavaScriptServices, который предоставляет:
- сборку webpack в памяти при разработке (чтобы не надо было пересобирать или ставить watch),
- добавляет поддержку hot module replacement для angular и некоторых других библиотек,
- добавляет рендеринг на стороне сервера
- и прочие плюшки.
А сгенерировать проект со всем этим добром, настроенным webpack и т.д. можно через их генератор:
npm install -g yo generator-aspnetcore-spa yo aspnetcore-spa
rocketjump
21.05.2017 14:27Typescript удобнее отлаживать в студии? Всегда думал что лучше дебажить js в браузере.
Разве не удобнее разнести вообще фронт и бэкенд как разные проекты? Бэкенд отдает чисто данные по бизнес логике, фронт роутит и наполняет контентом странички — или с typescript такое не прокатит и надо тащить еще фреймворк (angular, react)?
Так всегда проще нанять чистого фронта, которому привычнее работать через webstorm, sublime и тд чем через Vs.ultimabear
21.05.2017 14:37Еще раз, я ничего не знаю про веб. Но в случае с C++ например мне сложно представить кого-то, кому удобнее ковыряться в отладчике уровня ассемблера вместо отладки по исходникам в ide. Про разнос на 2 проекта — можно и разнести наверно, но зачем? Тут цель сделать минимально возможный по зависимостям и общей сложности проект, чтобы с него делать эксперименты с вебом (конкретно у меня это для обучения вебу в принципе). А вообще фронтендеру и так и так придется работать с .cshtml, ну а билдить проект он может и через командную строку через msbuild если студия так не нравится. И работать в своем блокноте сколько влезет.
kekekeks
Это, вообще говоря, очень не хорошо, ибо вы таким образом сборку прибиваете к студии и к наличию установленного в ней расширения. Лучше в файл проекта добавить target:
См. более полный пример.
ultimabear
Да, наверное так лучше