Olvasás angol nyelven

Megosztás a következőn keresztül:


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 -ból való meghívásáról a ASP.NET Core .NET-metódusainak meghívása a JavaScript-függvényekből című témakörben talál további információt.

JS függvények meghívása

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. A window.someScope.someFunctionhívásához használd az someScope.someFunctionazonosí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 a InvokeAsync metódusokhoz. A InvokeAsync kibontja a Promise-et, és visszaadja a Promiseá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 convertArrayJS 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));
    }
}

Felhasználói kézmozdulatokra korlátozott JavaScript API

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.

JavaScript-függvények meghívása visszaadott érték olvasása nélkül (InvokeVoidAsync)

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 displayTickerAlert1JS 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.

Összetevő (.razor) példa (InvokeVoidAsync)

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);
    }
}

Példa osztály (.cs) (InvokeVoidAsync)

JsInteropClasses1.cs:

using Microsoft.JSInterop;

namespace BlazorSample;

public class JsInteropClasses1(IJSRuntime js) : IDisposable
{
    private readonly IJSRuntime js = js;

    public async ValueTask TickerChanged(string symbol, decimal price) => 
        await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);

    // Calling SuppressFinalize(this) prevents derived types that introduce 
    // a finalizer from needing to re-implement IDisposable.
    public void Dispose() => GC.SuppressFinalize(this);
}
using Microsoft.JSInterop;

namespace BlazorSample;

public class JsInteropClasses1(IJSRuntime js) : IDisposable
{
    private readonly IJSRuntime js = js;

    public async ValueTask TickerChanged(string symbol, decimal price) => 
        await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);

    // Calling SuppressFinalize(this) prevents derived types that introduce 
    // a finalizer from needing to re-implement IDisposable.
    public void Dispose() => GC.SuppressFinalize(this);
}
using Microsoft.JSInterop;

public class JsInteropClasses1 : IDisposable
{
    private readonly IJSRuntime js;

    public JsInteropClasses1(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask TickerChanged(string symbol, decimal price)
    {
        await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
    }

    public void Dispose()
    {
    }
}
using Microsoft.JSInterop;

public class JsInteropClasses1 : IDisposable
{
    private readonly IJSRuntime js;

    public JsInteropClasses1(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask TickerChanged(string symbol, decimal price)
    {
        await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
    }

    public void Dispose()
    {
    }
}
using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses1 : IDisposable
{
    private readonly IJSRuntime js;

    public JsInteropClasses1(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask TickerChanged(string symbol, decimal price)
    {
        await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
    }

    public void Dispose()
    {
    }
}
using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses1 : IDisposable
{
    private readonly IJSRuntime js;

    public JsInteropClasses1(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask TickerChanged(string symbol, decimal price)
    {
        await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
    }

    public void Dispose()
    {
    }
}

TickerChanged 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();
}

JavaScript-függvények meghívása és visszaadott érték beolvasása (InvokeAsync)

Használja a InvokeAsync, amikor a .NET-nek be kell olvasnia egy JavaScript -JS- hívás eredményét.

Adjon meg egy displayTickerAlert2JS 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.

Összetevő (.razor) példa (InvokeAsync)

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}";
    }
}

Osztály (.cs) példa (InvokeAsync)

JsInteropClasses2.cs:

using Microsoft.JSInterop;

namespace BlazorSample;

public class JsInteropClasses2(IJSRuntime js) : IDisposable
{
    private readonly IJSRuntime js = js;

    public async ValueTask<string> TickerChanged(string symbol, decimal price) => 
        await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);

    // Calling SuppressFinalize(this) prevents derived types that introduce 
    // a finalizer from needing to re-implement IDisposable.
    public void Dispose() => GC.SuppressFinalize(this);
}
using Microsoft.JSInterop;

namespace BlazorSample;

public class JsInteropClasses2(IJSRuntime js) : IDisposable
{
    private readonly IJSRuntime js = js;

    public async ValueTask<string> TickerChanged(string symbol, decimal price) => 
        await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);

    // Calling SuppressFinalize(this) prevents derived types that introduce 
    // a finalizer from needing to re-implement IDisposable.
    public void Dispose() => GC.SuppressFinalize(this);
}
using Microsoft.JSInterop;

public class JsInteropClasses2 : IDisposable
{
    private readonly IJSRuntime js;

    public JsInteropClasses2(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> TickerChanged(string symbol, decimal price)
    {
        return await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
    }

    public void Dispose()
    {
    }
}
using Microsoft.JSInterop;

public class JsInteropClasses2 : IDisposable
{
    private readonly IJSRuntime js;

    public JsInteropClasses2(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> TickerChanged(string symbol, decimal price)
    {
        return await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
    }

    public void Dispose()
    {
    }
}
using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses2 : IDisposable
{
    private readonly IJSRuntime js;

    public JsInteropClasses2(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> TickerChanged(string symbol, decimal price)
    {
        return await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
    }

    public void Dispose()
    {
    }
}
using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses2 : IDisposable
{
    private readonly IJSRuntime js;

    public JsInteropClasses2(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> TickerChanged(string symbol, decimal price)
    {
        return await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
    }

    public void Dispose()
    {
    }
}

TickerChanged 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();
}

Dinamikus tartalomgenerálási forgatókönyvek

A BuildRenderTreedinamikus tartalomlétrehozáshoz használja a attribútumot:

[Inject]
IJSRuntime JS { get; set; }

Előrendezés

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:

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 scrollPositionnull.

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 .

Szinkron JS interoperabilitás az ügyféloldali elemekben

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).

JavaScript-hely

A JavaScript kód (JS) betöltése a JavaScript-elhelyezkedésről szóló cikkben ismertetett módszerek bármelyikével:

A szkriptek modulokbanelkülönítéséről a JavaScript-modulok szakaszban található JavaScript-elkülönítés című szakaszban talál további információt.

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.

JavaScript-elkülönítés JavaScript-modulokban

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:

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 MutationObserverJS 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:

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ő a wwwroot alatti elérési út és fájlnév.
  • 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 dinamikus import() 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ója ComponentLibrary, és a kódtár projektfájlja nem adja meg <PackageId>.
  • A {SCRIPT PATH AND FILE NAME (.js)} helyőrző a wwwrootalatti 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ár wwwroot 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ó).

Elemekre mutató hivatkozások rögzítése

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 clickElementclick 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);
    }
}

Referenciaelemek az összetevők között

A ElementReference nem adható át az összetevők között, mert:

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.

JavaScript interop-hívások megkeményítése

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.ConfigureServicesStartup.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ául TimeSpan.FromSeconds(80)).
  • A {ID} helyőrző a meghívandó függvény azonosítója. Az someScope.someFunction érték például meghívja a függvényt window.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.

Körkörös objektumhivatkozások elkerülése

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.

Felhasználói felületet megjelenítő JavaScript-kódtárak

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 firstRendertrue, 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.

Mapbox utcai térképe Tokióról, Japán, gombokkal Bristol, Egyesült Királyság, és Tokió, Japán kiválasztásához

Az előző példában:

  • A <div> a @ref="mapElement"-al üresen marad, ami Blazor illeti. A mapbox-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.

Bájttömb támogatása

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 receiveByteArrayJS 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 &copy;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 &copy;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.

Streamelés .NET-ről JavaScriptre

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 szerint false.

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 leaveOpentrue, 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.

JavaScript-kivételek elfogása

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 nonFunctionJS 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}";
        }
    }
}

Hosszú ideig futó JavaScript-függvény megszakítása

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 whileAbortSignal.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ő:

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!

JavaScript [JSImport]/[JSExport] interop

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.

Nem deszerializált JavaScript interoperabilitás

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.

Nem deszerializált JavaScript interoperabilitás

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á. A unmarshalledFunctionReturnString függvény meghívja a BINDING.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_arrayhaszná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.

JavaScript interop objektumhivatkozások megsemmisítése

A JavaScript (JS) interop cikkekben példák mutatnak be tipikus objektumelhelyezési mintákat:

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.

DOM-tisztítási feladatok az összetevők ártalmatlanítása során

További információ: ASP.NET Core Blazor JavaScript-együttműködés (JS interop).

JavaScript interop hívások körfolyamat nélkül

További információ: ASP.NET Core Blazor JavaScript-együttműködés (JS interop).

További erőforrások