Запуск .NET из JavaScript
В этой статье объясняется, как запустить .NET из JavaScript (JS) с помощью/JS[JSImport]
[JSExport]
взаимодействия.
Дополнительные рекомендации см. в руководстве по настройке и размещению приложений .NET WebAssembly в репозитории GitHub .NET Runtime (dotnet/runtime
). Мы планируем обновить эту статью, чтобы включить новую информацию в кросс-связанные рекомендации в последней части 2023 или начале 2024 года.
Существующие JS приложения могут использовать расширенную поддержку WebAssembly на стороне клиента в .NET 7 или более поздней версии для повторного использования библиотек .NET из JS или для создания новых версий. Приложения и платформы на основе NET.
Примечание.
В этой статье рассматривается запуск .NET из JS приложений без каких-либо зависимостей Blazor. Рекомендации по использованию [JSImport]
[JSExport]
/взаимодействия в приложениях см. в Blazor WebAssembly разделе "Импорт иJS экспорт JavaScriptJS" с ASP.NET Core.Blazor
Эти подходы подходы подходят только для выполнения только в WebAssembly (WASM). Библиотеки могут сделать среду выполнения проверка, чтобы определить, работает WASM ли приложение путем вызоваOperatingSystem.IsBrowser.
Необходимые компоненты
Установите последнюю версию пакета SDK для .NET.
Установите рабочую нагрузку wasm-tools
, которая содержит связанные целевые объекты MSBuild.
dotnet workload install wasm-tools
При необходимости установите wasm-experimental
рабочую нагрузку, содержащую экспериментальные шаблоны проектов для начала работы с .NET в WebAssembly в приложении браузера (приложение webAssembly Browser) или в консольном приложении на основе Node.js (консольное приложение WebAssembly). Эта рабочая нагрузка не требуется, если планируется интегрировать JS[JSExport]
[JSImport]
/взаимодействие в существующее JS приложение.
dotnet workload install wasm-experimental
Дополнительные сведения см. в разделе " Экспериментальная рабочая нагрузка и шаблоны проектов ".
Пространство имен
API взаимодействия, описанный JS в этой статье, управляется атрибутами в System.Runtime.InteropServices.JavaScript пространстве имен.
Конфигурация проекта
Чтобы настроить проект (.csproj
) для включения JS взаимодействия:
Целевой
net7.0
объект или более поздней версии:<TargetFramework>net7.0</TargetFramework>
Укажите
browser-wasm
идентификатор среды выполнения:<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
Укажите тип выходных данных исполняемого файла:
<OutputType>Exe</OutputType>
AllowUnsafeBlocks Включите свойство, которое позволяет генератору кода в компиляторе Roslyn использовать указатели для JS взаимодействия:
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
Предупреждение
JS API взаимодействия требует включенияAllowUnsafeBlocks. Будьте осторожны при реализации собственного небезопасного кода в приложениях .NET, что может привести к рискам безопасности и стабильности. Дополнительные сведения см. в разделе "Небезопасный код", "типы указателей" и указатели функций.
Укажите
WasmMainJSPath
, чтобы указать файл на диске. Этот файл публикуется с приложением, но использование файла не требуется, если вы интегрируете .NET в существующее JS приложение.В следующем примере JS файл на диске доступен
main.js
, но любое JS имя файла является допустимым:<WasmMainJSPath>main.js</WasmMainJSPath>
Пример файла проекта (.csproj
) после настройки:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<WasmMainJSPath>main.js</WasmMainJSPath>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Взаимодействие JavaScript в WASM
API в следующем примере импортируются из dotnet.js
. Эти API позволяют настраивать именованные модули, которые можно импортировать в код C# и вызывать методы, предоставляемые кодом .NET, в том числе Program.Main
.
Важно!
Импорт и экспорт в этой статье определяются с точки зрения .NET:
- Методы импорта JS приложения, чтобы их можно было вызывать из .NET.
- Приложение экспортирует методы .NET, чтобы их можно было вызывать из JS.
В следующем примере :
Файл
dotnet.js
используется для создания и запуска среды выполнения .NET WebAssembly.dotnet.js
создается в рамках выходных данных сборки приложения и находится в папкеAppBundle
:bin/{BUILD CONFIGURATION}/{TARGET FRAMEWORK}/browser-wasm/AppBundle
Заполнитель
{BUILD CONFIGURATION}
— это конфигурация сборки (например,Debug
,Release
), а{TARGET FRAMEWORK}
заполнитель — целевая платформа (например,net7.0
).Важно!
Чтобы интегрироваться с существующим приложением, скопируйте содержимое
AppBundle
папки, чтобы ее можно было обслуживать вместе с остальными приложениями. Для рабочих развертываний опубликуйте приложение сdotnet publish -c Release
помощью команды в командной оболочке и разверните папкуAppBundle
с приложением.dotnet.create()
настраивает среду выполнения .NET WebAssembly.setModuleImports
связывает имя с модулем функций для импорта JS в .NET. Модуль JS содержит функцию, которая возвращает текущийwindow.location.href
адрес страницы (URL-адрес). Имя модуля может быть любой строкой (она не должна быть именем файла), но она должна соответствовать имени, используемому сJSImportAttribute
(описано далее в этой статье). Функцияwindow.location.href
импортируется в C# и вызывается методомGetHRef
C#. ЭтотGetHRef
метод показан далее в этом разделе.exports.MyClass.Greeting()
вызовы в .NET (MyClass.Greeting
) из JS. МетодGreeting
C# возвращает строку, содержащую результат вызоваwindow.location.href
функции. ЭтотGreeting
метод показан далее в этом разделе.dotnet.run()
выполняетсяProgram.Main
.
JS Модуль:
import { dotnet } from './dotnet.js'
const is_browser = typeof window != "undefined";
if (!is_browser) throw new Error(`Expected to be running in a browser`);
const { setModuleImports, getAssemblyExports, getConfig } =
await dotnet.create();
setModuleImports("main.js", {
window: {
location: {
href: () => globalThis.window.location.href
}
}
});
const config = getConfig();
const exports = await getAssemblyExports(config.mainAssemblyName);
const text = exports.MyClass.Greeting();
console.log(text);
document.getElementById("out").innerHTML = text;
await dotnet.run();
Чтобы импортировать JS функцию, чтобы ее можно было вызвать из C#, используйте новую JSImportAttribute сигнатуру соответствующего метода. Первым параметром JSImportAttribute является имя функции для JS импорта, а второй параметр — имя модуля.
В следующем примере window.location.href
функция вызывается из main.js
модуля при GetHRef
вызове метода:
[JSImport("window.location.href", "main.js")]
internal static partial string GetHRef();
В импортированной сигнатуре метода можно использовать типы .NET для параметров и возвращаемых значений, которые автоматически маршалируются средой выполнения. Используется JSMarshalAsAttribute<T> для управления маршаллами импортированных параметров метода. Например, можно выбрать маршалировать как long
System.Runtime.InteropServices.JavaScript.JSType.Number или System.Runtime.InteropServices.JavaScript.JSType.BigInt. Обратные вызовы можно передавать Action/Func<TResult> в качестве параметров, которые маршалируются как вызываемые JS функции. Вы можете передавать JS как ссылки на управляемые объекты, так и маршалируются как прокси-объекты, сохраняя объект в живых по границе, пока прокси-сервер не собирает мусор. Вы также можете импортировать и экспортировать асинхронные методы с результатом Task , которые маршалируются в качестве JS обещаний. Большинство маршаллированных типов работают в обоих направлениях в качестве параметров и возвращаемых значений в импортированных и экспортированных методах.
В следующей таблице указаны поддерживаемые сопоставления типов.
.NET | JavaScript | Nullable |
Task ➔toPromise |
JSMarshalAs Дополнительные |
Array of |
---|---|---|---|---|---|
Boolean |
Boolean |
Поддерживается | Поддерживается | Поддерживается | Не поддерживаются |
Byte |
Number |
Поддерживается | Поддерживается | Поддерживается | Поддерживается |
Char |
String |
Поддерживается | Поддерживается | Поддерживается | Не поддерживаются |
Int16 |
Number |
Поддерживается | Поддерживается | Поддерживается | Не поддерживаются |
Int32 |
Number |
Поддерживается | Поддерживается | Поддерживается | Поддерживается |
Int64 |
Number |
Поддерживается | Поддерживается | Не поддерживаются | Не поддерживаются |
Int64 |
BigInt |
Поддерживается | Поддерживается | Не поддерживаются | Не поддерживаются |
Single |
Number |
Поддерживается | Поддерживается | Поддерживается | Не поддерживаются |
Double |
Number |
Поддерживается | Поддерживается | Поддерживается | Поддерживается |
IntPtr |
Number |
Поддерживается | Поддерживается | Поддерживается | Не поддерживаются |
DateTime |
Date |
Поддерживается | Поддерживается | Не поддерживаются | Не поддерживаются |
DateTimeOffset |
Date |
Поддерживается | Поддерживается | Не поддерживаются | Не поддерживаются |
Exception |
Error |
Не поддерживаются | Поддерживается | Поддерживается | Не поддерживаются |
JSObject |
Object |
Не поддерживаются | Поддерживается | Поддерживается | Поддерживается |
String |
String |
Не поддерживаются | Поддерживается | Поддерживается | Поддерживается |
Object |
Any |
Не поддерживаются | Поддерживается | Не поддерживаются | Поддерживается |
Span<Byte> |
MemoryView |
Не поддерживаются | Не поддерживаются | Не поддерживаются | Не поддерживаются |
Span<Int32> |
MemoryView |
Не поддерживаются | Не поддерживаются | Не поддерживаются | Не поддерживаются |
Span<Double> |
MemoryView |
Не поддерживаются | Не поддерживаются | Не поддерживаются | Не поддерживаются |
ArraySegment<Byte> |
MemoryView |
Не поддерживаются | Не поддерживаются | Не поддерживаются | Не поддерживаются |
ArraySegment<Int32> |
MemoryView |
Не поддерживаются | Не поддерживаются | Не поддерживаются | Не поддерживаются |
ArraySegment<Double> |
MemoryView |
Не поддерживаются | Не поддерживаются | Не поддерживаются | Не поддерживаются |
Task |
Promise |
Не поддерживаются | Не поддерживаются | Поддерживается | Не поддерживаются |
Action |
Function |
Не поддерживаются | Не поддерживаются | Не поддерживаются | Не поддерживаются |
Action<T1> |
Function |
Не поддерживаются | Не поддерживаются | Не поддерживаются | Не поддерживаются |
Action<T1, T2> |
Function |
Не поддерживаются | Не поддерживаются | Не поддерживаются | Не поддерживаются |
Action<T1, T2, T3> |
Function |
Не поддерживаются | Не поддерживаются | Не поддерживаются | Не поддерживаются |
Func<TResult> |
Function |
Не поддерживаются | Не поддерживаются | Не поддерживаются | Не поддерживаются |
Func<T1, TResult> |
Function |
Не поддерживаются | Не поддерживаются | Не поддерживаются | Не поддерживаются |
Func<T1, T2, TResult> |
Function |
Не поддерживаются | Не поддерживаются | Не поддерживаются | Не поддерживаются |
Func<T1, T2, T3, TResult> |
Function |
Не поддерживаются | Не поддерживаются | Не поддерживаются | Не поддерживаются |
Следующие условия применяются к сопоставлению типов и маршализированным значениям:
- Столбец
Array of
указывает, можно ли маршалировать тип .NET в виде JSArray
. Пример: C#int[]
(Int32
) сопоставлен с JSArray
Number
s. - При передаче JS значения в C# со значением неправильного типа платформа создает исключение в большинстве случаев. Платформа не выполняет тип времени компиляции проверка в JS.
JSObject
Task
,Exception
и созданиеGCHandle
иArraySegment
прокси-сервер. Вы можете активировать удаление в коде разработчика или разрешить сборку мусора .NET (GC) удалять объекты позже. Эти типы несут значительные затраты на производительность.Array
: маршалинг массива создает копию массива в JS или .NET.MemoryView
MemoryView
JS— это класс среды выполнения .NET WebAssembly для маршалированияSpan
иArraySegment
.- В отличие от маршалинга массива, маршалинг или
Span
ArraySegment
не создает копию базовой памяти. MemoryView
может быть правильно создано средой выполнения .NET WebAssembly. Поэтому невозможно импортировать JS функцию как метод .NET, имеющий параметрSpan
илиArraySegment
.MemoryView
создается только дляSpan
срока вызова взаимодействия. КакSpan
и в стеке вызовов, который не сохраняется после вызова взаимодействия, невозможно экспортировать метод .NET, который возвращаетSpan
.MemoryView
создано дляArraySegment
выживания после вызова взаимодействия и полезно для совместного использования буфера. Вызовdispose()
созданногоMemoryView
ArraySegment
для удаления прокси-сервера и открепляет базовый массив .NET. Рекомендуется вызыватьdispose()
блокtry-finally
дляMemoryView
.
Функции, доступные в глобальном пространстве имен, можно импортировать с помощью globalThis
префикса в имени функции и с помощью атрибута [JSImport]
без предоставления имени модуля. В следующем примере console.log
префикс с префиксом globalThis
. Импортированная функция вызывается методом C#Log
, который принимает строковое сообщение C# (message
) и маршалирует строку C# дляString
JSconsole.log
:
[JSImport("globalThis.console.log")]
internal static partial void Log([JSMarshalAs<JSType.String>] string message);
Чтобы экспортировать метод .NET, чтобы его можно было вызвать из JS, используйте метод JSExportAttribute.
В следующем примере Greeting
метод возвращает строку, содержащую результат вызова GetHRef
метода. Как показано ранее, GetHref
метод C# вызывает JSwindow.location.href
функцию из main.js
модуля. window.location.href
возвращает текущий адрес страницы (URL-адрес):
[JSExport]
internal static string Greeting()
{
var text = $"Hello, World! Greetings from {GetHRef()}";
Console.WriteLine(text);
return text;
}
Экспериментальная рабочая нагрузка и шаблоны проектов
Чтобы продемонстрировать функциональные JS возможности взаимодействия и получить JS шаблоны проектов взаимодействия, установите рабочую нагрузку wasm-experimental
:
dotnet workload install wasm-experimental
wasm-experimental
Рабочая нагрузка содержит два шаблона проекта: wasmbrowser
и wasmconsole
. Эти шаблоны являются экспериментальными в настоящее время, что означает, что рабочий процесс разработчика для шаблонов развивается. Однако api и .NET JS , используемые в шаблонах, поддерживаются в .NET 7 и предоставляют основу для использования .NET в WASMJS.
Приложение браузера
Вы можете создать приложение браузера с wasmbrowser
шаблоном, которое создает веб-приложение, которое демонстрирует использование .NET и JS вместе в браузере:
dotnet new wasmbrowser
Создайте приложение из Visual Studio или с помощью .NET CLI:
dotnet build
Встроенное приложение находится в каталоге bin/{BUILD CONFIGURATION}/{TARGET FRAMEWORK}/browser-wasm/AppBundle
. Заполнитель {BUILD CONFIGURATION}
— это конфигурация сборки (например, Debug
, Release
). Заполнитель {TARGET FRAMEWORK}
— это моникер целевой платформы (например, net7.0
).
Создайте и запустите приложение из Visual Studio или с помощью .NET CLI:
dotnet run
Кроме того, запустите любой статический файловый сервер из AppBundle
каталога:
dotnet serve -d:bin/$(Configuration)/{TARGET FRAMEWORK}/browser-wasm/AppBundle
В предыдущем примере {TARGET FRAMEWORK}
заполнитель — это моникер целевой платформы (например, net7.0
).
Консольное приложение Node.js
Консольное приложение можно создать с wasmconsole
помощью шаблона, которое создает приложение, которое выполняется в WASM качестве консольного приложения Node.js или версии 8 :
dotnet new wasmconsole
Создайте приложение из Visual Studio или с помощью .NET CLI:
dotnet build
Встроенное приложение находится в каталоге bin/{BUILD CONFIGURATION}/{TARGET FRAMEWORK}/browser-wasm/AppBundle
. Заполнитель {BUILD CONFIGURATION}
— это конфигурация сборки (например, Debug
, Release
). Заполнитель {TARGET FRAMEWORK}
— это моникер целевой платформы (например, net7.0
).
Создайте и запустите приложение из Visual Studio или с помощью .NET CLI:
dotnet run
Кроме того, запустите любой статический файловый сервер из AppBundle
каталога:
node bin/$(Configuration)/{TARGET FRAMEWORK}/browser-wasm/AppBundle/main.mjs
В предыдущем примере {TARGET FRAMEWORK}
заполнитель — это моникер целевой платформы (например, net7.0
).
Дополнительные ресурсы
- Настройка и размещение приложений .NET WebAssembly
- Документация по API
- Взаимодействие импорта иJS экспорта JavaScript JSс ASP.NET CoreBlazor
- В репозитории
dotnet/runtime
GitHub: - Использование .NET из любого приложения JavaScript в .NET 7
ASP.NET Core
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по