JavaScript-függvények meghívása .NET-metódusokból a ASP.NET Core Blazor
Megjegyzés
Ez nem a cikk legújabb verziója. Az aktuális kiadásért lásd e cikk .NET 9-es változatát.
Figyelmeztetés
A ASP.NET Core ezen verziója már nem támogatott. További információ: .NET és .NET Core támogatási szabályzat. Az aktuális kiadásért lásd e cikk .NET 9-es változatát.
Fontos
Ezek az információk egy olyan előzetes termékre vonatkoznak, amelyet a kereskedelmi forgalomba kerülés előtt jelentősen módosíthatnak. A Microsoft nem vállal kifejezett vagy hallgatólagos szavatosságot az itt megadott információkra vonatkozóan.
Az aktuális kiadásért lásd e cikk .NET 9-es változatát.
Ez a cikk bemutatja, hogyan hívhatja meg a JavaScript (JS) függvényeket a .NET-ből.
A .NET-metódusok
A IJSRuntime-t a Blazor keretrendszer regisztrálja. Ha a .NET-ből JS szeretne meghívni, injektálja a IJSRuntime absztrakciót, és hívja meg az alábbi módszerek egyikét:
Az előző .NET-metódusok esetében, amelyek JS függvényeket hívnak meg:
- A függvényazonosító (
String
) a globális hatókörhöz (window
) van viszonyítva. Awindow.someScope.someFunction
hívásához használd azsomeScope.someFunction
azonosítót. A függvény meghívása előtt nincs szükség a függvény regisztrálására. - Adjon át tetszőleges számú JSON-szerializálható argumentumot
Object[]
egy JS függvénynek. - A törlési token (
CancellationToken
) propagálja a műveletek megszakítására vonatkozó értesítést. -
TimeSpan
egy JS művelet időkorlátját jelöli. - A
TValue
visszatérési típusnak JSON szerializálhatónak is kell lennie.TValue
egyeznie kell azzal a .NET-típussal, amely a legjobban megfelel a visszaadott JSON-típusnak. - A JS
Promise
adódik vissza aInvokeAsync
metódusokhoz. AInvokeAsync
kibontja aPromise
-et, és visszaadja aPromise
által várt értéket.
Olyan Blazor alkalmazások esetében, amelyeken engedélyezve van az előrendelés, amely a kiszolgálóoldali alkalmazások alapértelmezett beállítása, az előrendelés során nem lehet meghívni a JS. További információkért lásd a Prerendering szakaszt.
Az alábbi példa egy TextDecoder
-re, egy JS-alapú dekóderre épül. A példa bemutatja, hogyan hívhat meg egy JS függvényt egy olyan C# metódusból, amely egy követelményt tölt ki a fejlesztői kódból egy meglévő JS API-ba. A JS függvény egy C# metódusból fogad el egy bájttömböt, dekódolja a tömböt, és visszaadja a szöveget az összetevőnek megjelenítésre.
<script>
window.convertArray = (win1251Array) => {
var win1251decoder = new TextDecoder('windows-1251');
var bytes = new Uint8Array(win1251Array);
var decodedArray = win1251decoder.decode(bytes);
return decodedArray;
};
</script>
Megjegyzés
A JS helyére és az éles alkalmazásokkal kapcsolatos ajánlásainkra vonatkozó általános útmutatásért nézze meg a JavaScript-helyet az ASP.NET Core Blazor alkalmazásokban.
A következő összetevő:
- Meghívja a
convertArray
JS függvényt InvokeAsync gomb (Convert Array
) kiválasztásakor. - A JS függvény meghívása után a rendszer sztringgé alakítja az átadott tömböt. A rendszer visszaadja a sztringet az összetevőnek megjelenítés céljából (
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));
}
}
Ez a szakasz a kiszolgálóoldali összetevőkre vonatkozik.
Egyes böngészőbeli JavaScript -API-k (JS) csak felhasználói kézmozdulatok kontextusában hajthatók végre, például a Fullscreen API
(MDN-dokumentáció). Ezek az API-k nem hívhatók meg a kiszolgálóoldali összetevők JS interop mechanizmusán keresztül, mert a felhasználói felület eseménykezelése aszinkron módon történik, és általában már nem a felhasználói kézmozdulat kontextusában történik. Az alkalmazásnak teljesen a JavaScriptben kell kezelnie a felhasználói felület eseményét, ezért onclick
kell használnia Blazor@onclick
direktíva attribútuma helyett.
Használja a InvokeVoidAsync-t, amikor:
- A .NET nem szükséges a JavaScript (JS) hívás eredményének olvasásához.
- JS függvények void(0)/void 0 vagy undefinedértékkel térnek vissza.
Adjon meg egy displayTickerAlert1
JS függvényt. A függvény InvokeVoidAsync van meghívva, és nem ad vissza értéket:
<script>
window.displayTickerAlert1 = (symbol, price) => {
alert(`${symbol}: $${price}!`);
};
</script>
Megjegyzés
A JS helyére és az éles alkalmazásokkal kapcsolatos ajánlásainkra vonatkozó általános útmutatásért nézze meg a JavaScript-helyet az ASP.NET Core Blazor alkalmazásokban.
TickerChanged
az alábbi összetevőben meghívja a handleTickerChanged1
metódust.
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);
}
}
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
az alábbi összetevőben meghívja a handleTickerChanged1
metódust.
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();
}
Használja a InvokeAsync, amikor a .NET-nek be kell olvasnia egy JavaScript -JS- hívás eredményét.
Adjon meg egy displayTickerAlert2
JS függvényt. Az alábbi példa egy sztringet ad vissza, amelyet a hívó megjelenít:
<script>
window.displayTickerAlert2 = (symbol, price) => {
if (price < 20) {
alert(`${symbol}: $${price}!`);
return "User alerted in the browser.";
} else {
return "User NOT alerted.";
}
};
</script>
Megjegyzés
A JS helyére és az éles alkalmazásokkal kapcsolatos ajánlásainkra vonatkozó általános útmutatásért nézze meg a JavaScript-helyet az ASP.NET Core Blazor alkalmazásokban.
TickerChanged
meghívja a handleTickerChanged2
metódust, és megjeleníti a visszaadott sztringet az alábbi összetevőben.
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}";
}
}
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
meghívja a handleTickerChanged2
metódust, és megjeleníti a visszaadott sztringet az alábbi összetevőben.
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();
}
A BuildRenderTree
[Inject]
IJSRuntime JS { get; set; }
Ez a szakasz azokra a kiszolgálóoldali alkalmazásokra vonatkozik, amelyek előre renderelik a Razor összetevőket. Az előzetes renderelés Prerender ASP.NET Core Razor összetevők.
Megjegyzés
A ben a Blazor Web App belső navigációja nem foglalja magában az új laptartalom kérését a kiszolgálótól. Ezért a belső lapkérelmek esetében nem történik meg az előfeldolgozás. Ha az alkalmazás interaktív útválasztást vezet be, végezzen teljes oldali újratöltést az előfeldolgozási viselkedést szemléltető komponensek példáihoz. További információ: Prerender ASP.NET Core Razor-összetevők.
Ez a szakasz a kiszolgálóoldali alkalmazásokra és a Blazor WebAssembly alkalmazásként hosztolt, Razor összetevőket előrenderelő alkalmazásokra vonatkozik. Az előrenderelés az ASP.NET Core Razor-összetevők integrálása az MVC-vel vagy az Razor Oldalak.
A prerenderelés során a JavaScript-be (JS) való meghívás nem lehetséges. Az alábbi példa bemutatja, hogyan használható JS interop egy összetevő inicializálási logikájának részeként az előrendeléssel kompatibilis módon.
A következő scrollElementIntoView
függvény:
- Görgessen az átadott elemhez
scrollIntoView
. - Az elem
top
tulajdonságértékét adja vissza agetBoundingClientRect
metódusból.
window.scrollElementIntoView = (element) => {
element.scrollIntoView();
return element.getBoundingClientRect().top;
}
Ahol IJSRuntime.InvokeAsync az összetevőkódban a JS függvényt hívja meg, ott a ElementReference csak a OnAfterRenderAsync-ban van használva, és nem egyetlen korábbi életciklus-metódusban sem, mivel nincs HTML DOM-elem, amíg az összetevő nincsen renderelve.
StateHasChanged
(referenciaforrás) meghívásra kerül, hogy sorba állítsa az összetevő újrarenderelését az JS interop hívásból kapott új állapottal (további információ: ASP.NET Core Razor összetevő renderelése). A rendszer nem hoz létre végtelen hurkot, mert StateHasChanged csak akkor hívjuk meg, ha scrollPosition
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();
}
}
}
Az előző példa egy globális függvénnyel szennyezi a klienst. A termelési alkalmazások jobb megközelítéséhez lásd a JavaScript-elkülönítést a JavaScript-modulokban .
Ez a szakasz csak az ügyféloldali összetevőkre vonatkozik.
JS interoperabilitási hívások aszinkron módon működnek, függetlenül attól, hogy a hívott kód szinkron vagy aszinkron. A hívások aszinkron módon biztosítják, hogy az összetevők kompatibilisek legyenek a kiszolgálóoldali és az ügyféloldali renderelési módok között. A kiszolgálón minden JS interop-hívásnak aszinkronnak kell lennie, mert hálózati kapcsolaton keresztül küldi őket.
Ha biztosan tudja, hogy az összetevő csak a WebAssemblyen fut, választhat, hogy szinkron JS interop-hívásokat kezdeményez. Ez valamivel kisebb többletterheléssel jár, mint az aszinkron hívások végrehajtása, és kevesebb renderelési ciklust eredményezhet, mivel nincs köztes állapot az eredményekre várva.
Egy ügyféloldali összetevőben történő, .NET-ről JavaScriptre végrehajtott szinkron híváshoz a IJSRuntime interop-hívás érdekében konvertálja a IJSInProcessRuntime-t JS-re.
@inject IJSRuntime JS
...
@code {
protected override void HandleSomeEvent()
{
var jsInProcess = (IJSInProcessRuntime)JS;
var value = jsInProcess.Invoke<string>("javascriptFunctionIdentifier");
}
}
Ha a IJSObjectReference ASP.NET Core 5.0-s vagy újabb ügyféloldali összetevőiben dolgozik, akkor használhatja a IJSInProcessObjectReference-et szinkron módon. IJSInProcessObjectReference implementálja a IAsyncDisposable/IDisposable-at, és a memóriaszivárgás elkerülése érdekében meg kell semmisíteni a szemétgyűjtéshez, ahogy az alábbi példa is mutatja:
@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();
}
}
}
Az előző példában egy JSDisconnectedException nincs elkapva a modul-eltávolítás során, mert nincs Blazor–SignalR áramkör egy Blazor WebAssembly alkalmazásban. További információ: ASP.NET Core Blazor JavaScript-együttműködés (JS interop).
A JavaScript kód (JS) betöltése a JavaScript-elhelyezkedésről szóló cikkben ismertetett módszerek bármelyikével:
A szkriptek
Figyelmeztetés
Csak akkor helyezzen <script>
címkét egy összetevőfájlba (.razor
), ha az összetevő garantáltan statikus kiszolgálóoldali renderelést (statikus SSR), mert a <script>
címke nem frissíthető dinamikusan.
Figyelmeztetés
Ne helyezzen <script>
címkét egy összetevőfájlba (.razor
), mert a <script>
címke nem frissíthető dinamikusan.
Blazor engedélyezi a JavaScript (JS) elkülönítését a standard JavaScript-modulokban (ECMAScript specifikáció). A JavaScript-modul betöltése ugyanúgy működik Blazor, mint más típusú webalkalmazások esetében, és szabadon testre szabhatja a modulok definiálását az alkalmazásban. A JavaScript-modulok használatáról további információt MDN Web Docs: JavaScript-modulokcímű témakörben talál.
JS elkülönítés a következő előnyöket biztosítja:
- Az importált JS már nem szennyezi a globális névteret.
- A könyvtárak és összetevők felhasználóinak nem szükséges importálniuk a kapcsolódó JS-t.
A dinamikus importálást az import()
operátorral az ASP.NET Core és a Blazortámogatja.
if ({CONDITION}) import("/additionalModule.js");
Az előző példában a {CONDITION}
helyőrző egy feltételes ellenőrzést jelöl annak megállapításához, hogy be kell-e tölteni a modult.
A böngésző kompatibilitását lásd: Használhatom: JavaScript-modulok: dinamikus importálási.
Kiszolgálóoldali forgatókönyvekben JS interop-hívások nem adhatók ki, miután BlazorSignalR kapcsolatcsoport megszakadt. Áramkör nélkül az összetevők ártalmatlanítása során vagy bármikor, amikor egy áramkör nem létezik, az alábbi metódushívások sikertelenek, és naplózzák azt az üzenetet, hogy az áramkör le van választva JSDisconnectedException:
- JS interop metódusok hívásai
-
Dispose
/hívjaDisposeAsync
bármelyik IJSObjectReference-at.
A JSDisconnectedException naplózásának vagy az egyéni adatok kiszolgálóoldali Blazorvaló naplózásának elkerülése érdekében a kivételt egy try-catch
utasításban észlelheti.
Az alábbi összetevő-ártalmatlanítási példa esetében:
- A kiszolgálóoldali összetevő implementálja a IAsyncDisposable.
-
module
egy IJSObjectReferenceJS modulhoz. - JSDisconnectedException el van kapva, és nincs rögzítve a naplóban.
- Igény szerint az egyéni adatokat az
catch
utasításban tetszőleges naplószinten naplózhatja. Az alábbi példa nem naplózza az egyéni adatokat. A kód feltételezi, hogy a fejlesztőt nem érdekli, hogy az összetevők ártalmatlanítása során mikor vagy hol bontódnak meg az áramkörök.
async ValueTask IAsyncDisposable.DisposeAsync()
{
try
{
if (module is not null)
{
await module.DisposeAsync();
}
}
catch (JSDisconnectedException)
{
}
}
Ha törölnie kell a saját JS objektumait, vagy más JS kódot kell végrehajtania az ügyfél oldalon, miután egy kör megszakadt egy kiszolgálóoldali Blazor alkalmazásban, használja a MutationObserver
JS mintát az ügyfélnél. A MutationObserver
minta lehetővé teszi JS kód végrehajtását, amikor egy elemet eltávolít a DOM-ból.
További információkért lásd a következő cikkeket:
-
ASP.NET Core Blazor JavaScript együttműködési (JS interop): A
MutationObserver
mintára példakódot tartalmaz. - ASP.NET Core Blazor-alkalmazások hibáinak kezelése: A JavaScript-interop szakasz JS interop forgatókönyvek hibakezelését ismerteti.
- ASP.NET Core Razor komponensek ártalmatlanítása: A cikk ismerteti, hogyan implementálhat ártalmatlanítási mintákat Razor komponensekben.
Az alábbi JS modul exportál egy JS függvényt egy böngészőablak-parancssor megjelenítéséhez. Helyezze a következő JS kódot egy külső JS fájlba.
wwwroot/scripts.js
:
export function showPrompt(message) {
return prompt(message, 'Type anything here');
}
Adja hozzá az előző JS modult egy alkalmazáshoz vagy osztálytárhoz statikus webes objektumként a wwwroot
mappában, majd importálja a modult a .NET-kódba a InvokeAsync-példány IJSRuntime meghívásával.
IJSRuntime a modult IJSObjectReferenceimportálja, amely egy JS objektumra mutató hivatkozást jelöl .NET-kódból. A IJSObjectReference használatával meghívhatja az exportált JS függvényeket a modulból.
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();
}
}
}
Az előző példában:
- Konvenció szerint a
import
azonosító egy speciális azonosító, amely kifejezetten egy JS modul importálására szolgál. - Adja meg a modul külső JS fájlját annak stabil statikus webes eszköz útvonalként:
./{SCRIPT PATH AND FILE NAME (.js)}
, ahol:- Az aktuális könyvtár elérési útja (
./
) szükséges a megfelelő statikus objektum elérési útjának létrehozásához a JS fájlhoz. - A
{SCRIPT PATH AND FILE NAME (.js)}
helyőrző awwwroot
alatti elérési út és fájlnév.
- Az aktuális könyvtár elérési útja (
- IJSObjectReference eltávolítja a hulladékgyűjtést területén IAsyncDisposable.DisposeAsync.
- Ne helyezzen
<script>
címkét a szkripthez a Blazor szkript után, mert a modul betöltése és gyorsítótárazása automatikusan megtörténik a dinamikusimport()
meghívásakor.
A modul dinamikus importálásához hálózati kérés szükséges, így csak aszinkron módon érhető el InvokeAsyncmeghívásával.
IJSInProcessObjectReference
egy olyan JS objektumra mutató hivatkozást jelöl, amelynek függvényei szinkron módon hívhatók meg az ügyféloldali összetevőkben. További információkért lásd a Szinkron JS interop az ügyféloldali összetevőkről szóló szakaszt.
Megjegyzés
Ha a külső JS fájlt egy Razor osztálykönyvtáradja meg, adja meg a modul JS fájlt a stabil statikus webes eszköz elérési útvonalán: ./_content/{PACKAGE ID}/{SCRIPT PATH AND FILE NAME (.js)}
:
- Az aktuális könyvtár elérési útja (
./
) szükséges a megfelelő statikus objektum elérési útjának létrehozásához a JS fájlhoz. - A
{PACKAGE ID}
helyőrző a könyvtár csomagazonosítója. A csomagazonosító alapértelmezésben a projekt neve lesz, ha<PackageId>
nincs megadva a projektfájlban. Az alábbi példában a kódtár szerelvényazonosítójaComponentLibrary
, és a kódtár projektfájlja nem adja meg<PackageId>
. - A
{SCRIPT PATH AND FILE NAME (.js)}
helyőrző awwwroot
alatti elérési út és fájlnév. Az alábbi példában a külső JS fájl (script.js
) az osztálytárwwwroot
mappájába kerül. -
module
az alkotó osztály (IJSObjectReference) privát, null értékűprivate IJSObjectReference? module;
-je.
module = await js.InvokeAsync<IJSObjectReference>(
"import", "./_content/ComponentLibrary/scripts.js");
További információ: ASP.NET Core Razor összetevőinek felhasználása Razor osztálykódtárból (RCL).
A Blazor dokumentációban a példák a modulfájlok .js
fájlbővítményét használják, nem a újabb .mjs
fájlkiterjesztést (RFC 9239). Dokumentációnk továbbra is a .js
fájlkiterjesztést használja ugyanazon okokból, amelyek miatt a Mozilla Foundation dokumentációja továbbra is a .js
fájlkiterjesztést használja. További információ: Aside – .mjs versus .js (MDN-dokumentáció).
Egyes JavaScript- (JS) interop-forgatókönyvekhez HTML-elemekre kell hivatkozni. Előfordulhat például, hogy egy felhasználói felületi kódtár elemhivatkozást igényel az inicializáláshoz, vagy parancsszerű API-kat kell meghívnia egy elemen, például click
vagy play
.
Az összetevők HTML-elemeire mutató hivatkozásokat az alábbi módszerrel rögzítheti:
- Adjon hozzá egy
@ref
attribútumot a HTML-elemhez. - Definiáljon egy ElementReference típusú mezőt, amelynek neve megegyezik a
@ref
attribútum értékével.
Az alábbi példa a username
<input>
elemre mutató hivatkozás rögzítését mutatja be:
<input @ref="username" ... />
@code {
private ElementReference username;
}
Figyelmeztetés
Csak elemhivatkozással változtathatja meg egy üres elem tartalmát, amelyik nem lép kapcsolatba a(z) Blazor-val. Ez a forgatókönyv akkor hasznos, ha egy külső API tartalommal látja el az elemet. Mivel a Blazor nem kommunikál az elemmel, nincs lehetőség ütközésre a Blazorábrázolása és a DOM között.
A következő példában veszélyes a rendezetlen lista (ul
) tartalmát MyList
-en keresztül JS interoppal megváltoztatni, mert Blazor a DOM-mal együttműködve tölti ki az elem listaelemeit (<li>
) az Todos
objektumból.
<ul @ref="MyList">
@foreach (var item in Todos)
{
<li>@item.Text</li>
}
</ul>
A MyList
elemhivatkozás használata csak DOM-tartalom olvasására vagy esemény aktiválására támogatott.
Ha a JS interop , és MyList
megpróbálja a különbségeket alkalmazni az elemre, a különbségek nem fognak egyezni a DOM-mal. A lista tartalmának JSMyList
elemhivatkozással való módosítása nem támogatott.
További információ: ASP.NET Core Blazor JavaScript-együttműködés (JS interop).
A ElementReference-t a JS interopon keresztül továbbítják a JS kódhoz. A JS kód egy HTMLElement
példányt kap, amelyet normál DOM API-k használatával használhat. A következő kód például egy .NET-bővítménymetódust (TriggerClickEvent
) határoz meg, amely lehetővé teszi az egérkattintás elemre való küldését.
A JS függvény clickElement
click
eseményt hoz létre az átadott HTML-elemen (element
):
window.interopFunctions = {
clickElement : function (element) {
element.click();
}
}
Ha olyan JS függvényt szeretne meghívni, amely nem ad vissza értéket, használja a JSRuntimeExtensions.InvokeVoidAsync. A következő kód ügyféloldali click
eseményt vált ki azzal, hogy meghívja az előző JS függvényt a rögzített ElementReference-vel.
@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);
}
}
Bővítménymetódus használatához hozzon létre egy statikus bővítménymetódust, amely megkapja a IJSRuntime-példányt:
public static async Task TriggerClickEvent(this ElementReference elementRef,
IJSRuntime js)
{
await js.InvokeVoidAsync("interopFunctions.clickElement", elementRef);
}
A clickElement
metódus meghívása közvetlenül az objektumon történik. Az alábbi példa feltételezi, hogy a TriggerClickEvent
metódus elérhető a JsInteropClasses
névtérből:
@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);
}
}
Fontos
A exampleButton
változó csak az összetevő renderelése után lesz feltöltve. Ha egy nem feltöltött ElementReference átadásra kerül a JS kódnak, akkor a JS kód null
értéket kap. Az elemhivatkozások az összetevő renderelésének befejezése után történő módosításához használja az OnAfterRenderAsync
vagy OnAfterRender
összetevő életciklus-módszereit.
Amikor általános típusokkal dolgozik, és értéket ad vissza, használja a ValueTask<TResult>:
public static ValueTask<T> GenericMethod<T>(
this ElementReference elementRef, IJSRuntime js) =>
js.InvokeAsync<T>("{JAVASCRIPT FUNCTION}", elementRef);
A {JAVASCRIPT FUNCTION}
helyőrző a JS függvény azonosítója.
A GenericMethod
közvetlenül az objektumon egy típussal van meghívva. Az alábbi példa feltételezi, hogy a GenericMethod
elérhető a JsInteropClasses
névtérből:
@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);
}
}
A ElementReference nem adható át az összetevők között, mert:
- A példány csak az összetevő renderelése után garantáltan létezik, amely egy összetevő OnAfterRender/OnAfterRenderAsync metódusának végrehajtása során vagy után történik.
- A ElementReference egy
struct
, amely nem adható át komponens paraméterként.
Ahhoz, hogy egy szülőösszetevő elérhetővé tegyen egy elemhivatkozást más összetevők számára, a szülőösszetevő a következő lehetőségek közül választhat:
- Gyermekösszetevők számára a visszahívások regisztrálásának engedélyezése.
- Hívja meg a regisztrált callbackeket a OnAfterRender esemény alatt az átadott elemhivatkozással. Közvetett módon ez a megközelítés lehetővé teszi, hogy a gyermekösszetevők kölcsönhatásba lépjenek a szülő elemhivatkozásával.
<style>
.red { color: red }
</style>
<script>
function setElementClass(element, className) {
var myElement = element;
myElement.classList.add(className);
}
</script>
Megjegyzés
A JS helyére és az éles alkalmazásokkal kapcsolatos ajánlásainkra vonatkozó általános útmutatásért nézze meg a JavaScript-helyet az ASP.NET Core Blazor alkalmazásokban.
CallJs7.razor
(szülőösszetevő):
@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
(szülőösszetevő):
@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
(szülőösszetevő):
@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
(szülőösszetevő):
@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
(szülőösszetevő):
@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
(szülőösszetevő):
@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);
}
}
}
}
Az előző példában az alkalmazás névtere BlazorSample
. Ha helyileg teszteli a kódot, frissítse a névteret.
SurveyPrompt.razor
(gyermek összetevő):
<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();
}
}
}
Az előző példában az alkalmazás névtere BlazorSample
a megosztott összetevőkkel a Shared
mappában. Ha helyileg teszteli a kódot, frissítse a névteret.
Ez a szakasz csak az interaktív kiszolgáló összetevőire vonatkozik, de az ügyféloldali összetevők JS interop-időtúllépéseket is beállíthatnak, ha a feltételek ezt indokolják.
A kiszolgálóoldalon futó alkalmazásokban, ahol van szerver interaktivitás, a JavaScript (JS) interoperabilitás hálózati hibák miatt meghiúsulhat, ezért megbízhatatlannak kell tekinteni. Blazor alkalmazások egyperces időtúllépést használnak JS interop hívásokhoz. Ha egy alkalmazás el tudja viselni az agresszívebb időtúllépést, állítsa be az időtúllépést az alábbi módszerek egyikével.
Globális időtúllépés beállítása a Program.cs
-ben a CircuitOptions.JSInteropDefaultCallTimeout segítségével.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents(options =>
options.JSInteropDefaultCallTimeout = {TIMEOUT});
builder.Services.AddServerSideBlazor(
options => options.JSInteropDefaultCallTimeout = {TIMEOUT});
Állítson be egy globális időtúllépést a Startup.ConfigureServices
Startup.cs
metódusában CircuitOptions.JSInteropDefaultCallTimeout:
services.AddServerSideBlazor(
options => options.JSInteropDefaultCallTimeout = {TIMEOUT});
A {TIMEOUT}
helyettesítő egy TimeSpan (például TimeSpan.FromSeconds(80)
).
Hívásonkénti időkorlát beállítása az összetevő kódban. A megadott időtúllépés felülírja a JSInteropDefaultCallTimeoutáltal beállított globális időtúllépést:
var result = await JS.InvokeAsync<string>("{ID}", {TIMEOUT}, [ "Arg1" ]);
var result = await JS.InvokeAsync<string>("{ID}", {TIMEOUT}, new[] { "Arg1" });
Az előző példában:
- A
{TIMEOUT}
helyettesítő egy TimeSpan (példáulTimeSpan.FromSeconds(80)
). - A
{ID}
helyőrző a meghívandó függvény azonosítója. AzsomeScope.someFunction
érték például meghívja a függvénytwindow.someScope.someFunction
.
Bár a kiszolgálóoldali összetevőkkel kapcsolatos hálózati hibák gyakori oka az JS interop-hibáknak, a hívásonkénti időtúllépések JS ügyféloldali összetevők interop hívásaihoz beállíthatók. Bár nincs Blazor–SignalR áramkör az ügyféloldali komponenshez, JS interop hívások más vonatkozó okok miatt meghiúsulhatnak.
Az erőforrás-kimerültségről további információt az ASP.NET Core interaktív kiszolgálóoldali rendereléséhez szükséges fenyegetéscsökkentési útmutatásBlazor című témakörben talál.
A körkörös hivatkozásokat tartalmazó objektumok nem szerializálhatók az ügyfélen:
- .NET-metódushívások.
- JavaScript-metódushívások C#-ból, ha a visszatérési típus körkörös hivatkozásokat tartalmaz.
Előfordulhat, hogy JavaScript -kódtárakat (JS) szeretne használni, amelyek látható felhasználói felületi elemeket hoznak létre a böngésző DOM-ében. Első pillantásra ez nehéznek tűnhet, mert Blazor"diffing rendszere a DOM-elemek fájának szabályozásán alapul, és hibákba ütközik, ha néhány külső kód mutálja a DOM-fát, és érvényteleníti a diffek alkalmazásának mechanizmusát. Ez nem Blazor-specifikus korlátozás. Ugyanez a kihívás minden diff-alapú felhasználói felületi keretrendszer esetében jelentkezik.
Szerencsére egyszerűen beágyazható a külsőleg létrehozott felhasználói felület egy Razor összetevő felhasználói felületén. Az ajánlott módszer az, hogy az összetevő kódja (.razor
fájl) üres elemet állít elő. Ami a Blazor"különbségek megállapító rendszerét" illeti, az elem mindig üres, így a renderelő nem hajt végre rekurzív folyamatot az elemen, hanem változatlanul hagyja annak tartalmát. Így biztonságosan feltöltheti az elemet tetszőleges külsőleg felügyelt tartalommal.
Az alábbi példa a koncepciót mutatja be. A if
utasításon belül, amikor firstRender
true
, lépj kapcsolatba unmanagedElement
-mal a Blazor kívül a JS interop használatával. Meghívhat például egy külső JS kódtárat az elem feltöltéséhez.
Blazor az összetevő eltávolításáig az elem tartalmát egyedül hagyja. Az összetevő eltávolításakor az összetevő teljes DOM-alhálózata is el lesz távolítva.
<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)
{
...
}
}
}
Tekintse meg az alábbi példát, amely interaktív térképet jelenít meg nyílt forráskódú Mapbox API-k.
A következő JS modul az alkalmazásba kerül, vagy elérhetővé válik egy Razor osztálytárból.
Megjegyzés
A Mapbox térkép létrehozásához szerezze be a hozzáférési tokent a Mapbox bejelentkezés során, és adja meg, ahol a {ACCESS TOKEN}
szerepel az alábbi kódban.
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]);
}
A megfelelő stílus létrehozásához adja hozzá a következő stíluslap címkét a gazda HTML-oldalához.
Adja hozzá a következő <link>
elemet a <head>
jelöléshez (helye a <head>
tartalomnak).
<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();
}
}
}
Az előző példa egy interaktív térkép felhasználói felületet hoz létre. A felhasználó:
- Húzással görgethet vagy nagyíthat.
- Az előre meghatározott helyekre ugráshoz válassza ki a gombokat.
Az előző példában:
- A
<div>
a@ref="mapElement"
-al üresen marad, ami Blazor illeti. Amapbox-gl.js
szkript biztonságosan feltöltheti az elemet, és módosíthatja annak tartalmát. Használja ezt a technikát a felhasználói felületet megjelenítő JS kódtárakkal. Az összetevőket egy külső JS SPA-keretrendszerből ágyazhatja be Razor összetevőkbe, feltéve, hogy nem próbálják meg elérni és módosítani a lap más részeit. Külső kód számára JS módosítani azokat az elemeket, amelyeket Blazor nem tekint üresnek. - Ha ezt a megközelítést használja, vegye figyelembe a DOM-elemek Blazor megőrzésére vagy megsemmisítésére vonatkozó szabályokat. Az összetevő biztonságosan kezeli a gombkattintásos eseményeket, és frissíti a meglévő térképpéldányt, mert a DOM-elemeket lehetőség szerint megtartja. Ha egy
@foreach
cikluson belülről rendereli a térképelemek listáját, az összetevők példányainak megőrzéséhez@key
kell használnia. Ellenkező esetben a listaadatok változásai miatt az összetevők példányai nemkívánatos módon megőrizhetik az előző példányok állapotát. További információkért tekintse meg, hogyan kell használni a@key
irányelv attribútumot az elemek, összetevők és modellobjektumokközötti kapcsolat megőrzéséhez. - A példa egy JavaScript-modul JS logikáját és függőségeit foglalja magában, és dinamikusan betölti a modult a
import
azonosító használatával. További információ: JavaScript-elkülönítés JavaScript-modulokban.
Blazor támogatja az optimalizált bájttömb JavaScript (JS) interop használatát, amely megakadályozza a bájttömbök Base64-be való kódolását/dekódolását. Az alábbi példa JS interop használatával ad át egy bájttömböt a JavaScriptnek.
Adjon meg egy receiveByteArray
JS függvényt. A függvény InvokeVoidAsync van meghívva, és nem ad vissza értéket:
<script>
window.receiveByteArray = (bytes) => {
let utf8decoder = new TextDecoder();
let str = utf8decoder.decode(bytes);
return str;
};
</script>
Megjegyzés
A JS helyére és az éles alkalmazásokkal kapcsolatos ajánlásainkra vonatkozó általános útmutatásért nézze meg a JavaScript-helyet az ASP.NET Core Blazor alkalmazásokban.
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);
}
}
A .NET metódusok JavaScriptből való meghívásakor bájttömbök használatáról további információért lásd: .NET-metódusok meghívása JavaScript-függvényekből az ASP.NET Core Blazor.
Blazor támogatja az adatok közvetlen átvitelét .NET-ről JavaScriptre (JS). A streamek egy DotNetStreamReferencehasználatával jönnek létre.
DotNetStreamReference .NET-streamet jelöl, és a következő paramétereket használja:
-
stream
: A stream a JSküldésre került. -
leaveOpen
: Meghatározza, hogy a stream nyitva marad-e az átvitel után. Ha nincs megadva érték,leaveOpen
alapértelmezés szerintfalse
.
A JSesetében használjon tömbpuffert vagy olvasható adatfolyamot az adatok fogadására.
Egy
ArrayBuffer
használata:async function streamToJavaScript(streamRef) { const data = await streamRef.arrayBuffer(); }
Egy
ReadableStream
használata:async function streamToJavaScript(streamRef) { const stream = await streamRef.stream(); }
C#-kódban:
var streamRef = new DotNetStreamReference(stream: {STREAM}, leaveOpen: false);
await JS.InvokeVoidAsync("streamToJavaScript", streamRef);
Az előző példában:
- A
{STREAM}
helyőrző azt a Stream-et képviseli, amelyet a JS-nek küldtek. -
JS
egy injektált IJSRuntime példány.
Egy DotNetStreamReference példány törlése általában szükségtelen. Ha leaveOpen
az alapértelmezett false
értékre van állítva, a mögöttes Stream automatikusan el lesz távolítva a JSután.
Ha leaveOpen
true
, akkor a DotNetStreamReference nem szünteti meg a mögöttes Stream-at. Az alkalmazás kódja határozza meg, hogy mikor érdemes megszüntetni az alapul szolgáló Stream. Az alapul szolgáló Streamelidegenítésének eldöntésekor vegye figyelembe a következőket:
- Egy Stream megszüntetése, miközben az JS-re van továbbítva, alkalmazáshibának minősül, és kezeletlen kivételt okozhat.
- Stream átvitel akkor kezdődik, amikor a DotNetStreamReference argumentumként továbbítják egy JS interop hívásnak, függetlenül attól, hogy a streamet ténylegesen használják-e JS logikában.
Ezeket a jellemzőket figyelembe véve azt javasoljuk, hogy a mögöttes Stream-t csak akkor semmisítse meg, ha az JS teljesen felhasználásra került (az ígéretet, amelyet a arrayBuffer
vagy stream
ad vissza, teljesül). Ebből következik, hogy egy DotNetStreamReference csak akkor adható át JS-nek, ha az JS logikája feltétel nélkül felhasználja.
.NET-metódusok meghívása a JavaScript-függvényekből az ASP.NET Core Blazor a fordított műveletet, a JavaScriptből a .NET-be való streamelést tárgyalja.
ASP.NET Core Blazor fájlok letöltéséről szól és ismerteti, hogyan tölthet le egy fájlt Blazor.
A JS kivételek megfogásához burkolja a JS interopot egy try
-catch
blokkba, és kapjon el egy JSException.
Az alábbi példában a nonFunction
JS függvény nem létezik. Ha a függvény nem található, a JSException egy Message csapdába esik, amely a következő hibát jelzi:
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}";
}
}
}
Egy JSAbortController használatával az összetevő CancellationTokenSource-ban megszakíthat egy hosszú ideig futó JavaScript-függvényt C#-kódból.
Az alábbi JSHelpers
osztály egy szimulált, hosszú ideig futó függvényt tartalmaz, longRunningFn
, amely folyamatosan számlál, amíg a AbortController.signal
nem jelzi, hogy a AbortController.abort
meghívták. A sleep
függvény a hosszú ideig futó függvény lassú végrehajtását szimulálja, és nem szerepel az éles kódban. Amikor egy összetevő meghívja a stopFn
-t, a longRunningFn
jelez az abortálásra a while
AbortSignal.aborted
ciklus feltételes ellenőrzésén keresztül.
<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>
Megjegyzés
A JS helyére és az éles alkalmazásokkal kapcsolatos ajánlásainkra vonatkozó általános útmutatásért nézze meg a JavaScript-helyet az ASP.NET Core Blazor alkalmazásokban.
A következő összetevő:
- Meghívja a JS függvényt
longRunningFn
aStart Task
gomb kiválasztásakor. A hosszú ideig futó függvény végrehajtásának kezelésére egy CancellationTokenSource szolgál. CancellationToken.Register beállít egy JS interop-call delegáltat a JS függvénystopFn
végrehajtásához a CancellationTokenSource.Token törlésekor. - Ha a
Cancel Task
gombot választjuk ki, a CancellationTokenSource.Token a Cancelhívásával törlődik. - A CancellationTokenSource a
Dispose
módszerrel van megsemmisítve.
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();
}
}
A böngésző fejlesztői eszközei konzolja jelzi, hogy a hosszú ideig futó JS függvény akkor hajtódik végre, amikor a Start Task
gombot kiválasztják, és akkor szakad meg, amikor a Cancel Task
gombot kiválasztják.
longRunningFn: 1
longRunningFn: 2
longRunningFn: 3
longRunningFn aborted!
Ez a szakasz az ügyféloldali összetevőkre vonatkozik.
A JavaScript (JS) ügyféloldali összetevőkben való használatának alternatívájaként BlazorJSIJSRuntimeJS[JSImport]
/[JSExport]
interfészen alapuló interop api-ja érhető el a .NET 7 vagy újabb verziót célzó alkalmazások számára.
További információért lásd: JavaScript JSImport/JSExport interop ASP.NET Core-val Blazor.
Ez a szakasz az ügyféloldali összetevőkre vonatkozik.
A IJSUnmarshalledRuntime interfésszel végzett unmarshalled interop elavult, és helyette JavaScript [JSImport]
/[JSExport]
interopot kell használni.
További információ: JavaScript JSImport/JSExport interop és ASP.NET Core Blazor.
Blazor WebAssembly összetevők gyenge teljesítményt tapasztalhatnak, ha a .NET-objektumok szerializálva vannak a JavaScript (JS) interophoz, és az alábbiak bármelyike igaz:
- A .NET-objektumok nagy mennyisége gyorsan szerializálódik. A gyenge teljesítmény például akkor fordulhat elő, ha JS interop-hívások egy bemeneti eszköz mozgatása, például egy egérkerék pörgetése alapján történik.
- A nagyméretű .NET-objektumokat vagy sok .NET-objektumot szerializálni kell JS interophoz. A gyenge teljesítmény például akkor fordulhat elő, ha JS interop hívások több tucat fájl szerializálását igénylik.
IJSUnmarshalledObjectReference olyan JS objektumra mutató hivatkozást jelöl, amelynek függvényei a .NET-adatok szerializálása nélkül hívhatók meg.
Az alábbi példában:
- Egy sztringet és egész számot tartalmazó
struktúra sorosítatlanul kerül átadásra a . - A JS függvények feldolgozzák az adatokat, és logikai értéket vagy egy karakterláncot adnak vissza a hívónak.
- A JS sztringek nem konvertálhatók közvetlenül .NET-
string
objektummá. AunmarshalledFunctionReturnString
függvény meghívja aBINDING.js_string_to_mono_string
-et egy JS karaktersorozat konvertálásának kezelésére.
Megjegyzés
Az alábbi példák nem tipikus használati esetek ebben a forgatókönyvben, mert a struktúra, amelyet a JS kap, nem vezet gyenge komponens teljesítményhez. A példa egy kis objektumot használ csupán a nemszerializált .NET-adatok átadásának fogalmainak szemléltetésére.
<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>
Megjegyzés
A JS helyére és az éles alkalmazásokkal kapcsolatos ajánlásainkra vonatkozó általános útmutatásért nézze meg a JavaScript-helyet az ASP.NET Core Blazor alkalmazásokban.
Figyelmeztetés
A js_string_to_mono_string
függvény neve, viselkedése és létezése változhat a .NET egy későbbi kiadásában. Például:
- A függvényt valószínűleg átnevezik.
- Maga a függvény eltávolítható a karakterláncok keretrendszer általi automatikus konvertálása javára.
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;
}
}
Ha egy IJSUnmarshalledObjectReference példány nincs C#-kódban megsemmisítve, meg lehet semmisíteni JS-ben. A következő dispose
függvény felszabadítja az objektumhivatkozást, amikor a JS-ből hívják meg:
window.exampleJSObjectReferenceNotDisposedInCSharp = () => {
return {
dispose: function () {
DotNet.disposeJSObjectReference(this);
},
...
};
}
A tömbtípusok JS objektumokból .NET-objektumokká alakíthatók js_typed_array_to_array
használatával, de a JS tömbnek beírt tömbnek kell lennie. A JS tömbjei c# kódban olvashatók .NET-objektumtömbként (object[]
).
Más adattípusok, például sztringtömbök is konvertálhatók, de új Mono tömbobjektumot (mono_obj_array_new
) kell létrehozniuk, és meg kell adni az értékét (mono_obj_array_set
).
Figyelmeztetés
JS a Blazor keretrendszer által nyújtott funkciók, például js_typed_array_to_array
, mono_obj_array_new
és mono_obj_array_set
, a .NET későbbi kiadásaiban névváltozások, viselkedésváltozások vagy eltávolítások alá esnek.
A JavaScript (JS) interop cikkekben példák mutatnak be tipikus objektumelhelyezési mintákat:
Amikor a .NET-ből hívja meg a JS-t, az itt leírtak szerint szabadítson fel minden létrehozott IJSObjectReference/IJSInProcessObjectReference/
JSObjectReference
objektumot akár a .NET-ből, akár a JS-ból, hogy elkerülje a JS memória szivárgását.Ha a .NET-et a JShívja meg, ahogy az a A .NET-metódusok ASP.NET Core-ban JavaScript-függvényekből történő meghívása Blazorleírja, akkor a .NET-ből vagy a DotNetObjectReference-ből létrehozott JS-t el kell távolítani a .NET-memória kiszivárgásának elkerülése érdekében.
JS interop objektumhivatkozások a hivatkozást létrehozó JS interop hívás oldalán található azonosító által kulcsolt térképként vannak implementálva. Ha az objektumelhelyezést a .NET vagy JS oldalról kezdeményezik, Blazor eltávolítja a bejegyzést a térképről, és az objektum szemétként gyűjthető, feltéve, hogy nincs más erős hivatkozás az objektumra.
Legalább a .NET oldalon létrehozott objektumokat mindig megsemmisítse, hogy elkerülje a .NET által felügyelt memória kiszivárgását.
További információ: ASP.NET Core Blazor JavaScript-együttműködés (JS interop).
További információ: ASP.NET Core Blazor JavaScript-együttműködés (JS interop).
- .NET-metódusok meghívása JavaScript-függvényekből a ASP.NET Core Blazor
-
InteropComponent.razor
példa (dotnet/AspNetCore
GitHub-adattármain
ág): Amain
ág a termékegység aktuális fejlesztését jelöli a ASP.NET Core következő kiadásához. Ha egy másik kiadás ágát szeretné kiválasztani (példáulrelease/{VERSION}
, ahol a{VERSION}
helyőrző a kiadás verziója), használja az Ágak vagy címkék váltása legördülő listából az ág kiválasztásához. A már nem létező ág esetén a Címkék lapon keresse meg az API-t (példáulv7.0.0
). -
Blazor GitHub-tárház mintái (
dotnet/blazor-samples
) ( hogyan lehet letölteni) - ASP.NET Core Blazor-alkalmazások hibáinak kezelése (JavaScript interop szakasz)
- fenyegetéscsökkentés: A .NET--ból meghívott JavaScript-függvények
ASP.NET Core-visszajelzés
A(z) ASP.NET Core egy nyílt forráskód projekt. Visszajelzés adásához válasszon egy hivatkozást: