Запуск .NET из JavaScript
В этой статье объясняется, как запустить .NET из JavaScript (JS) с помощью/JS[JSImport]
[JSExport]
взаимодействия.
Дополнительные рекомендации см. в руководстве по настройке и размещению приложений .NET WebAssembly в репозитории GitHub .NET Runtime (dotnet/runtime
).
Существующие JS приложения могут использовать расширенную поддержку WebAssembly на стороне клиента для повторного использования библиотек .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
Средства также можно установить с помощью установщика Visual Studio в ASP.NET и рабочей нагрузке веб-разработки в установщике Visual Studio. Выберите параметр средств сборки .NET WebAssembly из списка необязательных компонентов.
При необходимости установите wasm-experimental
рабочую нагрузку, содержащую экспериментальные шаблоны проектов для начала работы с .NET в WebAssembly в приложении браузера (приложение webAssembly Browser) или в консольном приложении на основе Node.js (консольное приложение WebAssembly). Эта рабочая нагрузка не требуется, если планируется интегрировать JS[JSExport]
[JSImport]
/взаимодействие в существующее JS приложение.
dotnet workload install wasm-experimental
Шаблоны также можно установить из пакета NuGet с помощью следующей Microsoft.NET.Runtime.WebAssembly.Templates
команды:
dotnet new install Microsoft.NET.Runtime.WebAssembly.Templates
Дополнительные сведения см. в разделе " Экспериментальная рабочая нагрузка и шаблоны проектов ".
Пространство имен
API взаимодействия, описанный JS в этой статье, управляется атрибутами в System.Runtime.InteropServices.JavaScript пространстве имен.
Конфигурация проекта
Чтобы настроить проект (.csproj
) для включения JS взаимодействия:
Задайте моникер целевой платформы (
{TARGET FRAMEWORK}
заполнитель):<TargetFramework>{TARGET FRAMEWORK}</TargetFramework>
Поддерживается .NET 7 (
net7.0
) или более поздней версии.AllowUnsafeBlocks Включите свойство, которое позволяет генератору кода в компиляторе Roslyn использовать указатели для JS взаимодействия:
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
Предупреждение
JS API взаимодействия требует включенияAllowUnsafeBlocks. Будьте осторожны при реализации собственного небезопасного кода в приложениях .NET, что может привести к рискам безопасности и стабильности. Дополнительные сведения см. в разделе "Небезопасный код", "типы указателей" и указатели функций.
Ниже приведен пример файла проекта (.csproj
) после настройки. Заполнитель {TARGET FRAMEWORK}
— это целевая платформа:
<Project Sdk="Microsoft.NET.Sdk.WebAssembly">
<PropertyGroup>
<TargetFramework>{TARGET FRAMEWORK}</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>
Задайте моникер целевой платформы:
<TargetFramework>net7.0</TargetFramework>
Поддерживается .NET 7 (
net7.0
) или более поздней версии.Укажите
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
создается в рамках выходных данных сборки приложения.Внимание
Чтобы интегрироваться с существующим приложением, скопируйте содержимое выходной папки публикации† в ресурсы развертывания существующего приложения, чтобы его можно было обслуживать вместе с остальной частью приложения. Для рабочих развертываний опубликуйте приложение с
dotnet publish -c Release
помощью команды в командной оболочке и разверните содержимое выходной папки с помощью приложения.† Папка вывода публикации — это целевое расположение профиля публикации. По умолчанию для Release профиля в .NET 8 или более поздней версии используется
bin/Release/{TARGET FRAMEWORK}/publish
{TARGET FRAMEWORK}
заполнитель — целевая платформа (например,net8.0
).dotnet.create()
настраивает среду выполнения .NET WebAssembly.
setModuleImports
связывает имя с модулем функций для импорта JS в .NET. Модуль JS содержитdom.setInnerText
функцию, которая принимает и селектор элементов и время для отображения текущего времени стоп-часов в пользовательском интерфейсе. Имя модуля может быть любой строкой (она не должна быть именем файла), но она должна соответствовать имени, используемому сJSImportAttribute
(описано далее в этой статье). Функцияdom.setInnerText
импортируется в C# и вызывается методомSetInnerText
C#. ЭтотSetInnerText
метод показан далее в этом разделе.exports.StopwatchSample.Reset()
вызовы в .NET (StopwatchSample.Reset
) из JS. МетодReset
C# перезапускает стоп-часы, если он запущен или сбрасывает его, если он не запущен. ЭтотReset
метод показан далее в этом разделе.exports.StopwatchSample.Toggle()
вызовы в .NET (StopwatchSample.Toggle
) из JS. МетодToggle
C# запускает или останавливает стоп-часы в зависимости от того, запущен ли он в данный момент или нет. ЭтотToggle
метод показан далее в этом разделе.runMain()
выполняетсяProgram.Main
.
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 './_framework/dotnet.js'
const { setModuleImports, getAssemblyExports, getConfig, runMain } = await dotnet
.withApplicationArguments("start")
.create();
setModuleImports('main.js', {
dom: {
setInnerText: (selector, time) =>
document.querySelector(selector).innerText = time
}
});
const config = getConfig();
const exports = await getAssemblyExports(config.mainAssemblyName);
document.getElementById('reset').addEventListener('click', e => {
exports.StopwatchSample.Reset();
e.preventDefault();
});
const pauseButton = document.getElementById('pause');
pauseButton.addEventListener('click', e => {
const isRunning = exports.StopwatchSample.Toggle();
pauseButton.innerText = isRunning ? 'Pause' : 'Start';
e.preventDefault();
});
await runMain();
import { dotnet } from './_framework/dotnet.js'
const { setModuleImports, getAssemblyExports, getConfig } = await dotnet
.withDiagnosticTracing(false)
.withApplicationArgumentsFromQuery()
.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();
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 импорта, а второй параметр — имя модуля.
В следующем примере dom.setInnerText
функция вызывается из main.js
модуля при SetInnerText
вызове метода:
[JSImport("dom.setInnerText", "main.js")]
internal static partial void SetInnerText(string selector, string content);
В следующем примере 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 ➔Кому Promise |
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.
В следующем примере каждый метод экспортируется JS в функции и может вызываться из JS функций:
- Метод
Toggle
запускает или останавливает секундомер в зависимости от состояния выполнения. - Метод
Reset
перезапускает стоп-часы, если он запущен или сбрасывает его, если он не запущен. - Метод
IsRunning
указывает, запущен ли стоп-часы.
[JSExport]
internal static bool Toggle()
{
if (stopwatch.IsRunning)
{
stopwatch.Stop();
return false;
}
else
{
stopwatch.Start();
return true;
}
}
[JSExport]
internal static void Reset()
{
if (stopwatch.IsRunning)
stopwatch.Restart();
else
stopwatch.Reset();
Render();
}
[JSExport]
internal static bool IsRunning() => stopwatch.IsRunning;
В следующем примере Greeting
метод возвращает строку, содержащую результат вызова GetHRef
метода. Как показано ранее, GetHref
метод C# вызывает JS window.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 8 и предоставляют основу для использования .NET в WASM JS.
Шаблоны также можно установить из пакета NuGet с помощью следующей Microsoft.NET.Runtime.WebAssembly.Templates
команды:
dotnet new install Microsoft.NET.Runtime.WebAssembly.Templates
Приложение браузера
Вы можете создать приложение браузера с wasmbrowser
шаблоном из командной строки, которое создает веб-приложение, демонстрирующее использование .NET и JS вместе в браузере:
dotnet new wasmbrowser
Кроме того, в Visual Studio можно создать приложение с помощью WebAssembly Browser App шаблона проекта.
Создайте приложение из Visual Studio или с помощью .NET CLI:
dotnet build
Создайте и запустите приложение из Visual Studio или с помощью .NET CLI:
dotnet run
Кроме того, установите и используйте dotnet serve
команду:
dotnet serve -d:bin/$(Configuration)/{TARGET FRAMEWORK}/publish
В предыдущем примере {TARGET FRAMEWORK}
заполнитель — это моникер целевой платформы.
Консольное приложение Node.js
Консольное приложение можно создать с wasmconsole
помощью шаблона, которое создает приложение, которое выполняется в WASM качестве консольного приложения Node.js или версии 8 :
dotnet new wasmconsole
Кроме того, в Visual Studio можно создать приложение с помощью WebAssembly Console App шаблона проекта.
Создайте приложение из Visual Studio или с помощью .NET CLI:
dotnet build
Создайте и запустите приложение из Visual Studio или с помощью .NET CLI:
dotnet run
Кроме того, запустите любой статический файловый сервер из выходного каталога публикации, содержащего main.mjs
файл:
node bin/$(Configuration)/{TARGET FRAMEWORK}/{PATH}/main.mjs
В предыдущем примере {TARGET FRAMEWORK}
заполнитель — это моникер целевой платформы, а {PATH}
заполнитель — путь к файлу main.mjs
.
Дополнительные ресурсы
- Настройка и размещение приложений .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 жүйесін мазмұнға арналған кері байланыс механизмі ретінде біртіндеп қолданыстан шығарамыз және оны жаңа кері байланыс жүйесімен ауыстырамыз. Қосымша ақпаратты мұнда қараңыз:Жіберу және пікірді көру