Мотивация
Предположим, вы работаете над веб-приложением ASP.NET Core, которое решает некоторые бизнес-задачи. Знаете, несколько форм, куда пользователи вводят свои данные и получают какие-то отчеты.
Хотя такой проект может не требовать какой-либо сложной логики на клиенте, вам все же, вероятно, потребуется написать некоторый код JavaScript, чтобы сделать взаимодействие пользователя с вашим приложением более удобным и приятным. Например, вам может понадобиться простое всплывающее окно с подсказкой об удалении элемента, так как использовать для этого отдельную страницу не совсем правильно. Или вы хотите выполнить проверку на стороне клиента. Или… это действительно может быть любая другая клиентская задача, которую вы называете.
Конечно, вы можете добавить несколько строк скрипта на каждую страницу (с помощью ванильного JavaScript или старого доброго JQuery), но очень сложно поддерживать все эти маленькие кусочки, когда ваш проект становится больше. Более того, некоторые из этих частей делают одно и то же, так что вам либо нужно реплицировать их во многих местах (плохое решение), либо вы в конечном итоге создаете небольшую библиотеку, содержащую все клиентские функции, структуры и классы, используемые в вашем проекте.
В этой статье описывается, как создать такую JS-библиотеку для вашего проекта ASP.NET Core с минимальными усилиями и таким образом, чтобы обеспечить лучшую поддержку для дальнейших изменений.
Решение
Короче говоря, мы собираемся поместить весь наш клиентский код в отдельные файлы (с классами, функциями, структурами данных и т. д.), а затем связать их с помощью WebPack 5. Полученный скрипт можно включить прямо в ваш _Layout.cshtml (тогда он будет доступен на всех страницах вашего веб-приложения) или вы можете включить его только на те страницы, где это необходимо.
Более того, мы будем использовать TypeScript вместо чистого JavaScript, потому что, как вы знаете, он современный и блестящий, а статическая типизация хороша и позволяет нам отлавливать множество ошибок во время компиляции.
Кроме того, вы можете рассматривать эту статью как краткое введение в клиентскую разработку. Особенно, если вы являетесь .NET-разработчиком, который по-прежнему предпочитает работать только с бэкендом и боится всех этих причудливых вещей на стороне клиента (как и я некоторое время назад).
Начало работы
Здесь мы опишем шаги, необходимые для настройки конфигурации для объединения небольшой библиотеки TypeScript с вашим собственным кодом.
Для упрощения наша библиотека пока будет содержать только одну функцию.
Шаг 0. Установите Node.JS
Я почти уверен, что он у вас уже установлен. Если нет - пожалуйста. Нам понадобится Node.JS версии 10.13.0 (или новее) на вашем компьютере для разработки/сборки.
Шаг 1. Создайте подпапку `ClientScript`.
Мы поместим все наши скрипты и файлы конфигурации в отдельную подпапку ClientScript в папке вашего основного проекта.
Это похоже на подпапку ClientApp, используемую в большинстве шаблонов SPA (одностраничных приложений), доступных для ASP.NET Core.
Шаг 2. Добавьте файлы конфигурации
Нам понадобятся 3 конфигурационных файла:
- package.json для определения нашего пакета и всех зависимостей.
- webpack.config.js для конфигурацииили WebPack.
- tsconfig.json для настроек TypeScript.
На данный момент вы можете просто скопировать эти файлы как есть. О каждом из них мы расскажем позже.
Шаг 3. Добавьте файлы TypeScript
Для простоты наша библиотека на этом начальном этапе будет содержать только одну функцию hello(), которая просто выводит «Hello world» в консоль браузера. Вот два файла, которые нам нужны для этой выдающейся :) функциональности:
Здесь hello.ts
содержит нашу функцию, упакованную в пространство имен funcs
, поэтому мы сможем вызывать ее как MYAPP.funcs.hello()
, а index.ts
— наша точка входа. Прежний файл TypeScript не будет содержать никаких функций или классов. Он просто определит, какие части нашего кода (функции, интерфейсы, классы и т. д.) мы собираемся открыть для внешнего мира. Поскольку у нас есть только один файл с «настоящей» функциональностью, наш index.ts
содержит только одну строку, которая просто экспортирует («показывает») весь общедоступный (экспортированный) код из hello.ts
.
Шаг 4: Создайте свою библиотеку
Вот и все. Мы готовы построить наш скрипт пакета. Для этого откройте программу терминала, перейдите в папку «ClientScript» и выполните следующие 2 команды:
> npm install
а потом:
> npm run build
Первый установит все необходимые библиотеки NPM (те, которые перечислены в разделах dependencies
и devDependencies
вашего package.json). Вам нужно будет запустить его перед первой сборкой и только тогда, когда вы добавите новую зависимость (другой пакет NPM).
Вторая команда фактически запускает WebPack, который компилирует (или, если быть более точным, «транспилирует») ваши файлы TypeScript в JavaScript, затем объединяет весь код JS в один файл app-client.js и, наконец, , помещает этот файл в папку wwwroot/js вашего веб-проекта, как указано в файле конфигурации webpack.config.js.
В соответствии с разделом output/library
этого файла все функции или структуры вашего нового пакета будут доступны через глобальную переменную MYAPP
.
Шаг 5. Прикрепите окончательный вариант сценария к приложению
Чтобы использовать наш скрипт, вам просто нужно включить его на свою страницу, как и любой другой файл JS:
<script src="/js/app-client.min.js"></script>
Вы можете добавить эту строку либо в _Layout.cshtml (чтобы сделать ее доступной на всех страницах вашего веб-приложения), либо только в представления или страницы Razor, где это необходимо.
Теперь вы можете вызывать функции из нашей новой JS-библиотеки:
<script> MYAPP.funcs.hello(); </script>
Настройка области
Одной из замечательных особенностей использования TypeScript и WebPack является организация вашего кода в модулях, а затем объединение этих модулей в пространства имен с использованием конфигурации WebPack и структуры namespace
TypeScript.
Есть несколько возможных вариантов.
1. Используйте имена модулей и их псевдонимы
Вы можете поместить функции и классы в модуль и экспортировать этот модуль либо «как есть», либо с псевдонимом.
Например, если у нас есть следующий модуль dialog.ts:
и мы используем эту экспортную декларацию в нашем index.ts:
export * from ‘./dialogs’;
тогда наш класс Dialog
и функция showDialog()
будут доступны прямо в пространстве имен MYAPP
как MYAPP.Dialog
и MYAPP.showDialog()
.
Вы также можете указать псевдоним для этого модуля диалогов:
export * as dlg from './dialogs';
Теперь наш класс и функция будут доступны как MYAPP.dlg.Dialog
и MYAPP.dlg.showDialog()
соответственно.
2. Использование пункта namespace
Вы также можете использовать предложение namespace
, а затем повторно экспортировать импортированные модули, чтобы все функции, переменные и типы, принадлежащие одному и тому же пространству имен, даже в разных модулях, были объединены вместе.
Например, у нас есть следующие два модуля:
Теперь, если мы поместим следующие две строки в наш модуль `index.ts`
export * from ‘./dialogs’; export * from ‘./widgets’;
мы сможем получить доступ ко всем этим экспортированным функциям и классам в пространстве имен MYAPP.ui
. Например: MYAPP.ui.renderWidget1()
Использование сторонних библиотек
Пожалуй, самым значительным преимуществом этой настройки (которая может показаться немного сложной для простой функции hello world) является возможность использовать любую стороннюю JS-библиотеку из сотен тысяч доступных в репозитории NPM.
Чтобы продемонстрировать эту возможность, мы немного изменим нашу функцию hello()
, чтобы она принимала параметр (name
) и выводила в консоль фразу «Привет, {имя}». Перед печатью строка, хранящаяся в переменной name
, будет написана с заглавной буквы функцией capitalize()
из известной библиотеки lodash.
Вот шаги, которые мы должны предпринять для достижения этой цели:
Шаг 1. Добавьте библиотеку `lodash` в файл package.json
Просто откройте терминал в папке ClientScript и введите:
npm install lodash
В результате этой операции вы увидите примерно следующее в разделе dependencies
вашего файла package.json:
"dependencies": { "lodash": "^4.17.21" }
NB: фактический номер версии может отличаться.
Шаг 2. Импортируйте функции lodash в файл hello.ts
Добавьте следующую строку в начало файла hello.ts:
import * as _ from ‘lodash’;
3. Изменить функцию `hello()`
Теперь мы можем использовать все функции библиотеки lodash, используя глобальную переменную _
(это способ использования функций lodash по умолчанию с тех пор, как это не было библиотекой NPM.
Итак, наша функция hello()
теперь будет выглядеть следующим образом:
Кроме того, мы изменим вызов функции на нашей странице:
<script> MYAPP.funcs.hello('sergiy'); </script>
Когда мы перестроим наш скрипт (>npm run build
), запустим приложение и откроем главную страницу, мы увидим следующую строку в панели консоли нашего браузера:
> Hello, Sergiy
Режим просмотра
Нет необходимости запускать команду build каждый раз, когда вы что-то меняете в своем скрипте или добавляете в проект новый пакет. Вместо этого вы можете использовать специальный режим watch (он предоставляется WebPack из коробки), поэтому WebPack будет автоматически пересобирать ваш проект каждый раз, когда что-то в исходном файле или конфигурации проекта будет изменено. Чтобы запустить режим просмотра, просто введите в окне терминала
> npm run watch
Выводы
Как мы видим, использование TypeScript с WebPack для создания ванильных клиентских сценариев JS для ваших проектов ASP.NET Core имеет множество преимуществ:
- Код TypeScript со строгой типизацией со всеми преимуществами новейших функций JavaScript: классы, стрелочные функции, модули, области видимости и промисы.
- Улучшенная поддержка редакторами кода (такими как Visual Studio Code) с подсветкой синтаксиса, IntelliSense и т. д.
- Возможность использования сторонних библиотек с определениями типов.
- Более компактный и оптимизированный код JS 5, сгенерированный WebPack.
- Горячая перезагрузка изменений, внесенных вами в редакторе кода.
- Лучший опыт отладки. Можно отлаживать исходный код TypeScript вместо минимизированного кода JS, доступного для вашего веб-приложения.
Есть еще одно замечание. В этой статье мы использовали WebPack, так как на данный момент это самый популярный сборщик модулей. Однако я считаю, что мы можем быстро получить такие же результаты с любым другим сборщиком, таким как Browserify, Parcel или Rollup.
Пожалуйста, дайте мне знать (через мой Twitter @korzhs или здесь в комментариях), если эта статья была для вас ценной и информативной.
Удачного кодирования!