Wywoływanie funkcji JavaScript z metod platformy .NET w programie ASP.NET Core Blazor
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
Ostrzeżenie
Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz .NET i .NET Core Support Policy (Zasady obsługi platformy .NET Core). Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
W tym artykule wyjaśniono, jak wywoływać funkcje języka JavaScript (JS) z platformy .NET.
Aby uzyskać informacje na temat wywoływania metod .NET z JSprogramu , zobacz Wywoływanie metod .NET z funkcji Języka JavaScript w programie ASP.NET Core Blazor.
Wywoływanie JS funkcji
IJSRuntime program jest zarejestrowany przez platformę Blazor . Aby wywołać metodę z JS platformy .NET, wstrzyknąć IJSRuntime abstrakcję i wywołać jedną z następujących metod:
W przypadku powyższych metod platformy .NET, które wywołują JS funkcje:
- Identyfikator funkcji (
String
) jest względny względem zakresu globalnego (window
). Aby wywołaćwindow.someScope.someFunction
metodę , identyfikator tosomeScope.someFunction
. Nie ma potrzeby rejestrowania funkcji przed jej wywołaniami. - Przekaż dowolną JS liczbę argumentów z możliwością serializacji JSON do
Object[]
funkcji. - Token anulowania (
CancellationToken
) propaguje powiadomienie, że operacje powinny zostać anulowane. TimeSpan
reprezentuje limit czasu dla JS operacji.- Zwracany
TValue
typ musi być również serializowalny w formacie JSON.TValue
powinien być zgodny z typem platformy .NET, który najlepiej mapuje zwracany typ JSON. - Element A JS
Promise
jest zwracany dlaInvokeAsync
metod.InvokeAsync
odpakowujePromise
element i zwraca wartość oczekiwanąPromise
przez element .
W przypadku Blazor aplikacji z włączonym prerenderingiem, które jest ustawieniem domyślnym dla aplikacji po stronie serwera, wywoływanie metody nie JS jest możliwe podczas prerenderingu. Aby uzyskać więcej informacji, zobacz sekcję Prerendering .
Poniższy przykład jest oparty na dekoderze opartym na TextDecoder
metodzie JS. W tym przykładzie pokazano, jak wywołać JS funkcję z metody języka C#, która odciąża wymaganie od kodu dewelopera do istniejącego JS interfejsu API. Funkcja JS akceptuje tablicę bajtów z metody języka C#, dekoduje tablicę i zwraca tekst do składnika do wyświetlania.
<script>
window.convertArray = (win1251Array) => {
var win1251decoder = new TextDecoder('windows-1251');
var bytes = new Uint8Array(win1251Array);
var decodedArray = win1251decoder.decode(bytes);
return decodedArray;
};
</script>
Uwaga
Aby uzyskać ogólne wskazówki dotyczące JS lokalizacji i naszych zaleceń dotyczących aplikacji produkcyjnych, zobacz Lokalizacja języka JavaScript w aplikacjach ASP.NET CoreBlazor.
Następujący składnik:
convertArray
JS Wywołuje funkcję za InvokeAsync pomocą polecenia podczas wybierania przycisku (Convert Array
).- Po wywołaniu funkcji przekazana JS tablica jest konwertowana na ciąg. Ciąg jest zwracany do składnika do wyświetlania (
text
).
CallJs1.razor
:
@page "/call-js-1"
@inject IJSRuntime JS
<PageTitle>Call JS 1</PageTitle>
<h1>Call JS Example 1</h1>
<p>
<button @onclick="ConvertArray">Convert Array</button>
</p>
<p>
@text
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0472710/">David Krumholtz on IMDB</a>
</p>
@code {
private MarkupString text;
private uint[] quoteArray =
new uint[]
{
60, 101, 109, 62, 67, 97, 110, 39, 116, 32, 115, 116, 111, 112, 32,
116, 104, 101, 32, 115, 105, 103, 110, 97, 108, 44, 32, 77, 97,
108, 46, 60, 47, 101, 109, 62, 32, 45, 32, 77, 114, 46, 32, 85, 110,
105, 118, 101, 114, 115, 101, 10, 10,
};
private async Task ConvertArray() =>
text = new(await JS.InvokeAsync<string>("convertArray", quoteArray));
}
CallJsExample1.razor
:
@page "/call-js-example-1"
@inject IJSRuntime JS
<h1>Call JS <code>convertArray</code> Function</h1>
<p>
<button @onclick="ConvertArray">Convert Array</button>
</p>
<p>
@text
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0472710/">David Krumholtz on IMDB</a>
</p>
@code {
private MarkupString text;
private uint[] quoteArray =
new uint[]
{
60, 101, 109, 62, 67, 97, 110, 39, 116, 32, 115, 116, 111, 112, 32,
116, 104, 101, 32, 115, 105, 103, 110, 97, 108, 44, 32, 77, 97,
108, 46, 60, 47, 101, 109, 62, 32, 45, 32, 77, 114, 46, 32, 85, 110,
105, 118, 101, 114, 115, 101, 10, 10,
};
private async Task ConvertArray()
{
text = new(await JS.InvokeAsync<string>("convertArray", quoteArray));
}
}
CallJsExample1.razor
:
@page "/call-js-example-1"
@inject IJSRuntime JS
<h1>Call JS <code>convertArray</code> Function</h1>
<p>
<button @onclick="ConvertArray">Convert Array</button>
</p>
<p>
@text
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0472710/">David Krumholtz on IMDB</a>
</p>
@code {
private MarkupString text;
private uint[] quoteArray =
new uint[]
{
60, 101, 109, 62, 67, 97, 110, 39, 116, 32, 115, 116, 111, 112, 32,
116, 104, 101, 32, 115, 105, 103, 110, 97, 108, 44, 32, 77, 97,
108, 46, 60, 47, 101, 109, 62, 32, 45, 32, 77, 114, 46, 32, 85, 110,
105, 118, 101, 114, 115, 101, 10, 10,
};
private async Task ConvertArray()
{
text = new(await JS.InvokeAsync<string>("convertArray", quoteArray));
}
}
CallJsExample1.razor
:
@page "/call-js-example-1"
@inject IJSRuntime JS
<h1>Call JS <code>convertArray</code> Function</h1>
<p>
<button @onclick="ConvertArray">Convert Array</button>
</p>
<p>
@text
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0472710/">David Krumholtz on IMDB</a>
</p>
@code {
private MarkupString text;
private uint[] quoteArray =
new uint[]
{
60, 101, 109, 62, 67, 97, 110, 39, 116, 32, 115, 116, 111, 112, 32,
116, 104, 101, 32, 115, 105, 103, 110, 97, 108, 44, 32, 77, 97,
108, 46, 60, 47, 101, 109, 62, 32, 45, 32, 77, 114, 46, 32, 85, 110,
105, 118, 101, 114, 115, 101, 10, 10,
};
private async Task ConvertArray()
{
text = new(await JS.InvokeAsync<string>("convertArray", quoteArray));
}
}
CallJsExample1.razor
:
@page "/call-js-example-1"
@inject IJSRuntime JS
<h1>Call JS <code>convertArray</code> Function</h1>
<p>
<button @onclick="ConvertArray">Convert Array</button>
</p>
<p>
@text
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0472710/">David Krumholtz on IMDB</a>
</p>
@code {
private MarkupString text;
private uint[] quoteArray =
new uint[]
{
60, 101, 109, 62, 67, 97, 110, 39, 116, 32, 115, 116, 111, 112, 32,
116, 104, 101, 32, 115, 105, 103, 110, 97, 108, 44, 32, 77, 97,
108, 46, 60, 47, 101, 109, 62, 32, 45, 32, 77, 114, 46, 32, 85, 110,
105, 118, 101, 114, 115, 101, 10, 10,
};
private async Task ConvertArray()
{
text = new MarkupString(await JS.InvokeAsync<string>("convertArray",
quoteArray));
}
}
Interfejs API języka JavaScript ograniczony do gestów użytkownika
Ta sekcja dotyczy składników po stronie serwera.
Niektóre interfejsy API języka JavaScript (JS) przeglądarki można wykonywać tylko w kontekście gestu użytkownika, takiego jak użycie Fullscreen API
(dokumentacja MDN). Te interfejsy API nie mogą być wywoływane za pośrednictwem JS mechanizmu międzyoperacyjnego w składnikach po stronie serwera, ponieważ obsługa zdarzeń interfejsu użytkownika jest wykonywana asynchronicznie i ogólnie nie jest już w kontekście gestu użytkownika. Aplikacja musi obsługiwać zdarzenie interfejsu użytkownika całkowicie w języku JavaScript, dlatego należy użyć onclick
zamiast Blazoratrybutu @onclick
dyrektywy .
Wywoływanie funkcji Języka JavaScript bez odczytywania zwróconej wartości (InvokeVoidAsync
)
Użyj InvokeVoidAsync polecenia , gdy:
- Platforma .NET nie jest wymagana do odczytania wyniku wywołania języka JavaScript (JS).
- JS funkcje zwracają void(0)/void 0 lub niezdefiniowane.
displayTickerAlert1
JS Podaj funkcję. Funkcja jest wywoływana z elementem InvokeVoidAsync i nie zwraca wartości:
<script>
window.displayTickerAlert1 = (symbol, price) => {
alert(`${symbol}: $${price}!`);
};
</script>
Uwaga
Aby uzyskać ogólne wskazówki dotyczące JS lokalizacji i naszych zaleceń dotyczących aplikacji produkcyjnych, zobacz Lokalizacja języka JavaScript w aplikacjach ASP.NET CoreBlazor.
Przykład składnika (.razor
) (InvokeVoidAsync
)
TickerChanged
wywołuje metodę handleTickerChanged1
w następującym składniku.
CallJs2.razor
:
@page "/call-js-2"
@inject IJSRuntime JS
<PageTitle>Call JS 2</PageTitle>
<h1>Call JS Example 2</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private string? stockSymbol;
private decimal price;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}" +
$"{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
await JS.InvokeVoidAsync("displayTickerAlert1", stockSymbol, price);
}
}
CallJsExample2.razor
:
@page "/call-js-example-2"
@inject IJSRuntime JS
<h1>Call JS Example 2</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private string? stockSymbol;
private decimal price;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
await JS.InvokeVoidAsync("displayTickerAlert1", stockSymbol, price);
}
}
CallJsExample2.razor
:
@page "/call-js-example-2"
@inject IJSRuntime JS
<h1>Call JS Example 2</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private string? stockSymbol;
private decimal price;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
await JS.InvokeVoidAsync("displayTickerAlert1", stockSymbol, price);
}
}
CallJsExample2.razor
:
@page "/call-js-example-2"
@inject IJSRuntime JS
<h1>Call JS Example 2</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private Random r = new();
private string stockSymbol;
private decimal price;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
await JS.InvokeVoidAsync("displayTickerAlert1", stockSymbol, price);
}
}
CallJsExample2.razor
:
@page "/call-js-example-2"
@inject IJSRuntime JS
<h1>Call JS Example 2</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol != null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private Random r = new Random();
private string stockSymbol;
private decimal price;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
await JS.InvokeVoidAsync("displayTickerAlert1", stockSymbol, price);
}
}
Przykład klasy (.cs
) (InvokeVoidAsync
)
JsInteropClasses1.cs
:
using Microsoft.JSInterop;
namespace BlazorSample;
public class JsInteropClasses1(IJSRuntime js) : IDisposable
{
private readonly IJSRuntime js = js;
public async ValueTask TickerChanged(string symbol, decimal price) =>
await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
// Calling SuppressFinalize(this) prevents derived types that introduce
// a finalizer from needing to re-implement IDisposable.
public void Dispose() => GC.SuppressFinalize(this);
}
using Microsoft.JSInterop;
public class JsInteropClasses1 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses1(IJSRuntime js)
{
this.js = js;
}
public async ValueTask TickerChanged(string symbol, decimal price)
{
await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
}
public void Dispose()
{
}
}
using Microsoft.JSInterop;
public class JsInteropClasses1 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses1(IJSRuntime js)
{
this.js = js;
}
public async ValueTask TickerChanged(string symbol, decimal price)
{
await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
}
public void Dispose()
{
}
}
using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class JsInteropClasses1 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses1(IJSRuntime js)
{
this.js = js;
}
public async ValueTask TickerChanged(string symbol, decimal price)
{
await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
}
public void Dispose()
{
}
}
using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class JsInteropClasses1 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses1(IJSRuntime js)
{
this.js = js;
}
public async ValueTask TickerChanged(string symbol, decimal price)
{
await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
}
public void Dispose()
{
}
}
TickerChanged
wywołuje metodę handleTickerChanged1
w następującym składniku.
CallJs3.razor
:
@page "/call-js-3"
@implements IDisposable
@inject IJSRuntime JS
<PageTitle>Call JS 3</PageTitle>
<h1>Call JS Example 3</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private string? stockSymbol;
private decimal price;
private JsInteropClasses1? jsClass;
protected override void OnInitialized() => jsClass = new(JS);
private async Task SetStock()
{
if (jsClass is not null)
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}" +
$"{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
await jsClass.TickerChanged(stockSymbol, price);
}
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample3.razor
:
@page "/call-js-example-3"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 3</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private string? stockSymbol;
private decimal price;
private JsInteropClasses1? jsClass;
protected override void OnInitialized()
{
jsClass = new(JS);
}
private async Task SetStock()
{
if (jsClass is not null)
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
await jsClass.TickerChanged(stockSymbol, price);
}
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample3.razor
:
@page "/call-js-example-3"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 3</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private string? stockSymbol;
private decimal price;
private JsInteropClasses1? jsClass;
protected override void OnInitialized()
{
jsClass = new(JS);
}
private async Task SetStock()
{
if (jsClass is not null)
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
await jsClass.TickerChanged(stockSymbol, price);
}
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample3.razor
:
@page "/call-js-example-3"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 3</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private Random r = new();
private string stockSymbol;
private decimal price;
private JsInteropClasses1 jsClass;
protected override void OnInitialized()
{
jsClass = new(JS);
}
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
await jsClass.TickerChanged(stockSymbol, price);
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample3.razor
:
@page "/call-js-example-3"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 3</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol != null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private Random r = new Random();
private string stockSymbol;
private decimal price;
private JsInteropClasses1 jsClass;
protected override void OnInitialized()
{
jsClass = new JsInteropClasses1(JS);
}
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
await jsClass.TickerChanged(stockSymbol, price);
}
public void Dispose() => jsClass?.Dispose();
}
Wywoływanie funkcji Języka JavaScript i odczytywanie zwróconej wartości (InvokeAsync
)
Użyj InvokeAsync polecenia , gdy platforma .NET powinna odczytać wynik wywołania języka JavaScript (JS).
displayTickerAlert2
JS Podaj funkcję. Poniższy przykład zwraca ciąg do wyświetlenia przez obiekt wywołujący:
<script>
window.displayTickerAlert2 = (symbol, price) => {
if (price < 20) {
alert(`${symbol}: $${price}!`);
return "User alerted in the browser.";
} else {
return "User NOT alerted.";
}
};
</script>
Uwaga
Aby uzyskać ogólne wskazówki dotyczące JS lokalizacji i naszych zaleceń dotyczących aplikacji produkcyjnych, zobacz Lokalizacja języka JavaScript w aplikacjach ASP.NET CoreBlazor.
Przykład składnika (.razor
) (InvokeAsync
)
TickerChanged
wywołuje metodę handleTickerChanged2
i wyświetla zwrócony ciąg w następującym składniku.
CallJs4.razor
:
@page "/call-js-4"
@inject IJSRuntime JS
<PageTitle>Call JS 4</PageTitle>
<h1>Call JS Example 4</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private string? stockSymbol;
private decimal price;
private string? result;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}" +
$"{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
var interopResult =
await JS.InvokeAsync<string>("displayTickerAlert2", stockSymbol, price);
result = $"Result of TickerChanged call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
CallJsExample4.razor
:
@page "/call-js-example-4"
@inject IJSRuntime JS
<h1>Call JS Example 4</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private string? stockSymbol;
private decimal price;
private string? result;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
var interopResult =
await JS.InvokeAsync<string>("displayTickerAlert2", stockSymbol, price);
result = $"Result of TickerChanged call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
CallJsExample4.razor
:
@page "/call-js-example-4"
@inject IJSRuntime JS
<h1>Call JS Example 4</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private string? stockSymbol;
private decimal price;
private string? result;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
var interopResult =
await JS.InvokeAsync<string>("displayTickerAlert2", stockSymbol, price);
result = $"Result of TickerChanged call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
CallJsExample4.razor
:
@page "/call-js-example-4"
@inject IJSRuntime JS
<h1>Call JS Example 4</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private Random r = new();
private string stockSymbol;
private decimal price;
private string result;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
var interopResult =
await JS.InvokeAsync<string>("displayTickerAlert2", stockSymbol, price);
result = $"Result of TickerChanged call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
CallJsExample4.razor
:
@page "/call-js-example-4"
@inject IJSRuntime JS
<h1>Call JS Example 4</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol != null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result != null)
{
<p>@result</p>
}
@code {
private Random r = new Random();
private string stockSymbol;
private decimal price;
private string result;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
var interopResult =
await JS.InvokeAsync<string>("displayTickerAlert2", stockSymbol, price);
result = $"Result of TickerChanged call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
Przykład klasy (.cs
) (InvokeAsync
)
JsInteropClasses2.cs
:
using Microsoft.JSInterop;
namespace BlazorSample;
public class JsInteropClasses2(IJSRuntime js) : IDisposable
{
private readonly IJSRuntime js = js;
public async ValueTask<string> TickerChanged(string symbol, decimal price) =>
await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
// Calling SuppressFinalize(this) prevents derived types that introduce
// a finalizer from needing to re-implement IDisposable.
public void Dispose() => GC.SuppressFinalize(this);
}
using Microsoft.JSInterop;
public class JsInteropClasses2 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses2(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> TickerChanged(string symbol, decimal price)
{
return await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
}
public void Dispose()
{
}
}
using Microsoft.JSInterop;
public class JsInteropClasses2 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses2(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> TickerChanged(string symbol, decimal price)
{
return await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
}
public void Dispose()
{
}
}
using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class JsInteropClasses2 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses2(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> TickerChanged(string symbol, decimal price)
{
return await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
}
public void Dispose()
{
}
}
using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class JsInteropClasses2 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses2(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> TickerChanged(string symbol, decimal price)
{
return await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
}
public void Dispose()
{
}
}
TickerChanged
wywołuje metodę handleTickerChanged2
i wyświetla zwrócony ciąg w następującym składniku.
CallJs5.razor
:
@page "/call-js-5"
@implements IDisposable
@inject IJSRuntime JS
<PageTitle>Call JS 5</PageTitle>
<h1>Call JS Example 5</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private string? stockSymbol;
private decimal price;
private JsInteropClasses2? jsClass;
private string? result;
protected override void OnInitialized() => jsClass = new(JS);
private async Task SetStock()
{
if (jsClass is not null)
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}" +
$"{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
var interopResult = await jsClass.TickerChanged(stockSymbol, price);
result = $"Result of TickerChanged call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample5.razor
:
@page "/call-js-example-5"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 5</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private string? stockSymbol;
private decimal price;
private JsInteropClasses2? jsClass;
private string? result;
protected override void OnInitialized()
{
jsClass = new(JS);
}
private async Task SetStock()
{
if (jsClass is not null)
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
var interopResult = await jsClass.TickerChanged(stockSymbol, price);
result = $"Result of TickerChanged call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample5.razor
:
@page "/call-js-example-5"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 5</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private string? stockSymbol;
private decimal price;
private JsInteropClasses2? jsClass;
private string? result;
protected override void OnInitialized()
{
jsClass = new(JS);
}
private async Task SetStock()
{
if (jsClass is not null)
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
var interopResult = await jsClass.TickerChanged(stockSymbol, price);
result = $"Result of TickerChanged call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample5.razor
:
@page "/call-js-example-5"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 5</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private Random r = new();
private string stockSymbol;
private decimal price;
private JsInteropClasses2 jsClass;
private string result;
protected override void OnInitialized()
{
jsClass = new(JS);
}
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
var interopResult = await jsClass.TickerChanged(stockSymbol, price);
result = $"Result of TickerChanged call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample5.razor
:
@page "/call-js-example-5"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 5</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol != null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result != null)
{
<p>@result</p>
}
@code {
private Random r = new Random();
private string stockSymbol;
private decimal price;
private JsInteropClasses2 jsClass;
private string result;
protected override void OnInitialized()
{
jsClass = new JsInteropClasses2(JS);
}
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
var interopResult = await jsClass.TickerChanged(stockSymbol, price);
result = $"Result of TickerChanged call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
public void Dispose() => jsClass?.Dispose();
}
Scenariusze dynamicznego generowania zawartości
W przypadku dynamicznego generowania zawartości za pomocą elementu BuildRenderTree użyj atrybutu [Inject]
:
[Inject]
IJSRuntime JS { get; set; }
Prerendering
Ta sekcja dotyczy aplikacji po stronie serwera, które prerender Razor składniki. Prerendering jest omówiony w składnikach prerender ASP.NET CoreRazor.
Uwaga
Nawigacja wewnętrzna na potrzeby routingu interakcyjnego w Blazor Web Appprogramie nie obejmuje żądania nowej zawartości strony z serwera. W związku z tym wstępne przetwarzanie nie występuje w przypadku żądań stron wewnętrznych. Jeśli aplikacja przyjmuje routing interakcyjny, wykonaj ponowne ładowanie pełnej strony dla przykładów składników, które pokazują zachowanie wstępne. Aby uzyskać więcej informacji, zobacz Prerender ASP.NET Core components (Składniki prerender ASP.NET CoreRazor).
Ta sekcja dotyczy aplikacji po stronie serwera i hostowanych Blazor WebAssembly aplikacji, które prerender Razor składniki. Prerendering jest omówiony w środowisku Prerender i integruje składniki ASP.NET CoreRazor.
Podczas wstępnego przetwarzania wywołanie kodu JavaScript (JS) nie jest możliwe. W poniższym przykładzie pokazano, jak używać JS międzyoperacyjności w ramach logiki inicjowania składnika w sposób zgodny z prerenderingiem.
Następująca scrollElementIntoView
funkcja:
- Przewija element do przekazanego elementu za pomocą polecenia
scrollIntoView
. - Zwraca wartość właściwości elementu
top
zgetBoundingClientRect
metody .
window.scrollElementIntoView = (element) => {
element.scrollIntoView();
return element.getBoundingClientRect().top;
}
Gdzie IJSRuntime.InvokeAsync wywołuje JS funkcję w kodzie składnika, parametr jest używany tylko w OnAfterRenderAsync metodzie cyklu życia i nie we wcześniejszej metodzie cyklu życia, ElementReference ponieważ nie ma elementu DOM HTML do momentu renderowania składnika.
StateHasChanged
(źródło referencyjne) jest wywoływane w celu ściągania rerendering składnika z nowym stanem uzyskanym z JS wywołania międzyoperacji (aby uzyskać więcej informacji, zobacz ASP.NET Renderowanie składników CoreRazor). Pętla nieskończona nie jest tworzona, ponieważ StateHasChanged jest wywoływana tylko wtedy, gdy scrollPosition
ma wartość null
.
PrerenderedInterop.razor
:
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<PageTitle>Prerendered Interop</PageTitle>
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
Powyższy przykład zanieczyszcza klienta za pomocą funkcji globalnej. Aby uzyskać lepsze podejście do aplikacji produkcyjnych, zobacz izolację języka JavaScript w modułach JavaScript.
Synchroniczne międzyoperajności JS w składnikach po stronie klienta
Ta sekcja dotyczy tylko składników po stronie klienta.
JS wywołania międzyoperacyjne są asynchroniczne, niezależnie od tego, czy wywoływany kod jest synchroniczny, czy asynchroniczny. Wywołania są asynchroniczne, aby upewnić się, że składniki są zgodne z trybami renderowania po stronie serwera i po stronie klienta. Na serwerze wszystkie JS wywołania międzyoperacyjne muszą być asynchroniczne, ponieważ są wysyłane za pośrednictwem połączenia sieciowego.
Jeśli wiesz, że składnik działa tylko w zestawie WebAssembly, możesz wykonać synchroniczne wywołania międzyoperacyjne JS . Ma to nieco mniejsze obciążenie niż wykonywanie wywołań asynchronicznych i może spowodować zmniejszenie liczby cykli renderowania, ponieważ nie ma stanu pośredniego podczas oczekiwania na wyniki.
Aby wykonać synchroniczne wywołanie z platformy .NET do języka JavaScript w składniku po stronie klienta, rzutuj IJSRuntime w celu IJSInProcessRuntime wywołania międzyoperacyjności JS :
@inject IJSRuntime JS
...
@code {
protected override void HandleSomeEvent()
{
var jsInProcess = (IJSInProcessRuntime)JS;
var value = jsInProcess.Invoke<string>("javascriptFunctionIdentifier");
}
}
Podczas pracy ze składnikami IJSObjectReference ASP.NET Core 5.0 lub nowszymi po stronie klienta można użyć IJSInProcessObjectReference synchronicznie. IJSInProcessObjectReference implementuje IAsyncDisposable/IDisposable i należy usunąć odzyskiwanie pamięci, aby zapobiec wyciekowi pamięci, jak pokazano w poniższym przykładzie:
@inject IJSRuntime JS
@implements IAsyncDisposable
...
@code {
...
private IJSInProcessObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSInProcessObjectReference>("import",
"./scripts.js");
}
}
...
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
Lokalizacja języka JavaScript
Załaduj kod JavaScript (JS) przy użyciu dowolnego podejścia opisanego w artykule dotyczącym lokalizacji języka JavaScript:
- Ładowanie skryptu w znaczniku
<head>
(zazwyczaj niezalecane) - Ładowanie skryptu w znaczniku
<body>
- Ładowanie skryptu z zewnętrznego pliku JavaScript (
.js
) znajdującego się w tej samej lokalizacji co składnik - Ładowanie skryptu z zewnętrznego pliku JavaScript (
.js
) - Wstrzykiwanie skryptu przed uruchomieniem lub po Blazor nim
Aby uzyskać informacje na temat izolowania skryptów w JS modułach, zobacz sekcję Izolacja języka JavaScript w modułach Języka JavaScript.
Ostrzeżenie
Umieść <script>
tag tylko w pliku składnika (.razor
), jeśli składnik ma gwarancję wdrożenia statycznego renderowania po stronie serwera (statyczny SSR), ponieważ <script>
tag nie może być aktualizowany dynamicznie.
Ostrzeżenie
Nie umieszczaj tagu <script>
w pliku składnika (.razor
), ponieważ <script>
tag nie może być aktualizowany dynamicznie.
Izolacja języka JavaScript w modułach języka JavaScript
Platforma Blazor włącza izolację języka JavaScript (JS) w standardowych modułach języka JavaScript (Specyfikacja ECMAScript). Ładowanie modułu JavaScript działa tak samo jak w Blazor przypadku innych typów aplikacji internetowych i możesz dostosować sposób definiowania modułów w aplikacji. Aby zapoznać się z przewodnikiem dotyczącym używania modułów Języka JavaScript, zobacz Artykuł MdN Web Docs: JavaScript modules (Dokumentacja internetowa mdN: moduły Języka JavaScript).
Izolacja języka JS zapewnia następujące korzyści:
- Zaimportowany kod JS nie zanieczyszcza już globalnej przestrzeni nazw.
- Użytkownicy biblioteki i składników nie są zobowiązani do zaimportowania powiązanego kodu JS.
Importowanie dynamiczne za pomocą import()
operatora jest obsługiwane w przypadku platformy ASP.NET Core i Blazor:
if ({CONDITION}) import("/additionalModule.js");
W poprzednim przykładzie symbol zastępczy reprezentuje sprawdzanie warunkowe, aby określić, {CONDITION}
czy moduł powinien zostać załadowany.
Aby uzyskać informacje o zgodności przeglądarki, zobacz Czy mogę używać: moduły JavaScript: import dynamiczny.
Na przykład poniższy JS moduł eksportuje JS funkcję do wyświetlania monitu o okno przeglądarki. Umieść następujący JS kod w pliku zewnętrznym JS .
wwwroot/scripts.js
:
export function showPrompt(message) {
return prompt(message, 'Type anything here');
}
Dodaj poprzedni JS moduł do biblioteki aplikacji lub klas jako statyczny zasób internetowy w wwwroot
folderze, a następnie zaimportuj moduł do kodu platformy .NET, wywołując InvokeAsync IJSRuntime wystąpienie.
IJSRuntime importuje moduł jako IJSObjectReferenceelement , który reprezentuje odwołanie do JS obiektu z kodu platformy .NET. Użyj polecenia , IJSObjectReference aby wywołać wyeksportowane JS funkcje z modułu.
CallJs6.razor
:
@page "/call-js-6"
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call JS 6</PageTitle>
<h1>Call JS Example 6</h1>
<p>
<button @onclick="TriggerPrompt">Trigger browser window prompt</button>
</p>
<p>
@result
</p>
@code {
private IJSObjectReference? module;
private string? result;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./scripts.js");
}
}
private async Task TriggerPrompt() => result = await Prompt("Provide text");
public async ValueTask<string?> Prompt(string message) =>
module is not null ?
await module.InvokeAsync<string>("showPrompt", message) : null;
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
CallJsExample6.razor
:
@page "/call-js-example-6"
@implements IAsyncDisposable
@inject IJSRuntime JS
<h1>Call JS Example 6</h1>
<p>
<button @onclick="TriggerPrompt">Trigger browser window prompt</button>
</p>
<p>
@result
</p>
@code {
private IJSObjectReference? module;
private string? result;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./scripts.js");
}
}
private async Task TriggerPrompt()
{
result = await Prompt("Provide some text");
}
public async ValueTask<string?> Prompt(string message) =>
module is not null ?
await module.InvokeAsync<string>("showPrompt", message) : null;
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
CallJsExample6.razor
:
@page "/call-js-example-6"
@implements IAsyncDisposable
@inject IJSRuntime JS
<h1>Call JS Example 6</h1>
<p>
<button @onclick="TriggerPrompt">Trigger browser window prompt</button>
</p>
<p>
@result
</p>
@code {
private IJSObjectReference? module;
private string? result;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./scripts.js");
}
}
private async Task TriggerPrompt()
{
result = await Prompt("Provide some text");
}
public async ValueTask<string?> Prompt(string message) =>
module is not null ?
await module.InvokeAsync<string>("showPrompt", message) : null;
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
CallJsExample6.razor
:
@page "/call-js-example-6"
@implements IAsyncDisposable
@inject IJSRuntime JS
<h1>Call JS Example 6</h1>
<p>
<button @onclick="TriggerPrompt">Trigger browser window prompt</button>
</p>
<p>
@result
</p>
@code {
private IJSObjectReference module;
private string result;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./scripts.js");
}
}
private async Task TriggerPrompt()
{
result = await Prompt("Provide some text");
}
public async ValueTask<string> Prompt(string message)
{
return await module.InvokeAsync<string>("showPrompt", message);
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
W powyższym przykładzie:
- Zgodnie z konwencją
import
identyfikator jest specjalnym identyfikatorem używanym specjalnie do importowania modułu JS . - Określ plik zewnętrzny JS modułu przy użyciu stabilnej statycznej ścieżki zasobu internetowego:
./{SCRIPT PATH AND FILE NAME (.js)}
, gdzie:- Segment ścieżki dla bieżącego katalogu (
./
) jest wymagany w celu utworzenia poprawnej statycznej ścieżki zasobów do pliku JS. - Symbol zastępczy
{SCRIPT PATH AND FILE NAME (.js)}
to ścieżka i nazwa pliku w folderzewwwroot
.
- Segment ścieżki dla bieżącego katalogu (
- Usuwa element IJSObjectReference do odzyskiwania pamięci w pliku IAsyncDisposable.DisposeAsync.
Dynamiczne importowanie modułu wymaga żądania sieciowego, więc można go osiągnąć asynchronicznie tylko przez wywołanie metody InvokeAsync.
IJSInProcessObjectReference
reprezentuje odwołanie do JS obiektu, którego funkcje mogą być wywoływane synchronicznie w składnikach po stronie klienta. Aby uzyskać więcej informacji, zobacz sekcję Synchroniczne międzyoperacji JS w składnikach po stronie klienta.
Uwaga
Gdy plik zewnętrzny JS jest dostarczany przez bibliotekę Razor klas, określ plik modułu JS przy użyciu jego stabilnej statycznej ścieżki zasobu internetowego: : ./_content/{PACKAGE ID}/{SCRIPT PATH AND FILE NAME (.js)}
- Segment ścieżki dla bieżącego katalogu (
./
) jest wymagany w celu utworzenia poprawnej statycznej ścieżki zasobów do pliku JS. - Symbol zastępczy
{PACKAGE ID}
to identyfikator pakietu biblioteki. Identyfikator pakietu domyślnie określa nazwę zestawu projektu, jeśli tag<PackageId>
nie jest określony w pliku projektu. W poniższym przykładzie nazwa zestawu biblioteki toComponentLibrary
, a plik projektu biblioteki nie określa<PackageId>
. - Symbol zastępczy
{SCRIPT PATH AND FILE NAME (.js)}
to ścieżka i nazwa pliku w folderzewwwroot
. W poniższym przykładzie plik zewnętrzny JS (script.js
) jest umieszczany w folderze bibliotekiwwwroot
klas. module
jest prywatną wartością null IJSObjectReference klasy składnika (private IJSObjectReference? module;
).
module = await js.InvokeAsync<IJSObjectReference>(
"import", "./_content/ComponentLibrary/scripts.js");
Aby uzyskać więcej informacji, zobacz Korzystanie ze składników platformy ASP.NET Core Razor z biblioteki klas Razor (RCL).
W całej Blazor dokumentacji przykłady używają .js
rozszerzenia pliku dla plików modułu, a nie nowszego .mjs
rozszerzenia pliku (RFC 9239). Nasza dokumentacja nadal używa .js
rozszerzenia pliku z tych samych powodów, dla których dokumentacja Mozilla Foundation nadal używa .js
rozszerzenia pliku. Aby uzyskać więcej informacji, zobacz Odkładanie — .mjs i .js (dokumentacja MDN).
Przechwytywanie odwołań do elementów
Niektóre scenariusze międzyoperacyjności języka JavaScript (JS) wymagają odwołań do elementów HTML. Na przykład biblioteka interfejsu użytkownika może wymagać odwołania do elementu do inicjowania lub może być konieczne wywołanie interfejsów API podobnych do poleceń w elemencie, takim jak click
lub play
.
Przechwyć odwołania do elementów HTML w składniku przy użyciu następującego podejścia:
@ref
Dodaj atrybut do elementu HTML.- Zdefiniuj pole typu ElementReference , którego nazwa odpowiada wartości atrybutu
@ref
.
W poniższym przykładzie pokazano przechwytywanie odwołania do username
<input>
elementu:
<input @ref="username" ... />
@code {
private ElementReference username;
}
Ostrzeżenie
Użyj odwołania do elementu, aby zmutować zawartość pustego elementu, który nie wchodzi w interakcję z elementem Blazor. Ten scenariusz jest przydatny, gdy interfejs API innej firmy dostarcza zawartość do elementu. Ponieważ Blazor element nie wchodzi w interakcje, nie ma możliwości konfliktu między Blazorreprezentacją elementu a elementem DOM.
W poniższym przykładzie niebezpieczne jest zmutowanie zawartości nieurządzanej listy () przy użyciu MyList
międzyoperacyjnościJS, ponieważ Blazor współdziała z domem w celu wypełnienia elementów listy tego elementu (ul
<li>
) z Todos
obiektu:
<ul @ref="MyList">
@foreach (var item in Todos)
{
<li>@item.Text</li>
}
</ul>
MyList
Używanie odwołania do elementu tylko do odczytywania zawartości DOM lub wyzwalania zdarzenia jest obsługiwane.
Jeśli JS interop wycisza zawartość elementu MyList
i Blazor próbuje zastosować różnice do elementu, różnice nie będą zgodne z dom. Modyfikowanie zawartości listy za pośrednictwem JS międzyoperajności z odwołaniem MyList
do elementu nie jest obsługiwane.
Aby uzyskać więcej informacji, zobacz ASP.NET Core Blazor JavaScript interoperability (JS interop).
Element ElementReference jest przekazywany do JS kodu za pośrednictwem JS międzyoperacjności. Kod JS odbiera HTMLElement
wystąpienie, którego może używać z normalnymi interfejsami API DOM. Na przykład poniższy kod definiuje metodę rozszerzenia .NET (TriggerClickEvent
), która umożliwia wysyłanie kliknięcia myszą do elementu.
Funkcja JS clickElement
tworzy click
zdarzenie dla przekazanego elementu HTML (element
):
window.interopFunctions = {
clickElement : function (element) {
element.click();
}
}
Aby wywołać JS funkcję, która nie zwraca wartości, użyj polecenia JSRuntimeExtensions.InvokeVoidAsync. Poniższy kod wyzwala zdarzenie po stronie click
klienta przez wywołanie JS poprzedniej funkcji z przechwyconą ElementReferencefunkcją :
@inject IJSRuntime JS
<button @ref="exampleButton">Example Button</button>
<button @onclick="TriggerClick">
Trigger click event on <code>Example Button</code>
</button>
@code {
private ElementReference exampleButton;
public async Task TriggerClick()
{
await JS.InvokeVoidAsync(
"interopFunctions.clickElement", exampleButton);
}
}
Aby użyć metody rozszerzenia, utwórz statyczną metodę rozszerzenia, która odbiera IJSRuntime wystąpienie:
public static async Task TriggerClickEvent(this ElementReference elementRef,
IJSRuntime js)
{
await js.InvokeVoidAsync("interopFunctions.clickElement", elementRef);
}
Metoda clickElement
jest wywoływana bezpośrednio w obiekcie . W poniższym przykładzie przyjęto założenie, że TriggerClickEvent
metoda jest dostępna w JsInteropClasses
przestrzeni nazw:
@inject IJSRuntime JS
@using JsInteropClasses
<button @ref="exampleButton">Example Button</button>
<button @onclick="TriggerClick">
Trigger click event on <code>Example Button</code>
</button>
@code {
private ElementReference exampleButton;
public async Task TriggerClick()
{
await exampleButton.TriggerClickEvent(JS);
}
}
Ważne
Zmienna exampleButton
jest wypełniana tylko po renderowaniu składnika. Jeśli kod zostanie przekazany do kodu, ElementReference JS kod otrzyma wartość null
.JS Aby manipulować odwołaniami do elementów po zakończeniu renderowania składnika, użyj OnAfterRenderAsync
metod cyklu życia składników lubOnAfterRender
.
Podczas pracy z typami ogólnymi i zwracania wartości użyj polecenia ValueTask<TResult>:
public static ValueTask<T> GenericMethod<T>(
this ElementReference elementRef, IJSRuntime js) =>
js.InvokeAsync<T>("{JAVASCRIPT FUNCTION}", elementRef);
Symbol {JAVASCRIPT FUNCTION}
zastępczy jest identyfikatorem JS funkcji.
GenericMethod
obiekt jest wywoływany bezpośrednio na obiekcie o typie. W poniższym przykładzie przyjęto założenie, że GenericMethod
element jest dostępny w JsInteropClasses
przestrzeni nazw:
@inject IJSRuntime JS
@using JsInteropClasses
<input @ref="username" />
<button @onclick="OnClickMethod">Do something generic</button>
<p>
returnValue: @returnValue
</p>
@code {
private ElementReference username;
private string? returnValue;
private async Task OnClickMethod()
{
returnValue = await username.GenericMethod<string>(JS);
}
}
@inject IJSRuntime JS
@using JsInteropClasses
<input @ref="username" />
<button @onclick="OnClickMethod">Do something generic</button>
<p>
returnValue: @returnValue
</p>
@code {
private ElementReference username;
private string? returnValue;
private async Task OnClickMethod()
{
returnValue = await username.GenericMethod<string>(JS);
}
}
@inject IJSRuntime JS
@using JsInteropClasses
<input @ref="username" />
<button @onclick="OnClickMethod">Do something generic</button>
<p>
returnValue: @returnValue
</p>
@code {
private ElementReference username;
private string returnValue;
private async Task OnClickMethod()
{
returnValue = await username.GenericMethod<string>(JS);
}
}
Odwołania do elementów między składnikami
ElementReference Nie można przekazać elementu między składnikami, ponieważ:
- Wystąpienie ma gwarancję, że istnieje tylko po renderowaniu składnika, który jest wykonywany podczas lub po wykonaniu metody składnika OnAfterRender/OnAfterRenderAsync .
- Element ElementReference to
struct
, którego nie można przekazać jako parametru składnika.
Aby składnik nadrzędny udostępnić odwołanie do innych składników, składnik nadrzędny może:
- Zezwalaj składnikom podrzędnym na rejestrowanie wywołań zwrotnych.
- Wywołaj zarejestrowane wywołania zwrotne podczas OnAfterRender zdarzenia za pomocą przekazanego odwołania do elementu. Pośrednio takie podejście umożliwia składnikom podrzędnym interakcję z odwołaniem do elementu nadrzędnego.
<style>
.red { color: red }
</style>
<script>
function setElementClass(element, className) {
var myElement = element;
myElement.classList.add(className);
}
</script>
Uwaga
Aby uzyskać ogólne wskazówki dotyczące JS lokalizacji i naszych zaleceń dotyczących aplikacji produkcyjnych, zobacz Lokalizacja języka JavaScript w aplikacjach ASP.NET CoreBlazor.
CallJs7.razor
(składnik nadrzędny):
@page "/call-js-7"
<PageTitle>Call JS 7</PageTitle>
<h1>Call JS Example 7</h1>
<h2 @ref="title">Hello, world!</h2>
Welcome to your new app.
<SurveyPrompt Parent="this" Title="How is Blazor working for you?" />
CallJsExample7.razor
(składnik nadrzędny):
@page "/call-js-example-7"
<h1>Call JS Example 7</h1>
<h2 @ref="title">Hello, world!</h2>
Welcome to your new app.
<SurveyPrompt Parent="this" Title="How is Blazor working for you?" />
CallJsExample7.razor
(składnik nadrzędny):
@page "/call-js-example-7"
<h1>Call JS Example 7</h1>
<h2 @ref="title">Hello, world!</h2>
Welcome to your new app.
<SurveyPrompt Parent="this" Title="How is Blazor working for you?" />
CallJsExample7.razor
(składnik nadrzędny):
@page "/call-js-example-7"
<h1>Call JS Example 7</h1>
<h2 @ref="title">Hello, world!</h2>
Welcome to your new app.
<SurveyPrompt Parent="this" Title="How is Blazor working for you?" />
CallJsExample7.razor
(składnik nadrzędny):
@page "/call-js-example-7"
<h1>Call JS Example 7</h1>
<h2 @ref="title">Hello, world!</h2>
Welcome to your new app.
<SurveyPrompt Parent="this" Title="How is Blazor working for you?" />
CallJs7.razor.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.Pages;
public partial class CallJs7 :
ComponentBase, IObservable<ElementReference>, IDisposable
{
private bool disposing;
private readonly List<IObserver<ElementReference>> subscriptions = [];
private ElementReference title;
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
foreach (var subscription in subscriptions)
{
subscription.OnNext(title);
}
}
public void Dispose()
{
disposing = true;
foreach (var subscription in subscriptions)
{
try
{
subscription.OnCompleted();
}
catch (Exception)
{
}
}
subscriptions.Clear();
// The following prevents derived types that introduce a
// finalizer from needing to re-implement IDisposable.
GC.SuppressFinalize(this);
}
public IDisposable Subscribe(IObserver<ElementReference> observer)
{
if (disposing)
{
throw new InvalidOperationException("Parent being disposed");
}
subscriptions.Add(observer);
return new Subscription(observer, this);
}
private class Subscription(IObserver<ElementReference> observer,
CallJs7 self) : IDisposable
{
public IObserver<ElementReference> Observer { get; } = observer;
public CallJs7 Self { get; } = self;
public void Dispose() => Self.subscriptions.Remove(Observer);
}
}
CallJsExample7.razor.cs
:
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
namespace BlazorSample.Pages;
public partial class CallJsExample7 :
ComponentBase, IObservable<ElementReference>, IDisposable
{
private bool disposing;
private IList<IObserver<ElementReference>> subscriptions =
new List<IObserver<ElementReference>>();
private ElementReference title;
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
foreach (var subscription in subscriptions)
{
subscription.OnNext(title);
}
}
public void Dispose()
{
disposing = true;
foreach (var subscription in subscriptions)
{
try
{
subscription.OnCompleted();
}
catch (Exception)
{
}
}
subscriptions.Clear();
}
public IDisposable Subscribe(IObserver<ElementReference> observer)
{
if (disposing)
{
throw new InvalidOperationException("Parent being disposed");
}
subscriptions.Add(observer);
return new Subscription(observer, this);
}
private class Subscription : IDisposable
{
public Subscription(IObserver<ElementReference> observer,
CallJsExample7 self)
{
Observer = observer;
Self = self;
}
public IObserver<ElementReference> Observer { get; }
public CallJsExample7 Self { get; }
public void Dispose()
{
Self.subscriptions.Remove(Observer);
}
}
}
CallJsExample7.razor.cs
:
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
namespace BlazorSample.Pages;
public partial class CallJsExample7 :
ComponentBase, IObservable<ElementReference>, IDisposable
{
private bool disposing;
private IList<IObserver<ElementReference>> subscriptions =
new List<IObserver<ElementReference>>();
private ElementReference title;
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
foreach (var subscription in subscriptions)
{
subscription.OnNext(title);
}
}
public void Dispose()
{
disposing = true;
foreach (var subscription in subscriptions)
{
try
{
subscription.OnCompleted();
}
catch (Exception)
{
}
}
subscriptions.Clear();
}
public IDisposable Subscribe(IObserver<ElementReference> observer)
{
if (disposing)
{
throw new InvalidOperationException("Parent being disposed");
}
subscriptions.Add(observer);
return new Subscription(observer, this);
}
private class Subscription : IDisposable
{
public Subscription(IObserver<ElementReference> observer,
CallJsExample7 self)
{
Observer = observer;
Self = self;
}
public IObserver<ElementReference> Observer { get; }
public CallJsExample7 Self { get; }
public void Dispose()
{
Self.subscriptions.Remove(Observer);
}
}
}
CallJsExample7.razor.cs
:
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
namespace BlazorSample.Pages
{
public partial class CallJsExample7 :
ComponentBase, IObservable<ElementReference>, IDisposable
{
private bool disposing;
private IList<IObserver<ElementReference>> subscriptions =
new List<IObserver<ElementReference>>();
private ElementReference title;
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
foreach (var subscription in subscriptions)
{
subscription.OnNext(title);
}
}
public void Dispose()
{
disposing = true;
foreach (var subscription in subscriptions)
{
try
{
subscription.OnCompleted();
}
catch (Exception)
{
}
}
subscriptions.Clear();
}
public IDisposable Subscribe(IObserver<ElementReference> observer)
{
if (disposing)
{
throw new InvalidOperationException("Parent being disposed");
}
subscriptions.Add(observer);
return new Subscription(observer, this);
}
private class Subscription : IDisposable
{
public Subscription(IObserver<ElementReference> observer,
CallJsExample7 self)
{
Observer = observer;
Self = self;
}
public IObserver<ElementReference> Observer { get; }
public CallJsExample7 Self { get; }
public void Dispose()
{
Self.subscriptions.Remove(Observer);
}
}
}
}
CallJsExample7.razor.cs
:
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
namespace BlazorSample.Pages
{
public partial class CallJsExample7 :
ComponentBase, IObservable<ElementReference>, IDisposable
{
private bool disposing;
private IList<IObserver<ElementReference>> subscriptions =
new List<IObserver<ElementReference>>();
private ElementReference title;
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
foreach (var subscription in subscriptions)
{
subscription.OnNext(title);
}
}
public void Dispose()
{
disposing = true;
foreach (var subscription in subscriptions)
{
try
{
subscription.OnCompleted();
}
catch (Exception)
{
}
}
subscriptions.Clear();
}
public IDisposable Subscribe(IObserver<ElementReference> observer)
{
if (disposing)
{
throw new InvalidOperationException("Parent being disposed");
}
subscriptions.Add(observer);
return new Subscription(observer, this);
}
private class Subscription : IDisposable
{
public Subscription(IObserver<ElementReference> observer,
CallJsExample7 self)
{
Observer = observer;
Self = self;
}
public IObserver<ElementReference> Observer { get; }
public CallJsExample7 Self { get; }
public void Dispose()
{
Self.subscriptions.Remove(Observer);
}
}
}
}
W poprzednim przykładzie przestrzeń nazw aplikacji to BlazorSample
. W przypadku lokalnego testowania kodu zaktualizuj przestrzeń nazw.
SurveyPrompt.razor
(składnik podrzędny):
<div class="alert alert-secondary mt-4">
<span class="oi oi-pencil me-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold link-dark" href="https://go.microsoft.com/fwlink/?linkid=2186158">brief survey</a>
</span>
and tell us what you think.
</div>
@code {
// Demonstrates how a parent component can supply parameters
[Parameter]
public string? Title { get; set; }
}
<div class="alert alert-secondary mt-4">
<span class="oi oi-pencil me-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold link-dark" href="https://go.microsoft.com/fwlink/?linkid=2186157">brief survey</a>
</span>
and tell us what you think.
</div>
@code {
// Demonstrates how a parent component can supply parameters
[Parameter]
public string? Title { get; set; }
}
<div class="alert alert-secondary mt-4" role="alert">
<span class="oi oi-pencil mr-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold"
href="https://go.microsoft.com/fwlink/?linkid=2109206">brief survey</a>
</span>
and tell us what you think.
</div>
@code {
[Parameter]
public string? Title { get; set; }
}
<div class="alert alert-secondary mt-4" role="alert">
<span class="oi oi-pencil mr-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold"
href="https://go.microsoft.com/fwlink/?linkid=2109206">brief survey</a>
</span>
and tell us what you think.
</div>
@code {
[Parameter]
public string Title { get; set; }
}
<div class="alert alert-secondary mt-4" role="alert">
<span class="oi oi-pencil mr-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold"
href="https://go.microsoft.com/fwlink/?linkid=2109206">brief survey</a>
</span>
and tell us what you think.
</div>
@code {
[Parameter]
public string Title { get; set; }
}
SurveyPrompt.razor.cs
:
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace BlazorSample.Components;
public partial class SurveyPrompt :
ComponentBase, IObserver<ElementReference>, IDisposable
{
private IDisposable? subscription = null;
[Parameter]
public IObservable<ElementReference>? Parent { get; set; }
[Inject]
public IJSRuntime? JS {get; set;}
protected override void OnParametersSet()
{
base.OnParametersSet();
subscription?.Dispose();
subscription = Parent?.Subscribe(this);
}
public void OnCompleted() => subscription = null;
public void OnError(Exception error) => subscription = null;
public void OnNext(ElementReference value) =>
_ = (JS?.InvokeAsync<object>("setElementClass", [value, "red"]));
public void Dispose()
{
subscription?.Dispose();
// The following prevents derived types that introduce a
// finalizer from needing to re-implement IDisposable.
GC.SuppressFinalize(this);
}
}
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace BlazorSample.Shared;
public partial class SurveyPrompt :
ComponentBase, IObserver<ElementReference>, IDisposable
{
private IDisposable? subscription = null;
[Parameter]
public IObservable<ElementReference>? Parent { get; set; }
[Inject]
public IJSRuntime? JS {get; set;}
protected override void OnParametersSet()
{
base.OnParametersSet();
subscription?.Dispose();
subscription =
Parent is not null ? Parent.Subscribe(this) : null;
}
public void OnCompleted()
{
subscription = null;
}
public void OnError(Exception error)
{
subscription = null;
}
public void OnNext(ElementReference value)
{
JS.InvokeAsync<object>(
"setElementClass", new object[] { value, "red" });
}
public void Dispose()
{
subscription?.Dispose();
}
}
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace BlazorSample.Shared;
public partial class SurveyPrompt :
ComponentBase, IObserver<ElementReference>, IDisposable
{
private IDisposable? subscription = null;
[Parameter]
public IObservable<ElementReference>? Parent { get; set; }
[Inject]
public IJSRuntime? JS {get; set;}
protected override void OnParametersSet()
{
base.OnParametersSet();
subscription?.Dispose();
subscription =
Parent is not null ? Parent.Subscribe(this) : null;
}
public void OnCompleted()
{
subscription = null;
}
public void OnError(Exception error)
{
subscription = null;
}
public void OnNext(ElementReference value)
{
JS.InvokeAsync<object>(
"setElementClass", new object[] { value, "red" });
}
public void Dispose()
{
subscription?.Dispose();
}
}
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace BlazorSample.Shared
{
public partial class SurveyPrompt :
ComponentBase, IObserver<ElementReference>, IDisposable
{
private IDisposable subscription = null;
[Parameter]
public IObservable<ElementReference> Parent { get; set; }
[Inject]
public IJSRuntime JS {get; set;}
protected override void OnParametersSet()
{
base.OnParametersSet();
subscription?.Dispose();
subscription = Parent.Subscribe(this);
}
public void OnCompleted()
{
subscription = null;
}
public void OnError(Exception error)
{
subscription = null;
}
public void OnNext(ElementReference value)
{
JS.InvokeAsync<object>(
"setElementClass", new object[] { value, "red" });
}
public void Dispose()
{
subscription?.Dispose();
}
}
}
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace BlazorSample.Shared
{
public partial class SurveyPrompt :
ComponentBase, IObserver<ElementReference>, IDisposable
{
private IDisposable subscription = null;
[Parameter]
public IObservable<ElementReference> Parent { get; set; }
[Inject]
public IJSRuntime JS {get; set;}
protected override void OnParametersSet()
{
base.OnParametersSet();
subscription?.Dispose();
subscription = Parent.Subscribe(this);
}
public void OnCompleted()
{
subscription = null;
}
public void OnError(Exception error)
{
subscription = null;
}
public void OnNext(ElementReference value)
{
JS.InvokeAsync<object>(
"setElementClass", new object[] { value, "red" });
}
public void Dispose()
{
subscription?.Dispose();
}
}
}
W poprzednim przykładzie przestrzeń nazw aplikacji jest BlazorSample
z udostępnionymi składnikami w folderze Shared
. W przypadku lokalnego testowania kodu zaktualizuj przestrzeń nazw.
Wzmacnianie połączeń międzyoperacyjnych języka JavaScript
Ta sekcja dotyczy tylko składników interactive server, ale składniki po stronie klienta mogą również ustawiać JS limity czasu międzyoperacyjności, jeśli warunki go uzasadniają.
W aplikacjach po stronie serwera z interakcyjnością serwera współdziałanie języka JavaScript (JS) może zakończyć się niepowodzeniem z powodu błędów sieci i powinno być traktowane jako zawodne. Blazor aplikacje używają limitu czasu minuty dla JS wywołań międzyoperaowych. Jeśli aplikacja może tolerować bardziej agresywny limit czasu, ustaw limit czasu przy użyciu jednej z poniższych metod.
Ustaw globalny limit czasu w elemencji za Program.cs
pomocą CircuitOptions.JSInteropDefaultCallTimeoutpolecenia :
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents(options =>
options.JSInteropDefaultCallTimeout = {TIMEOUT});
builder.Services.AddServerSideBlazor(
options => options.JSInteropDefaultCallTimeout = {TIMEOUT});
Ustaw globalny limit czasu w metodzie Startup.cs
za Startup.ConfigureServices
pomocą CircuitOptions.JSInteropDefaultCallTimeoutpolecenia :
services.AddServerSideBlazor(
options => options.JSInteropDefaultCallTimeout = {TIMEOUT});
Symbol {TIMEOUT}
zastępczy to TimeSpan (na przykład TimeSpan.FromSeconds(80)
).
Ustaw limit czasu wywołania w kodzie składnika. Określony limit czasu zastępuje globalny limit czasu ustawiony przez JSInteropDefaultCallTimeout:
var result = await JS.InvokeAsync<string>("{ID}", {TIMEOUT}, new[] { "Arg1" });
W powyższym przykładzie:
- Symbol
{TIMEOUT}
zastępczy to TimeSpan (na przykładTimeSpan.FromSeconds(80)
). - Symbol
{ID}
zastępczy jest identyfikatorem funkcji do wywołania. Na przykład wartośćsomeScope.someFunction
wywołuje funkcjęwindow.someScope.someFunction
.
Chociaż częstą przyczyną JS niepowodzeń międzyoperacyjnych są błędy sieciowe ze składnikami po stronie serwera, limity czasu wywołania można ustawić dla JS wywołań międzyoperacyjnych dla składników po stronie klienta. Mimo że nie SignalR istnieje obwód dla składnika po stronie klienta, JS wywołania międzyoperaciowe mogą zakończyć się niepowodzeniem z innych powodów, które mają zastosowanie.
Aby uzyskać więcej informacji na temat wyczerpania zasobów, zobacz Wskazówki dotyczące ograniczania zagrożeń dotyczące renderowania interaktywnego po stronie serwera ASP.NET CoreBlazor.
Unikaj odwołań do obiektów okrągłych
Obiekty zawierające odwołania cykliczne nie mogą być serializowane na kliencie dla jednego z następujących obiektów:
- Wywołania metody .NET.
- Wywołania metody Języka JavaScript z języka C#, gdy zwracany typ zawiera odwołania cykliczne.
Biblioteki języka JavaScript renderujące interfejs użytkownika
Czasami warto użyć bibliotek Języka JavaScript (JS), które generują widoczne elementy interfejsu użytkownika w przeglądarce DOM. Na pierwszy rzut oka może się to wydawać trudne, ponieważ Blazorsystem różnicowania polega na kontroli nad drzewem elementów DOM i występuje w błędach, jeśli niektóre zewnętrzne kody wyciszają drzewo DOM i unieważniają jego mechanizm stosowania różnic. Nie jest Blazorto ograniczenie specyficzne. To samo wyzwanie występuje w przypadku dowolnej platformy interfejsu użytkownika opartej na różnicach.
Na szczęście interfejs użytkownika generowany zewnętrznie można niezawodnie osadzić w Razor interfejsie użytkownika składnika. Zalecaną techniką jest utworzenie pustego elementu w kodzie (.razor
pliku) składnika. Jeśli chodzi o Blazorsystem różnicowania, element jest zawsze pusty, więc moduł renderujący nie powraca do elementu i zamiast tego pozostawia jego zawartość sam. Dzięki temu można bezpiecznie wypełnić element dowolną zawartością zarządzaną zewnętrznie.
W poniższym przykładzie przedstawiono koncepcję. W instrukcji if
when firstRender
to true
, wchodzić w interakcje poza unmanagedElement
użyciem JS międzyoperacjnościBlazor. Na przykład wywołaj bibliotekę zewnętrzną JS , aby wypełnić element. Blazor pozostawia zawartość samego elementu do momentu usunięcia tego składnika. Po usunięciu składnika cały poddrzewo DOM składnika również zostanie usunięte.
<h1>Hello! This is a Razor component rendered at @DateTime.Now</h1>
<div @ref="unmanagedElement"></div>
@code {
private ElementReference unmanagedElement;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
...
}
}
}
Rozważmy poniższy przykład, który renderuje mapę interaktywną przy użyciu interfejsów API mapbox typu open source.
JS Poniższy moduł zostanie umieszczony w aplikacji lub udostępniony z Razor biblioteki klas.
Uwaga
Aby utworzyć mapę Mapbox , uzyskaj token dostępu z usługi Mapbox Sign in i podaj go, gdzie {ACCESS TOKEN}
znajduje się w poniższym kodzie.
wwwroot/mapComponent.js
:
import 'https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js';
mapboxgl.accessToken = '{ACCESS TOKEN}';
export function addMapToElement(element) {
return new mapboxgl.Map({
container: element,
style: 'mapbox://styles/mapbox/streets-v11',
center: [-74.5, 40],
zoom: 9
});
}
export function setMapCenter(map, latitude, longitude) {
map.setCenter([longitude, latitude]);
}
Aby utworzyć poprawny styl, dodaj następujący tag arkusza stylów do strony HTML hosta.
Dodaj następujący <link>
element do <head>
znaczników elementu (lokalizacja <head>
zawartości):
<link href="https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css"
rel="stylesheet" />
CallJs8.razor
:
@page "/call-js-8"
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call JS 8</PageTitle>
<HeadContent>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css"
rel="stylesheet" />
</HeadContent>
<h1>Call JS Example 8</h1>
<div @ref="mapElement" style='width:400px;height:300px'></div>
<button @onclick="() => ShowAsync(51.454514, -2.587910)">Show Bristol, UK</button>
<button @onclick="() => ShowAsync(35.6762, 139.6503)">Show Tokyo, Japan</button>
@code
{
private ElementReference mapElement;
private IJSObjectReference? mapModule;
private IJSObjectReference? mapInstance;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
mapModule = await JS.InvokeAsync<IJSObjectReference>(
"import", "./mapComponent.js");
mapInstance = await mapModule.InvokeAsync<IJSObjectReference>(
"addMapToElement", mapElement);
}
}
private async Task ShowAsync(double latitude, double longitude)
{
if (mapModule is not null && mapInstance is not null)
{
await mapModule.InvokeVoidAsync("setMapCenter", mapInstance,
latitude, longitude);
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (mapInstance is not null)
{
await mapInstance.DisposeAsync();
}
if (mapModule is not null)
{
await mapModule.DisposeAsync();
}
}
}
CallJsExample8.razor
:
@page "/call-js-example-8"
@implements IAsyncDisposable
@inject IJSRuntime JS
<h1>Call JS Example 8</h1>
<div @ref="mapElement" style='width:400px;height:300px'></div>
<button @onclick="() => ShowAsync(51.454514, -2.587910)">Show Bristol, UK</button>
<button @onclick="() => ShowAsync(35.6762, 139.6503)">Show Tokyo, Japan</button>
@code
{
private ElementReference mapElement;
private IJSObjectReference? mapModule;
private IJSObjectReference? mapInstance;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
mapModule = await JS.InvokeAsync<IJSObjectReference>(
"import", "./mapComponent.js");
mapInstance = await mapModule.InvokeAsync<IJSObjectReference>(
"addMapToElement", mapElement);
}
}
private async Task ShowAsync(double latitude, double longitude)
{
if (mapModule is not null && mapInstance is not null)
{
await mapModule.InvokeVoidAsync("setMapCenter", mapInstance,
latitude, longitude);
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (mapInstance is not null)
{
await mapInstance.DisposeAsync();
}
if (mapModule is not null)
{
await mapModule.DisposeAsync();
}
}
}
CallJsExample8.razor
:
@page "/call-js-example-8"
@implements IAsyncDisposable
@inject IJSRuntime JS
<h1>Call JS Example 8</h1>
<div @ref="mapElement" style='width:400px;height:300px'></div>
<button @onclick="() => ShowAsync(51.454514, -2.587910)">Show Bristol, UK</button>
<button @onclick="() => ShowAsync(35.6762, 139.6503)">Show Tokyo, Japan</button>
@code
{
private ElementReference mapElement;
private IJSObjectReference? mapModule;
private IJSObjectReference? mapInstance;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
mapModule = await JS.InvokeAsync<IJSObjectReference>(
"import", "./mapComponent.js");
mapInstance = await mapModule.InvokeAsync<IJSObjectReference>(
"addMapToElement", mapElement);
}
}
private async Task ShowAsync(double latitude, double longitude)
{
if (mapModule is not null && mapInstance is not null)
{
await mapModule.InvokeVoidAsync("setMapCenter", mapInstance,
latitude, longitude);
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (mapInstance is not null)
{
await mapInstance.DisposeAsync();
}
if (mapModule is not null)
{
await mapModule.DisposeAsync();
}
}
}
Powyższy przykład tworzy interaktywny interfejs użytkownika mapy. Użytkownik:
- Można przeciągnąć, aby przewinąć lub powiększyć.
- Wybierz przyciski, aby przejść do wstępnie zdefiniowanych lokalizacji.
W powyższym przykładzie:
- Z
<div>
jest@ref="mapElement"
pozostawiony pusty, jeśli Blazor chodzi o to. Skryptmapbox-gl.js
może bezpiecznie wypełnić element i zmodyfikować jego zawartość. Użyj tej techniki z dowolną JS biblioteką renderowaną przez interfejs użytkownika. Składniki można osadzać z platformy SPA innej firmy JS wewnątrz Razor składników, o ile nie próbują skontaktować się i zmodyfikować inne części strony. Kod zewnętrzny JS nie jest bezpieczny, aby modyfikować elementy, które Blazor nie są uważane za puste. - W przypadku korzystania z tego podejścia należy pamiętać o zasadach zachowania Blazor lub zniszczenia elementów DOM. Składnik bezpiecznie obsługuje zdarzenia kliknięcia przycisku i aktualizuje istniejące wystąpienie mapy, ponieważ elementy DOM są zachowywane tam, gdzie to możliwe. Jeśli renderowano listę elementów mapy z wewnątrz
@foreach
pętli, chcesz użyć@key
polecenia , aby zapewnić zachowanie wystąpień składników. W przeciwnym razie zmiany w danych listy mogą spowodować, że wystąpienia składników zachowają stan poprzednich wystąpień w niepożądany sposób. Aby uzyskać więcej informacji, zobacz, jak używać atrybutu@key
dyrektywy w celu zachowania relacji między elementami, składnikami i obiektami modelu. - Przykład hermetyzuje JS logikę i zależności w module JavaScript i ładuje moduł dynamicznie przy użyciu identyfikatora
import
. Aby uzyskać więcej informacji, zobacz Izolacja języka JavaScript w modułach Języka JavaScript.
Obsługa tablic bajtów
Blazor obsługuje zoptymalizowaną międzyoperacyjną tablicę bajtów JavaScript (JS), która pozwala uniknąć kodowania/dekodowania tablic bajtowych do base64. W poniższym przykładzie użyto JS międzyoperacji w celu przekazania tablicy bajtów do języka JavaScript.
receiveByteArray
JS Podaj funkcję. Funkcja jest wywoływana z elementem InvokeVoidAsync i nie zwraca wartości:
<script>
window.receiveByteArray = (bytes) => {
let utf8decoder = new TextDecoder();
let str = utf8decoder.decode(bytes);
return str;
};
</script>
Uwaga
Aby uzyskać ogólne wskazówki dotyczące JS lokalizacji i naszych zaleceń dotyczących aplikacji produkcyjnych, zobacz Lokalizacja języka JavaScript w aplikacjach ASP.NET CoreBlazor.
CallJs9.razor
:
@page "/call-js-9"
@inject IJSRuntime JS
<h1>Call JS Example 9</h1>
<p>
<button @onclick="SendByteArray">Send Bytes</button>
</p>
<p>
@result
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>
@code {
private string? result;
private async Task SendByteArray()
{
var bytes = new byte[] { 0x45, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69,
0x6e, 0x67, 0x27, 0x73, 0x20, 0x73, 0x68, 0x69, 0x6e, 0x79, 0x2c,
0x20, 0x43, 0x61, 0x70, 0x74, 0x69, 0x61, 0x6e, 0x2e, 0x20, 0x4e,
0x6f, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x72, 0x65, 0x74, 0x2e };
result = await JS.InvokeAsync<string>("receiveByteArray", bytes);
}
}
CallJsExample9.razor
:
@page "/call-js-example-9"
@inject IJSRuntime JS
<h1>Call JS Example 9</h1>
<p>
<button @onclick="SendByteArray">Send Bytes</button>
</p>
<p>
@result
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>
@code {
private string? result;
private async Task SendByteArray()
{
var bytes = new byte[] { 0x45, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69,
0x6e, 0x67, 0x27, 0x73, 0x20, 0x73, 0x68, 0x69, 0x6e, 0x79, 0x2c,
0x20, 0x43, 0x61, 0x70, 0x74, 0x69, 0x61, 0x6e, 0x2e, 0x20, 0x4e,
0x6f, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x72, 0x65, 0x74, 0x2e };
result = await JS.InvokeAsync<string>("receiveByteArray", bytes);
}
}
Aby uzyskać informacje na temat używania tablicy bajtów podczas wywoływania platformy .NET z języka JavaScript, zobacz Wywoływanie metod platformy .NET z funkcji języka JavaScript w programie ASP.NET Core Blazor.
Przesyłanie strumieniowe z platformy .NET do języka JavaScript
Blazor obsługuje przesyłanie strumieniowe danych bezpośrednio z platformy .NET do języka JavaScript (JS). Strumienie są tworzone przy użyciu elementu DotNetStreamReference.
DotNetStreamReference reprezentuje strumień platformy .NET i używa następujących parametrów:
stream
: strumień wysłany do elementu JS.leaveOpen
: określa, czy strumień jest pozostawiony otwarty po transmisji. Jeśli wartość nie jest podana,leaveOpen
wartość domyślna tofalse
.
W JSsystemie użyj buforu tablicy lub strumienia możliwego do odczytu, aby odbierać dane:
Przy użyciu elementu
ArrayBuffer
:async function streamToJavaScript(streamRef) { const data = await streamRef.arrayBuffer(); }
Przy użyciu elementu
ReadableStream
:async function streamToJavaScript(streamRef) { const stream = await streamRef.stream(); }
W kodzie języka C#:
var streamRef = new DotNetStreamReference(stream: {STREAM}, leaveOpen: false);
await JS.InvokeVoidAsync("streamToJavaScript", streamRef);
W powyższym przykładzie:
- Symbol
{STREAM}
zastępczy reprezentuje Stream element wysłany do elementu JS. JS
jest wystąpieniem wstrzykniętym IJSRuntime .
DotNetStreamReference Usuwanie wystąpienia jest zwykle niepotrzebne. Gdy leaveOpen
parametr jest ustawiony na wartość domyślną false
, element bazowy Stream jest automatycznie usuwany po transmisji do .JS
Jeśli leaveOpen
element ma true
wartość , wówczas nie DotNetStreamReference usuwa jego bazowego Streamelementu . Kod aplikacji określa, kiedy usunąć bazowy Streamelement . Podczas podejmowania decyzji o sposobie usuwania bazowego Streamelementu należy wziąć pod uwagę następujące kwestie:
- Stream Usuwanie podczas przesyłania go do JS jest uznawane za błąd aplikacji i może spowodować wystąpienie nieobsługiwanego wyjątku.
- Stream transmisja zaczyna się zaraz po DotNetStreamReference przekazaniu elementu jako argumentu do JS wywołania międzyoperacyjnego, niezależnie od tego, czy strumień jest rzeczywiście używany w JS logice.
Biorąc pod uwagę te cechy, zalecamy dysponowanie bazowego Stream tylko po jego pełnym użyciu JS (obietnica zwrócona przez arrayBuffer
lub stream
rozwiązania). Wynika z tego, że element powinien zostać przekazany tylko wtedy, DotNetStreamReference gdy jest bezwarunkowo spożywany JS przez JS logikę.
Wywołanie metod platformy .NET z funkcji Języka JavaScript w programie ASP.NET Core Blazor obejmuje operację odwrotną, przesyłaną strumieniowo z języka JavaScript do platformy .NET.
pliki ASP.NET Core Blazor obejmują sposób pobierania pliku w programie Blazor.
Przechwytywanie wyjątków języka JavaScript
Aby przechwycić JS wyjątki, opakuj międzyoperajności JS w-try
catch
bloku i przechwyć JSExceptionelement .
W poniższym przykładzie nonFunction
JS funkcja nie istnieje. Gdy funkcja nie zostanie znaleziona, JSException element jest uwięziony za pomocą elementu Message , który wskazuje następujący błąd:
Could not find 'nonFunction' ('nonFunction' was undefined).
CallJs11.razor
:
@page "/call-js-11"
@inject IJSRuntime JS
<PageTitle>Call JS 11</PageTitle>
<h1>Call JS Example 11</h1>
<p>
<button @onclick="CatchUndefinedJSFunction">Catch Exception</button>
</p>
<p>
@result
</p>
<p>
@errorMessage
</p>
@code {
private string? errorMessage;
private string? result;
private async Task CatchUndefinedJSFunction()
{
try
{
result = await JS.InvokeAsync<string>("nonFunction");
}
catch (JSException e)
{
errorMessage = $"Error Message: {e.Message}";
}
}
}
CallJsExample11.razor
:
@page "/call-js-example-11"
@inject IJSRuntime JS
<h1>Call JS Example 11</h1>
<p>
<button @onclick="CatchUndefinedJSFunction">Catch Exception</button>
</p>
<p>
@result
</p>
<p>
@errorMessage
</p>
@code {
private string? errorMessage;
private string? result;
private async Task CatchUndefinedJSFunction()
{
try
{
result = await JS.InvokeAsync<string>("nonFunction");
}
catch (JSException e)
{
errorMessage = $"Error Message: {e.Message}";
}
}
}
CallJsExample11.razor
:
@page "/call-js-example-11"
@inject IJSRuntime JS
<h1>Call JS Example 11</h1>
<p>
<button @onclick="CatchUndefinedJSFunction">Catch Exception</button>
</p>
<p>
@result
</p>
<p>
@errorMessage
</p>
@code {
private string? errorMessage;
private string? result;
private async Task CatchUndefinedJSFunction()
{
try
{
result = await JS.InvokeAsync<string>("nonFunction");
}
catch (JSException e)
{
errorMessage = $"Error Message: {e.Message}";
}
}
}
CallJsExample11.razor
:
@page "/call-js-example-11"
@inject IJSRuntime JS
<h1>Call JS Example 11</h1>
<p>
<button @onclick="CatchUndefinedJSFunction">Catch Exception</button>
</p>
<p>
@result
</p>
<p>
@errorMessage
</p>
@code {
private string errorMessage;
private string result;
private async Task CatchUndefinedJSFunction()
{
try
{
result = await JS.InvokeAsync<string>("nonFunction");
}
catch (JSException e)
{
errorMessage = $"Error Message: {e.Message}";
}
}
}
CallJsExample11.razor
:
@page "/call-js-example-11"
@inject IJSRuntime JS
<h1>Call JS Example 11</h1>
<p>
<button @onclick="CatchUndefinedJSFunction">Catch Exception</button>
</p>
<p>
@result
</p>
<p>
@errorMessage
</p>
@code {
private string errorMessage;
private string result;
private async Task CatchUndefinedJSFunction()
{
try
{
result = await JS.InvokeAsync<string>("nonFunction");
}
catch (JSException e)
{
errorMessage = $"Error Message: {e.Message}";
}
}
}
Przerywanie długotrwałej funkcji języka JavaScript
JSUżyj abortController z elementem CancellationTokenSource w składniku, aby przerwać długotrwałą funkcję Języka JavaScript z kodu języka C#.
Poniższa JSHelpers
klasa zawiera symulowaną długotrwałą funkcję , longRunningFn
do liczenia w sposób ciągły do AbortController.signal
momentu AbortController.abort
wywołania metody . Funkcja sleep
służy do celów demonstracyjnych, aby symulować powolne wykonywanie długotrwałej funkcji i nie byłoby obecne w kodzie produkcyjnym. Gdy składnik wywołuje stopFn
metodę , longRunningFn
element jest sygnalizowany, aby przerwać za pośrednictwem sprawdzania warunkowego while
pętli w systemie AbortSignal.aborted
.
<script>
class Helpers {
static #controller = new AbortController();
static async #sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
static async longRunningFn() {
var i = 0;
while (!this.#controller.signal.aborted) {
i++;
console.log(`longRunningFn: ${i}`);
await this.#sleep(1000);
}
}
static stopFn() {
this.#controller.abort();
console.log('longRunningFn aborted!');
}
}
window.Helpers = Helpers;
</script>
Uwaga
Aby uzyskać ogólne wskazówki dotyczące JS lokalizacji i naszych zaleceń dotyczących aplikacji produkcyjnych, zobacz Lokalizacja języka JavaScript w aplikacjach ASP.NET CoreBlazor.
Następujący składnik:
- JS Wywołuje funkcję
longRunningFn
po wybraniuStart Task
przycisku. Element A CancellationTokenSource służy do zarządzania wykonywaniem długotrwałej funkcji. CancellationToken.Register Ustawia delegata wywołania międzyoperaciowego JS w celu wykonania JS funkcjistopFn
po CancellationTokenSource.Token anulowaniu. - Po wybraniu
Cancel Task
CancellationTokenSource.Token przycisku element zostanie anulowany za pomocą wywołania metody Cancel. - Element CancellationTokenSource jest usuwany w metodzie
Dispose
.
CallJs12.razor
:
@page "/call-js-12"
@inject IJSRuntime JS
<h1>Cancel long-running JS interop</h1>
<p>
<button @onclick="StartTask">Start Task</button>
<button @onclick="CancelTask">Cancel Task</button>
</p>
@code {
private CancellationTokenSource? cts;
private async Task StartTask()
{
cts = new CancellationTokenSource();
cts.Token.Register(() => JS.InvokeVoidAsync("Helpers.stopFn"));
await JS.InvokeVoidAsync("Helpers.longRunningFn");
}
private void CancelTask()
{
cts?.Cancel();
}
public void Dispose()
{
cts?.Cancel();
cts?.Dispose();
}
}
CallJsExample12.razor
:
@page "/call-js-example-12"
@inject IJSRuntime JS
<h1>Cancel long-running JS interop</h1>
<p>
<button @onclick="StartTask">Start Task</button>
<button @onclick="CancelTask">Cancel Task</button>
</p>
@code {
private CancellationTokenSource? cts;
private async Task StartTask()
{
cts = new CancellationTokenSource();
cts.Token.Register(() => JS.InvokeVoidAsync("Helpers.stopFn"));
await JS.InvokeVoidAsync("Helpers.longRunningFn");
}
private void CancelTask()
{
cts?.Cancel();
}
public void Dispose()
{
cts?.Cancel();
cts?.Dispose();
}
}
Konsola narzędzi deweloperskich przeglądarki wskazuje wykonywanie długotrwałej JS funkcji po wybraniu Start Task
przycisku, a po wybraniu funkcji zostanie przerwana po wybraniu Cancel Task
przycisku:
longRunningFn: 1
longRunningFn: 2
longRunningFn: 3
longRunningFn aborted!
Interopcja języka JavaScript [JSImport]
/[JSExport]
Ta sekcja dotyczy składników po stronie klienta.
Alternatywą dla interakcji z językiem JavaScript (JS) w składnikach po stronie klienta przy użyciu JS Blazormechanizmu międzyoperacyjnego opartego na interfejsie IJSRuntime jest/JS[JSImport]
[JSExport]
dostępny interfejs API międzyoperacyjny dla aplikacji przeznaczonych dla platformy .NET 7 lub nowszej.
Aby uzyskać więcej informacji, zobacz JavaScript JSImport/JSExport interop with ASP.NET Core (Interopcja javaScript JSImport/JSExport with ASP.NET Core Blazor).
Unmarshalled międzyoperacyjności języka JavaScript
Ta sekcja dotyczy składników po stronie klienta.
Unmarshalled interop przy użyciu interfejsu IJSUnmarshalledRuntime jest przestarzały i powinien zostać zastąpiony międzyoperacyjnością języka JavaScript/[JSImport]
[JSExport]
.
Aby uzyskać więcej informacji, zobacz JavaScript JSImport/JSExport interop with ASP.NET Core (Interopcja javaScript JSImport/JSExport with ASP.NET Core Blazor).
Unmarshalled międzyoperacyjności języka JavaScript
Blazor WebAssembly Składniki mogą mieć niską wydajność, gdy obiekty platformy .NET są serializowane dla międzyoperacyjności języka JavaScript (JS), a jedną z następujących wartości jest spełniony:
- Duża liczba obiektów platformy .NET jest szybko serializowana. Na przykład niska wydajność może spowodować, że JS wywołania międzyoperamentowe są wykonywane na podstawie przenoszenia urządzenia wejściowego, takiego jak obracanie kółka myszy.
- Duże obiekty platformy .NET lub wiele obiektów platformy .NET muszą być serializowane na potrzeby JS międzyoperacyjności. Na przykład niska wydajność może spowodować, że JS wywołania międzyoperacjonowe wymagają serializacji kilkudziesięciu plików.
IJSUnmarshalledObjectReference reprezentuje odwołanie do JS obiektu, którego funkcje można wywołać bez konieczności serializacji danych platformy .NET.
W poniższym przykładzie:
- Struktura zawierająca ciąg i liczba całkowita jest przekazywana nieserializowane do JS.
- JS funkcje przetwarzają dane i zwracają wartość logiczną lub ciąg do obiektu wywołującego.
- Ciąg JS nie jest bezpośrednio konwertowany na obiekt .NET
string
. FunkcjaunmarshalledFunctionReturnString
wywołujeBINDING.js_string_to_mono_string
metodę zarządzania konwersją JS ciągu.
Uwaga
Poniższe przykłady nie są typowymi przypadkami użycia w tym scenariuszu, ponieważ struktura przekazana do JS nie powoduje niskiej wydajności składników. W tym przykładzie użyto tylko małego obiektu, aby zademonstrować koncepcje przekazywania nieserializowanych danych platformy .NET.
<script>
window.returnObjectReference = () => {
return {
unmarshalledFunctionReturnBoolean: function (fields) {
const name = Blazor.platform.readStringField(fields, 0);
const year = Blazor.platform.readInt32Field(fields, 8);
return name === "Brigadier Alistair Gordon Lethbridge-Stewart" &&
year === 1968;
},
unmarshalledFunctionReturnString: function (fields) {
const name = Blazor.platform.readStringField(fields, 0);
const year = Blazor.platform.readInt32Field(fields, 8);
return BINDING.js_string_to_mono_string(`Hello, ${name} (${year})!`);
}
};
}
</script>
Uwaga
Aby uzyskać ogólne wskazówki dotyczące JS lokalizacji i naszych zaleceń dotyczących aplikacji produkcyjnych, zobacz Lokalizacja języka JavaScript w aplikacjach ASP.NET CoreBlazor.
Ostrzeżenie
Nazwa js_string_to_mono_string
, zachowanie i istnienie funkcji mogą ulec zmianie w przyszłej wersji platformy .NET. Na przykład:
- Prawdopodobnie zostanie zmieniona nazwa funkcji.
- Sama funkcja może zostać usunięta na rzecz automatycznej konwersji ciągów przez platformę.
CallJsExample10.razor
:
@page "/call-js-example-10"
@using System.Runtime.InteropServices
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Call JS Example 10</h1>
@if (callResultForBoolean)
{
<p>JS interop was successful!</p>
}
@if (!string.IsNullOrEmpty(callResultForString))
{
<p>@callResultForString</p>
}
<p>
<button @onclick="CallJSUnmarshalledForBoolean">
Call Unmarshalled JS & Return Boolean
</button>
<button @onclick="CallJSUnmarshalledForString">
Call Unmarshalled JS & Return String
</button>
</p>
<p>
<a href="https://www.doctorwho.tv">Doctor Who</a>
is a registered trademark of the <a href="https://www.bbc.com/">BBC</a>.
</p>
@code {
private bool callResultForBoolean;
private string? callResultForString;
private void CallJSUnmarshalledForBoolean()
{
var unmarshalledRuntime = (IJSUnmarshalledRuntime)JS;
var jsUnmarshalledReference = unmarshalledRuntime
.InvokeUnmarshalled<IJSUnmarshalledObjectReference>(
"returnObjectReference");
callResultForBoolean =
jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, bool>(
"unmarshalledFunctionReturnBoolean", GetStruct());
}
private void CallJSUnmarshalledForString()
{
var unmarshalledRuntime = (IJSUnmarshalledRuntime)JS;
var jsUnmarshalledReference = unmarshalledRuntime
.InvokeUnmarshalled<IJSUnmarshalledObjectReference>(
"returnObjectReference");
callResultForString =
jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, string>(
"unmarshalledFunctionReturnString", GetStruct());
}
private InteropStruct GetStruct()
{
return new InteropStruct
{
Name = "Brigadier Alistair Gordon Lethbridge-Stewart",
Year = 1968,
};
}
[StructLayout(LayoutKind.Explicit)]
public struct InteropStruct
{
[FieldOffset(0)]
public string Name;
[FieldOffset(8)]
public int Year;
}
}
@page "/call-js-example-10"
@using System.Runtime.InteropServices
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Call JS Example 10</h1>
@if (callResultForBoolean)
{
<p>JS interop was successful!</p>
}
@if (!string.IsNullOrEmpty(callResultForString))
{
<p>@callResultForString</p>
}
<p>
<button @onclick="CallJSUnmarshalledForBoolean">
Call Unmarshalled JS & Return Boolean
</button>
<button @onclick="CallJSUnmarshalledForString">
Call Unmarshalled JS & Return String
</button>
</p>
<p>
<a href="https://www.doctorwho.tv">Doctor Who</a>
is a registered trademark of the <a href="https://www.bbc.com/">BBC</a>.
</p>
@code {
private bool callResultForBoolean;
private string callResultForString;
private void CallJSUnmarshalledForBoolean()
{
var unmarshalledRuntime = (IJSUnmarshalledRuntime)JS;
var jsUnmarshalledReference = unmarshalledRuntime
.InvokeUnmarshalled<IJSUnmarshalledObjectReference>(
"returnObjectReference");
callResultForBoolean =
jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, bool>(
"unmarshalledFunctionReturnBoolean", GetStruct());
}
private void CallJSUnmarshalledForString()
{
var unmarshalledRuntime = (IJSUnmarshalledRuntime)JS;
var jsUnmarshalledReference = unmarshalledRuntime
.InvokeUnmarshalled<IJSUnmarshalledObjectReference>(
"returnObjectReference");
callResultForString =
jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, string>(
"unmarshalledFunctionReturnString", GetStruct());
}
private InteropStruct GetStruct()
{
return new InteropStruct
{
Name = "Brigadier Alistair Gordon Lethbridge-Stewart",
Year = 1968,
};
}
[StructLayout(LayoutKind.Explicit)]
public struct InteropStruct
{
[FieldOffset(0)]
public string Name;
[FieldOffset(8)]
public int Year;
}
}
IJSUnmarshalledObjectReference Jeśli wystąpienie nie jest usuwane w kodzie języka C#, można go usunąć w pliku JS. Następująca dispose
funkcja usuwa odwołanie do obiektu po wywołaniu z elementu JS:
window.exampleJSObjectReferenceNotDisposedInCSharp = () => {
return {
dispose: function () {
DotNet.disposeJSObjectReference(this);
},
...
};
}
Typy tablic można konwertować z JS obiektów na obiekty platformy .NET przy użyciu klasy js_typed_array_to_array
, ale tablica JS musi być tablicą typową. Tablice z JS programu można odczytać w kodzie języka C# jako tablicę obiektów platformy .NET (object[]
).
Inne typy danych, takie jak tablice ciągów, można przekonwertować, ale wymagają utworzenia nowego obiektu tablicy Mono (mono_obj_array_new
) i ustawienia jego wartości (mono_obj_array_set
).
Ostrzeżenie
JS funkcje udostępniane przez platformę Blazor , takie jak js_typed_array_to_array
, mono_obj_array_new
i mono_obj_array_set
, podlegają zmianom nazw, zmianom behawioralnym lub usunięciu w przyszłych wersjach platformy .NET.
Usuwanie odwołań do obiektów międzyoperacyjnych języka JavaScript
Przykłady w artykułach międzyoperacyjnych języka JavaScript (JS) przedstawiają typowe wzorce usuwania obiektów:
Podczas wywoływania JS z platformy .NET, zgodnie z opisem w tym artykule, należy usunąć wszystkie utworzone z/IJSInProcessObjectReference/
JSObjectReference
IJSObjectReferenceplatformy .NET lub uniknąć JS wycieku JS pamięci.Podczas wywoływania platformy .NET z programu zgodnie z JSopisem w temacie Wywoływanie metod .NET z funkcji JavaScript w programie ASP.NET Core Blazornależy usunąć utworzony DotNetObjectReference element z platformy .NET lub uniknąć JS wycieku pamięci platformy .NET.
JS Odwołania do obiektów międzyoperacyjności są implementowane jako mapowane przez identyfikator po stronie wywołania międzyoperacyjnego JS , które tworzy odwołanie. Gdy usuwanie obiektu jest inicjowane z platformy .NET lub JS po stronie, Blazor usuwa wpis z mapy, a obiekt może zostać odśmiecany, o ile nie ma innego silnego odwołania do obiektu.
Co najmniej zawsze usuwaj obiekty utworzone po stronie platformy .NET, aby uniknąć wycieku pamięci zarządzanej platformy .NET.
Zadania oczyszczania modelu DOM podczas usuwania składników
Aby uzyskać więcej informacji, zobacz ASP.NET Core Blazor JavaScript interoperability (JS interop).
Wywołania międzyoperacyjne języka JavaScript bez obwodu
Aby uzyskać więcej informacji, zobacz ASP.NET Core Blazor JavaScript interoperability (JS interop).
Dodatkowe zasoby
- Wywoływanie metod platformy .NET z funkcji języka JavaScript z na platformie ASP.NET Core Blazor
InteropComponent.razor
example (dotnet/AspNetCore
Gałąź repozytoriummain
GitHub):main
Gałąź reprezentuje bieżący rozwój jednostki produktu dla następnej wersji ASP.NET Core. Aby wybrać gałąź dla innej wersji (na przykładrelease/5.0
), użyj listy rozwijanej Przełącz gałęzie lub tagi , aby wybrać gałąź.- Blazorprzykładowe repozytorium GitHub () (
dotnet/blazor-samples
jak pobrać) - Obsługa błędów w aplikacjach ASP.NET Core Blazor (sekcja międzyoperacji w języku JavaScript)
- Ograniczanie zagrożeń: funkcje języka JavaScript wywoływane z platformy .NET