Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz artykuł wersji .NET 9.
Ostrzeżenie
Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz zasady pomocy technicznej platformy .NET i platformy .NET Core. Aby zapoznać się z bieżącą wersją, zobacz artykuł wersji .NET 9.
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ą, zobacz artykuł wersji .NET 9.
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 JS, zobacz Wywoływanie metod .NET z funkcji JavaScript w ASP.NET Core Blazor.
Wywoływanie JS funkcji
IJSRuntime jest zarejestrowany przez framework Blazor. Aby wywołać JS z platformy .NET, zaimplementuj abstrakcję IJSRuntime i użyj jednej z następujących metod:
W przypadku wcześniejszych metod .NET, które wywołują funkcje JS:
- Identyfikator funkcji (
String
) jest względny względem zakresu globalnego (window
). Aby wywołaćwindow.someScope.someFunction
, identyfikator tosomeScope.someFunction
. Nie ma potrzeby rejestrowania funkcji przed jej wywołaniami. - Do funkcji
Object[]
przekaż dowolną liczbę argumentów JSON-serializowalnych w JS. - 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 JS
Promise
jest zwracany dla metodInvokeAsync
.InvokeAsync
odpakowujePromise
i zwraca wartość oczekiwaną przezPromise
.
W przypadku Blazor aplikacji z włączonym prerenderingiem, co jest domyślnym ustawieniem dla aplikacji po stronie serwera, wywoływanie JS nie jest możliwe podczas prerenderingu. Aby uzyskać więcej informacji, zobacz sekcję Prerendering .
Poniższy przykład oparty jest na dekoderze bazującym na TextDecoder
i JS. W tym przykładzie pokazano, jak wywołać funkcję JS z metody C#, która przenosi wymaganie z kodu dewelopera na istniejący interfejs API JS. 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 lokalizacji JS i naszych zaleceń dotyczących aplikacji produkcyjnych, zobacz lokalizację JavaScript w aplikacjach ASP.NET CoreBlazor.
Następujący składnik:
-
convertArray
JS wywołuje funkcję z InvokeAsync po wybraniu przycisku (Convert Array
). - Po wywołaniu funkcji, przekazana tablica JS 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));
}
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, na przykład przy użyciu polecenia Fullscreen API
. Te interfejsy API nie mogą być wywoływane za pośrednictwem mechanizmu międzyoperacyjnego JS w składnikach po stronie serwera, ponieważ obsługa zdarzeń interfejsu użytkownika jest wykonywana asynchronicznie i na ogół 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 atrybutu dyrektywy Blazor@onclick
.
Wywoływanie funkcji Języka JavaScript bez odczytywania zwróconej wartości (InvokeVoidAsync
)
Użyj InvokeVoidAsync, 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 lokalizacji JS i naszych zaleceń dotyczących aplikacji produkcyjnych, zobacz lokalizację JavaScript w aplikacjach ASP.NET CoreBlazor.
Przykład składnika (.razor
) (InvokeVoidAsync
)
TickerChanged
wywołuje metodę handleTickerChanged1
w następującym komponencie.
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);
}
}
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;
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 komponencie.
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();
}
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 lokalizacji JS i naszych zaleceń dotyczących aplikacji produkcyjnych, zobacz lokalizację 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}";
}
}
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;
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();
}
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; }
Wstępne renderowanie
Ta sekcja dotyczy aplikacji po stronie serwera, które prerenderują składniki Razor. Prerendering jest omówione w Prerender ASP.NET Core Razor komponentach.
Uwaga
Nawigacja wewnętrzna na potrzeby routingu interakcyjnego w Blazor Web Appnie obejmuje żądania nowej zawartości strony z serwera. W związku z tym prerenderowanie nie występuje w przypadku żądań stron wewnętrznych. Jeśli aplikacja przyjmuje interaktywny routing, wykonaj ponowne ładowanie pełnej strony dla przykładów komponentów, które ilustrują zachowanie podczas wstępnego renderowania. Aby uzyskać więcej informacji, przeczytaj
Ta sekcja dotyczy aplikacji po stronie serwera i hostowanych Blazor WebAssembly aplikacji, które prerenderują Razor składniki. Renderowanie wstępne jest omówione w temacie Integrowanie składników ASP.NET Core Razor z MVC lub Razor Pages.
Podczas wstępnego przetwarzania wywołanie kodu JavaScript (JS) nie jest możliwe. W poniższym przykładzie pokazano, jak używać JS interop w ramach logiki inicjalizacji komponentu w sposób zgodny z prerenderingiem.
Następująca scrollElementIntoView
funkcja:
- Przewija do przekazanego elementu za pomocą
scrollIntoView
. - Zwraca wartość właściwości elementu
top
zgetBoundingClientRect
metody .
window.scrollElementIntoView = (element) => {
element.scrollIntoView();
return element.getBoundingClientRect().top;
}
Gdy IJSRuntime.InvokeAsync wywołuje funkcję JS w kodzie komponentu, ElementReference jest używane tylko w OnAfterRenderAsync, a nie w żadnej wcześniejszej metodzie cyklu życia, ponieważ element DOM HTML nie istnieje aż do momentu renderowania komponentu.
StateHasChanged
(źródło referencyjne) jest wywoływane w celu umieszczenia w kolejce ponownego renderowania składnika z nowym stanem uzyskanym z JS wywołania interopu (aby uzyskać więcej informacji, zobacz ASP.NET Core Razor komponentów rendering). 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.
Synchroniczna interoperacyjność 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 zapewnić zgodność komponentów z trybami renderowania po stronie serwera i klienta. Na serwerze wszystkie JS wywołania międzyoperacyjne muszą być asynchroniczne, ponieważ są wysyłane za pośrednictwem połączenia sieciowego.
Jeśli z całą pewnością wiesz, że twój komponent działa tylko na WebAssembly, możesz zdecydować się na 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 na IJSInProcessRuntime, aby wywołać interfejs współpracy 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 powinien być usunięty do odzyskiwania pamięci, aby zapobiec wyciekowi pamięci, jak pokazano w poniższym przykładzie:
@inject IJSRuntime JS
@implements IDisposable
...
@code {
...
private IJSInProcessObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var jsInProcess = (IJSInProcessRuntime)JS;
module = await jsInProcess.Invoke<IJSInProcessObjectReference>("import",
"./scripts.js");
var value = module.Invoke<string>("javascriptFunctionIdentifier");
}
}
...
void IDisposable.Dispose()
{
if (module is not null)
{
await module.Dispose();
}
}
}
W poprzednim przykładzie element JSDisconnectedException nie jest uwięziony podczas usuwania modułu, ponieważ w aplikacji Blazor nie ma obwodu SignalR-Blazor WebAssembly, który można utracić. Aby uzyskać więcej informacji, zobacz ASP.NET Core Blazor JavaScript interoperability (JS interop).
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) -
Załaduj skrypt 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
) - Wstrzyknij skrypt przed lub po Blazor jego uruchomieniu
Aby uzyskać informacje na temat izolowania skryptów w JS modułach, zobacz rozdział Izolacja JavaScript w modułach JavaScript.
Ostrzeżenie
Umieść <script>
tag tylko w pliku składnika (.razor
), jeśli składnik ma gwarancję adopcji 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
Blazor umożliwia izolację JavaScript (JS) w standardowych modułach 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 MDN Web Docs: JavaScript modules.
Izolacja 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 {CONDITION}
reprezentuje sprawdzanie warunkowe, aby określić, czy moduł powinien zostać załadowany.
Aby uzyskać informacje o zgodności przeglądarki, zobacz Czy mogę używać: moduły JavaScript: import dynamiczny.
W scenariuszach po stronie serwera po rozłączeniu obwodu JS nie można wydać wywołań międzyoperacyjnych Blazor. Gdy brakuje obwodu podczas usuwania składników lub w dowolnym innym momencie, gdy obwód nie istnieje, wywołania następującej metody kończą się niepowodzeniem i rejestrują komunikat, że obwód jest odłączony jako JSDisconnectedException.
- JS wywołania metody interoperacyjnej
-
Dispose
/DisposeAsync
wywołuje każde IJSObjectReference.
Aby uniknąć rejestrowania JSDisconnectedException lub zarejestrować niestandardowe informacje po stronie serwera Blazor, należy przechwycić wyjątek w instrukcji try-catch
.
W poniższym przykładzie usuwania składników:
- Składnik po stronie serwera implementuje IAsyncDisposable.
-
module
jest elementem IJSObjectReference dla modułu JS . - JSDisconnectedException jest przechwycony i nie jest rejestrowany.
- Opcjonalnie możesz zapisywać niestandardowe informacje w instrukcji
catch
na dowolnym poziomie rejestrowania, jaki preferujesz. Poniższy przykład nie rejestruje informacji niestandardowych. W kodzie przyjęto założenie, że deweloper nie dba o to, kiedy lub gdzie obwody są rozłączane podczas usuwania składników.
async ValueTask IAsyncDisposable.DisposeAsync()
{
try
{
if (module is not null)
{
await module.DisposeAsync();
}
}
catch (JSDisconnectedException)
{
}
}
Jeśli musisz wyczyścić własne JS obiekty lub wykonać inny JS kod na kliencie po utracie obwodu w aplikacji Blazor po stronie serwera, użyj wzorca MutationObserver
w JS na kliencie. Wzorzec MutationObserver
umożliwia wykonywanie JS kodu po usunięciu elementu z modelu DOM.
Aby uzyskać więcej informacji, zobacz następujące artykuły:
- JS: zawiera przykład wzorca kodu .
- Obsługa błędów w aplikacjach ASP.NET Core: Sekcja JavaScript interop omawia obsługę błędów w scenariuszach interoperacyjnych.
- ASP.NET Core Razor usuwanie komponentów: W artykule opisano, jak implementować wzorce usuwania w komponentach Razor.
JS Poniższy moduł eksportuje funkcję JS do wyświetlania monitu okna 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 wcześniej wymieniony moduł JS do aplikacji lub biblioteki klas jako statyczny zasób w folderze wwwroot
, a następnie zaimportuj moduł do kodu platformy .NET, wywołując InvokeAsync na wystąpieniu IJSRuntime.
IJSRuntime importuje moduł jako element IJSObjectReference, który reprezentuje odwołanie do obiektu JS 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)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
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)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
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 podwwwroot
.
- Segment ścieżki dla bieżącego katalogu (
- Usuwa element IJSObjectReference do zbierania śmieci w IAsyncDisposable.DisposeAsync.
- Nie umieszczaj tagu
<script>
dla skryptu Blazor po, ponieważ moduł jest ładowany i buforowany automatycznie, kiedy wywoływana jest dynamicznaimport()
.
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ę Synchronicznej współpracy JS w komponentach po stronie klienta.
Uwaga
Gdy plik zewnętrzny
- 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 dla biblioteki nie zawiera<PackageId>
. - Symbol zastępczy
{SCRIPT PATH AND FILE NAME (.js)}
to ścieżka i nazwa pliku podwwwroot
. W poniższym przykładzie plik zewnętrzny JS (script.js
) jest umieszczany w folderze bibliotekiwwwroot
klas. -
module
jest prywatną zmienną typu nullable 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 Aside — .mjs versus .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 inicjalizacji, lub możesz potrzebować wywołać API przypominające polecenia na 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 nie wchodzi w interakcję z elementem, nie ma możliwości konfliktu między reprezentacją Blazor elementu a elementem DOM.
W poniższym przykładzie niebezpieczne jest użycie przez ul
interop do modyfikowania zawartości nieuporządkowanej listy (MyList
), ponieważ JS oddziałuje z DOM, aby uzupełnić elementy tej listy (Blazor) z obiektu <li>
.
<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 zmienia 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 interop 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 interopu. Kod JS odbiera instancję HTMLElement
, które może być używane ze standardowymi 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 JSclickElement
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, wywołując poprzedzającą JS funkcję z przechwyconym ElementReference:
@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 przyjmuje 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 . Przykład poniżej zakłada, że metoda TriggerClickEvent
jest dostępna w przestrzeni nazw JsInteropClasses
:
@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 nieuzupełniony ElementReference zostanie przekazany do kodu JS, kod JS otrzyma wartość null
. Aby manipulować odwołaniami do elementów po zakończeniu renderowania składnika, użyj metod cyklu życia komponentu OnAfterRenderAsync
lub OnAfterRender
.
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 zastępczy {JAVASCRIPT FUNCTION}
jest identyfikatorem funkcji JS.
GenericMethod
jest wywoływany bezpośrednio na obiekcie z określonym typem. W poniższym przykładzie zakłada się, że GenericMethod
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
Nie można przekazać ElementReference pomiędzy komponentami, ponieważ:
- Wystąpienie jest gwarantowane, że istnieje tylko po renderowaniu składnika, podczas lub po wykonaniu metody składnika OnAfterRender/OnAfterRenderAsync.
- Element ElementReference to
struct
, którego nie można przekazać jako parametru składnika.
Aby komponent nadrzędny udostępnił referencję do innych komponentów, może:
- Zezwalaj komponentom 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 komponentom dzieci interakcję z referencją elementu rodzica.
<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 lokalizacji JS i naszych zaleceń dotyczących aplikacji produkcyjnych, zobacz lokalizację 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?" />
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);
}
}
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=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 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.
Utwardzanie wywołań międzyoperacyjnych 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ą jednominutowego limitu czasu dla JS wywołań międzyoperacyjnych. 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 elemencie za pomocą Program.cs
CircuitOptions.JSInteropDefaultCallTimeout:
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents(options =>
options.JSInteropDefaultCallTimeout = {TIMEOUT});
builder.Services.AddServerSideBlazor(
options => options.JSInteropDefaultCallTimeout = {TIMEOUT});
Ustaw globalny limit czasu w metodzie Startup.ConfigureServices
za pomocą Startup.cs
z CircuitOptions.JSInteropDefaultCallTimeout polecenia:
services.AddServerSideBlazor(
options => options.JSInteropDefaultCallTimeout = {TIMEOUT});
Symbol zastępczy {TIMEOUT}
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}, [ "Arg1" ]);
var result = await JS.InvokeAsync<string>("{ID}", {TIMEOUT}, new[] { "Arg1" });
W powyższym przykładzie:
- Symbol zastępczy
{TIMEOUT}
to TimeSpan (na przykładTimeSpan.FromSeconds(80)
). - Placeholder
{ID}
jest identyfikatorem funkcji do wywołania. Na przykład wartośćsomeScope.someFunction
wywołuje funkcjęwindow.someScope.someFunction
.
Chociaż częstą przyczyną JS niepowodzeń interoperacyjnych są błędy sieciowe związane ze składnikami po stronie serwera, dla JS wywołań interoperacyjnych w składnikach po stronie klienta można ustawić limity czasu wywołania. Mimo że nie istnieje obwód Blazor—SignalR dla składnika po stronie klienta, wywołania interoperacyjne JS mogą zakończyć się niepowodzeniem z innych powodów, które mają zastosowanie.
Aby uzyskać więcej informacji na temat wyczerpania zasobów, zobacz Wytyczne dotyczące łagodzenia zagrożeń dla ASP.NET Core Blazor interaktywnego renderowania po stronie serwera.
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 metod JavaScript z C#, gdy zwracany typ zawiera odniesienia 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ż system różnicowania Blazor opiera się na kontroli nad drzewem elementów DOM i napotyka błędy, jeśli niektóre zewnętrzne kody modyfikują drzewo DOM i unieważniają jego mechanizm nakładania różnic. Nie jest to ograniczeniem specyficznym dla Blazor. To samo wyzwanie występuje w przypadku dowolnej platformy interfejsu użytkownika opartej na różnicach.
Na szczęście można niezawodnie osadzić zewnętrznie generowany interfejs użytkownika w interfejsie użytkownika składnika Razor. 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
, gdy firstRender
jest true
, nawiąż interakcję z unmanagedElement
poza Blazor za pomocą interop JS. Na przykład wywołaj bibliotekę zewnętrzną JS , aby wypełnić element.
Blazor pozostawia zawartość elementu nienaruszoną do czasu usunięcia tego komponentu. Po usunięciu składnika całe poddrzewo DOM składnika jest również usuwane.
<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 open-source'owych API Mapbox.
JS Poniższy moduł jest 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 element <link>
do znacznika elementu <head>
(lokalizacja zawartości <head>
):
<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)
{
try
{
await mapInstance.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
if (mapModule is not null)
{
try
{
await mapModule.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
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)
{
try
{
await mapInstance.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
if (mapModule is not null)
{
try
{
await mapModule.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
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żesz przeciągnąć, aby przewinąć lub powiększyć.
- Wybierz przyciski, aby przejść do wstępnie zdefiniowanych lokalizacji.
W powyższym przykładzie:
- W odniesieniu do
<div>
,@ref="mapElement"
z Blazor jest pozostawiony pusty. Skryptmapbox-gl.js
może bezpiecznie wypełnić element i zmodyfikować jego zawartość. Użyj tej techniki z dowolną JS biblioteką, która renderuje interfejs użytkownika. Można osadzać komponenty z innego frameworka SPA JS wewnątrz Razor komponentów, o ile nie próbują ingerować w i modyfikować inne części strony. Nie jest bezpieczne dla zewnętrznego kodu modyfikowanie elementów, które nie są uznawane za puste. - Podczas korzystania z tego podejścia, należy mieć na uwadze zasady, jak Blazor zachowuje lub niszczy elementy 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 renderujesz listę elementów mapy w środku pętli
@foreach
, chcesz ty użyć@key
, aby zapewnić zachowanie wystąpień komponentów. W przeciwnym razie zmiany w danych listy mogą spowodować, że instancje komponentów zachowają stan poprzednich instancji 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 za pomocą identyfikatora
import
. Aby uzyskać więcej informacji, zobacz Izolacja języka JavaScript w modułach JavaScript.
Obsługa tablic bajtów
Blazor obsługuje zoptymalizowaną międzyoperacyjną tablicę bajtów JavaScript (JS), która unika kodowania/dekodowania tablic bajtowych do Base64. W poniższym przykładzie użyto JS funkcji interop w celu przekazania tablicy bajtów do 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 lokalizacji JS i naszych zaleceń dotyczących aplikacji produkcyjnych, zobacz lokalizację 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 z .NET do JavaScript
Blazor obsługuje przesyłanie strumieniowe danych bezpośrednio z platformy .NET do języka JavaScript (JS). Strumienie są tworzone za pomocą elementu DotNetStreamReference.
DotNetStreamReference reprezentuje strumień platformy .NET i używa następujących parametrów:
-
stream
: strumień wysłany do JS. -
leaveOpen
: określa, czy strumień jest pozostawiony otwarty po transmisji. Jeśli wartość nie jest podana,leaveOpen
wartość domyślna tofalse
.
W JS użyj bufora tablicy lub strumienia do odczytu, by otrzymywać 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 zastępczy
{STREAM}
reprezentuje Stream wysłany do elementu JS. -
JS
jest wstrzykniętą instancją IJSRuntime.
Zwykle nie jest konieczne pozbywanie się wystąpienia DotNetStreamReference. Gdy parametr leaveOpen
jest ustawiony na swoją wartość domyślną false
, element bazowy Stream jest automatycznie usuwany po transmisji do JS.
Jeśli leaveOpen
jest true
, wówczas usunięcie DotNetStreamReference nie powoduje usunięcia jego bazowego Stream. 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:
- Podczas przesyłania Stream do JS, jego usunięcie jest uznawane za błąd aplikacji i może spowodować wystąpienie nieobsługiwanego wyjątku.
- Stream transmisja zaczyna się w momencie, gdy DotNetStreamReference zostaje przekazane jako argument do JS wywołania międzyoperacyjnego, niezależnie od tego, czy strumień faktycznie jest używany w JS logice.
Biorąc pod uwagę te cechy, zalecamy usunięcie bazowego Stream dopiero po jego pełnym zużyciu przez JS (obietnica zwrócona przez arrayBuffer
lub stream
zostanie rozwiązana). Wynika z tego, że DotNetStreamReference powinien być przekazany do JS tylko wtedy, gdy jest bezwarunkowo przetwarzany przez logikę JS.
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.
Pobieranie plików w ASP.NET Core Blazor obejmuje sposób pobierania plików w programie Blazor.
Przechwytywanie wyjątków języka JavaScript
Aby przechwycić JS wyjątki, opakuj interoperacyjność JS w try
-catch
bloku i przechwyć JSException.
W poniższym przykładzie nonFunction
JS funkcja nie istnieje. Gdy funkcja nie zostanie znaleziona, JSException jest przechwytywany przez Message, który wskazuje na 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}";
}
}
}
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
JS Użyj AbortController z CancellationTokenSource w składniku, aby przerwać długotrwałą funkcję JavaScript z kodu C#.
Poniższa JSHelpers
klasa zawiera symulowaną długotrwałą funkcję, longRunningFn
, która liczy w sposób ciągły aż do momentu, gdy AbortController.signal
wskaże, że AbortController.abort
została wywołana. 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
, longRunningFn
jest sygnalizowany do przerwania poprzez warunkowe sprawdzenie w pętli while
na 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 lokalizacji JS i naszych zaleceń dotyczących aplikacji produkcyjnych, zobacz lokalizację JavaScript w aplikacjach ASP.NET CoreBlazor.
Następujący składnik:
- Wywołuje funkcję JS
longRunningFn
po wybraniu przyciskuStart Task
. Element A CancellationTokenSource służy do zarządzania wykonywaniem długotrwałej funkcji. CancellationToken.Register ustawia delegata wywołania międzyoperacyjnego JS do wykonania funkcji JSstopFn
kiedy CancellationTokenSource.Token zostanie anulowany. - Po wybraniu przycisku
Cancel Task
, CancellationTokenSource.Token zostaje anulowany za pomocą wywołania metody Cancel. - Element CancellationTokenSource jest usuwany metodą
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 oraz przerwanie tej funkcji 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 komponentach po stronie klienta przy użyciu mechanizmu międzyoperacyjnego BlazorJS opartego na interfejsie IJSRuntime jest dostępny interfejs API do międzyoperacyjności JS[JSImport]
/[JSExport]
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).
Nieprzetworzona interoperacyjność 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 interoperacyjnością z JavaScriptem[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).
Nieprzetworzona interoperacyjność JavaScript
Blazor WebAssembly Składniki mogą mieć obniżoną wydajność, gdy obiekty .NET są serializowane dla współpracy z JavaScript (JS) i spełniony jest jeden z następujących warunków:
- Duża liczba obiektów platformy .NET jest szybko serializowana. Na przykład niska wydajność może wystąpić, gdy wywołania interop JS są wykonywane na podstawie ruchu 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 słaba wydajność może wynikać, gdy JS wywołania międzyoperacyjne 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 znaków i liczbę całkowitą jest przekazywana nieserializowana 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
w celu zarządzania konwersją ciągu JS.
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 lokalizacji JS i naszych zaleceń dotyczących aplikacji produkcyjnych, zobacz lokalizację 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 framework.
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;
}
}
Jeśli wystąpienie IJSUnmarshalledObjectReference nie zostanie zniszczone w kodzie języka C#, można je zniszczyć w 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 można odczytać w kodzie języka C# jako tablicę obiektów .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 obiekty, zarówno z platformy .NET, jak i z IJSObjectReference, aby uniknąć wycieku pamięci /.
pl-PL: Podczas wywoływania platformy .NET z JS, zgodnie z tematem Wywoływanie metod .NET z funkcji JavaScript w programie ASP.NET Core Blazor, należy usunąć utworzony DotNetObjectReference zarówno z poziomu platformy .NET, jak i z poziomu JS, aby uniknąć wycieku pamięci platformy .NET.
JS Odwołania do obiektów międzyoperacyjności są implementowane jako mapa, której kluczami są identyfikatory po stronie wywołania międzyoperacyjnego JS tworzącego odwołanie. Gdy usuwanie obiektu jest inicjowane po stronie platformy .NET lub JS, Blazor usuwa wpis z mapy, a obiekt może podlegać odśmiecaniu, o ile nie istnieje inne silne odwołanie do obiektu.
Co najmniej zawsze usuwaj obiekty utworzone po stronie platformy .NET, aby uniknąć wycieku pamięci zarządzanej platformy .NET.
Czyszczenie DOM podczas usuwania komponentó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/{VERSION}
, gdzie{VERSION}
to wersja wydania), użyj listy rozwijanej Przełącz gałęzie lub tagi, aby wybrać gałąź. W przypadku gałęzi, która już nie istnieje, użyj karty Tagi , aby znaleźć interfejs API (na przykładv7.0.0
). -
Blazor (
dotnet/blazor-samples
) - Obsługa błędów w aplikacjach ASP.NET Core (sekcja Blazor)
- Ograniczanie zagrożeń: funkcje języka JavaScript wywoływane z platformy .NET