.NET yöntemlerinden JavaScript işlevlerini ASP.NET Core Blazor

Bu makale. .NET'te JavaScript ( JS ) işlevlerinin nasıl yazılması gerekir? 'den .NET yöntemlerini çağırma hakkında bilgi için JS bkz. ASP.NET Core içindeki JavaScript işlevlerinden .NET yöntemlerini çağırın Blazor .

JS.NET'den çağrısı yapmak için IJSRuntime soyutlamayı ekleme ve aşağıdaki yöntemlerden birini çağırma:

İşlevleri çağıran önceki .NET yöntemleri JS için:

  • İşlev tanımlayıcısı ( String ) genel kapsam () ile görelidir. window çağrısı yapmak window.someScope.someFunction için tanımlayıcı şu someScope.someFunction şekildedir: . Çağrılmadan önce işlevi kaydetmeye gerek yoktur.
  • herhangi bir sayıda JS ON-serializable bağımsız değişkenlerini bir Object[] işleve JS iletir.
  • İptal CancellationToken belirteci ( ) işlemlerin iptal edilmesi gerektiğine yönelik bir bildirim yayır.
  • TimeSpan , bir işlem için bir zaman sınırını JS temsil eder.
  • Dönüş TValue türü de SERI HALE JS GETIRilebilir ON türündedir. TValue döndürülen ON türüyle en iyi eşlene .NET JS türüyle eşleşmesi gerekir.
  • Metotlar JS Promise için InvokeAsync bir döndürülür. InvokeAsync , veraplarını Promise açar ve tarafından beklenen değeri Promise döndürür.

Başlatma etkinleştirilmiş uygulamalar için, ilk başlatma sırasında içine Blazor Server JS çağrılma mümkün değildir. JS birlikte çalışma çağrılarını tarayıcıyla bağlantı kurulana kadar ertelenmiş olması gerekir. Daha fazla bilgi için, Bir uygulamanın ne zaman ön Blazor Server ekli olduğunu algılama bölümüne bakın.

Aşağıdaki örnek, tabanlı TextDecoder bir kod çözücü olan JS tabanlıdır. Örnekte, bir gereksinimi geliştirici kodundan var olan bir API'ye yükten alan JS bir C# yönteminden işlevin nasıl çağrıl olduğu JS gösterilmektedir. işlevi bir C# yönteminden bir byte dizisini kabul eder, dizinin kodunu çözerek metni görüntülemek için JS bileşene döndürür.

Kapanış JS etiketine ( ) veya </body> ( ) aşağıdaki kodu wwwroot/index.html Blazor WebAssembly Pages/_Layout.cshtml Blazor Server ekleyin:

<script>
  window.convertArray = (win1251Array) => {
    var win1251decoder = new TextDecoder('windows-1251');
    var bytes = new Uint8Array(win1251Array);
    var decodedArray = win1251decoder.decode(bytes);
    console.log(decodedArray);
    return decodedArray;
  };
</script>

Aşağıdaki CallJsExample1 bileşen:

  • Bir convertArray JS düğmeyi InvokeAsync () seçerken işlevini ile Convert Array çağırır.
  • İşlev JS çağrıldıktan sonra, geçirilen dizi bir dizeye dönüştürülür. dizesini görüntülemek için bileşene döndürülür ( text ).

Pages/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 &copy;2005 <a href="https://www.uphe.com">Universal Pictures</a>: 
    <a href="https://www.uphe.com/movies/serenity">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));
    }
}

Döndürülen değeri okumadan JavaScript işlevlerini çağırma ( InvokeVoidAsync )

Şu InvokeVoidAsync zaman kullanın:

</body>( ) veya ( ) kapanış wwwroot/index.html Blazor WebAssembly Pages/_Layout.cshtml etiketinin içinde bir işlev Blazor Server displayTickerAlert1 JS girin. işlevi ile InvokeVoidAsync çağrılır ve bir değer dönmez:

<script>
  window.displayTickerAlert1 = (symbol, price) => {
    alert(`${symbol}: $${price}!`);
  };
</script>

Bileşen ( .razor ) örneği ( InvokeVoidAsync )

TickerChanged aşağıdaki handleTickerChanged1 bileşende yöntemini CallJsExample2 çağıran.

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

Sınıf ( .cs ) örneği ( InvokeVoidAsync )

JsInteropClasses1.cs:

using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses1
{
    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 aşağıdaki handleTickerChanged1 bileşende yöntemini CallJsExample3 çağıran.

Pages/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()
    {
        if (jsClass is not null)
        {
            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 işlevlerini çağırma ve döndürülen değeri okuma ( InvokeAsync )

InvokeAsync.NET'in bir çağrının sonucu okuması gerektiği zaman JS kullanın.

</body>( ) veya ( ) kapanış wwwroot/index.html Blazor WebAssembly Pages/_Layout.cshtml etiketinin içinde bir işlev Blazor Server displayTickerAlert2 JS girin. Aşağıdaki örnek, çağıranın görüntülemesi için bir dize döndürür:

<script>
  window.displayTickerAlert2 = (symbol, price) => {
    if (price < 20) {
      alert(`${symbol}: $${price}!`);
      return "User alerted in the browser.";
    } else {
      return "User NOT alerted.";
    }
  };
</script>

Bileşen ( .razor ) örneği ( InvokeAsync )

TickerChanged yöntemini handleTickerChanged2 çağırarak döndürülen dizeyi aşağıdaki bileşende CallJsExample4 görüntüler.

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

Sınıf ( .cs ) örneği ( InvokeAsync )

JsInteropClasses2.cs:

using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses2
{
    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 yöntemini handleTickerChanged2 çağırarak döndürülen dizeyi aşağıdaki bileşende CallJsExample5 görüntüler.

Pages/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()
    {
        if (jsClass is not null)
        {
            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();
}

Dinamik içerik oluşturma senaryoları

BuildRenderTreeile dinamik içerik oluşturma için özniteliğini [Inject] kullanın:

[Inject]
IJSRuntime JS { get; set; }

Bir Blazor Server uygulamanın ne zaman önceden işleyici olduğunu algılama

Bu bölüm Blazor Server , ve için Blazor WebAssembly PreRender bileşenleri tarafından barındırılan uygulamalar için geçerlidir Razor . Prerendering, içinde ele alınmıştır ASP.NET Core bileşenlerini önceden ASP.NET Core Razor ve tümleştirin .

Bir uygulama prerendering olduğunda, JavaScript 'e çağırma gibi bazı eylemler mümkün değildir. Bileşenler, ön işlenmiş olduğunda farklı şekilde işlenmesi gerekebilir.

Aşağıdaki örnekte, setElementText1 işlevi <head> wwwroot/index.html ( Blazor WebAssembly ) veya Pages/_Layout.cshtml () öğesinin içine yerleştirilir Blazor Server . İşlevi ile çağrılır JSRuntimeExtensions.InvokeVoidAsync ve bir değer döndürmez:

<script>
  window.setElementText1 = (element, text) => element.innerText = text;
</script>

Uyarı

Yukarıdaki örnek yalnızca tanıtım amacıyla Belge Nesne Modeli (DOM) değiştirir. JavaScript ile doğrudan JavaScript 'in değiştirilmesi, JavaScript 'in değişiklik izlemesini kesintiye uğradığı için çoğu senaryoda önerilmez Blazor . Daha fazla bilgi için bkz. Blazor JavaScript birlikte çalışabilirliği JS (birlikte çalışma).

Bu tür çağrıların çalışacağı bir nokta kadar JavaScript birlikte çalışma çağrılarını geciktirmek için OnAfterRender{Async} yaşam döngüsü olayınıgeçersiz kılın. Bu olay yalnızca uygulama tamamen işlendikten sonra çağırılır.

Pages/PrerenderedInterop1.razor:

@page "/prerendered-interop-1"
@using Microsoft.JSInterop
@inject IJSRuntime JS

<div @ref="divElement">Text during render</div>

@code {
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JS.InvokeVoidAsync(
                "setElementText1", divElement, "Text after render");
        }
    }
}

Not

Önceki örnek, istemciyi genel yöntemlerle pollutes. Üretim uygulamalarında daha iyi bir yaklaşım için bkz. JavaScript modüllerinde JavaScript yalıtımı.

Örnek:

export setElementText1 = (element, text) => element.innerText = text;

Aşağıdaki bileşen, prerendering ile uyumlu bir şekilde bileşenin başlatma mantığının bir parçası olarak JavaScript birlikte çalışabilirinin nasıl kullanılacağını göstermektedir. Bileşen içinden bir işleme güncelleştirmesi tetiklemenin mümkün olduğunu gösterir OnAfterRenderAsync . Bu senaryoda bir sonsuz döngü oluşturmaktan kaçınmak için geliştirici dikkatli olmalıdır.

Aşağıdaki örnekte, setElementText2 işlevi <head> wwwroot/index.html ( Blazor WebAssembly ) veya Pages/_Layout.cshtml () öğesinin içine yerleştirilir Blazor Server . İşlevi ile çağrılır IJSRuntime.InvokeAsync ve bir değer döndürür:

<script>
  window.setElementText2 = (element, text) => {
    element.innerText = text;
    return text;
  };
</script>

Uyarı

Yukarıdaki örnek yalnızca tanıtım amacıyla Belge Nesne Modeli (DOM) değiştirir. JavaScript ile doğrudan JavaScript 'in değiştirilmesi, JavaScript 'in değişiklik izlemesini kesintiye uğradığı için çoğu senaryoda önerilmez Blazor . Daha fazla bilgi için bkz. Blazor JavaScript birlikte çalışabilirliği JS (birlikte çalışma).

Burada JSRuntime.InvokeAsync çağrılır, ElementReference OnAfterRenderAsync bileşen Işlenene kadar JavaScript öğesi olmadığından, yalnızca ' de kullanılır.

StateHasChanged JavaScript birlikte çalışma çağrısından alınan yeni durumla birlikte bileşeni yeniden yüklemek için çağrılır (daha fazla bilgi için bkz BlazorASP.NET Core bileşen işleme .). Kod sonsuz döngü oluşturmaz çünkü StateHasChanged yalnızca olduğu zaman çağrılır data null .

Pages/PrerenderedInterop2.razor:

@page "/prerendered-interop-2"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS

<p>
    Get value via JS interop call:
    <strong id="val-get-by-interop">@(data ?? "No value yet")</strong>
</p>

<p>
    Set value via JS interop call:
</p>

<div id="val-set-by-interop" @ref="divElement"></div>

@code {
    private string? data;
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && data == null)
        {
            data = await JS.InvokeAsync<string>(
                "setElementText2", divElement, "Hello from interop call!");

            StateHasChanged();
        }
    }
}

Not

Önceki örnek, istemciyi genel yöntemlerle pollutes. Üretim uygulamalarında daha iyi bir yaklaşım için bkz. JavaScript modüllerinde JavaScript yalıtımı.

Örnek:

export setElementText2 = (element, text) => {
  element.innerText = text;
  return text;
};

JavaScipt'nin konumu

JSJavaScript ( ) birlikte çalışabilirlik (birlikte çalışma) genel bakış makalesinde açıklanan yaklaşımlardan birini kullanarak JavaScript JS ( ) kodunu yükleme:

Modüllerde betikleri JS yalıtma hakkında bilgi için JavaScript modüllerde JavaScript yalıtımı bölümüne bakın.

Uyarı

Etiket dinamik olarak güncelleştirilenene kadar bir bileşen dosyasına <script> ( .razor ) etiket <script> ekleyemezsiniz.

JavaScript modüllerde JavaScript yalıtımı

Blazorstandart JavaScript JS modüllerde JavaScript ( ) yalıtımını(ECMAScript belirtimi) sağlar.

JS yalıtım aşağıdaki avantajları sağlar:

  • İçeri JS aktarılan dosya artık genel ad alanını etkilemeyecek.
  • Bir kitaplığın ve bileşenlerin tüketicilerinin ilgili 'i içeri aktarması JS gerekmez.

Örneğin, aşağıdaki modül JS tarayıcı penceresi JS istemini göstermek için bir işlevi dışarı aktarıyor. Aşağıdaki kodu JS bir dış dosyaya JS girin.

wwwroot/scripts.js:

export function showPrompt(message) {
  return prompt(message, 'Type anything here');
}

Önceki modülü bir uygulama veya sınıf kitaplığına klasöründe statik web varlığı olarak ekleyin ve ardından örneği çağırarak JS wwwroot modülü .NET InvokeAsync koduna IJSRuntime aktarın.

IJSRuntime , .NET kodundan IJSObjectReference bir nesneye başvuru temsil eden JS modülünü olarak içeri aktarıyor. Modülden IJSObjectReference dışarı aktaran işlevleri JS çağırmak için kullanın.

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

Yukarıdaki örnekte:

  • Kural gereği import tanımlayıcı, özellikle bir modülü içeri aktarma için kullanılan özel bir JS tanımlayıcıdır.
  • Kararlı statik web varlığı JS yolunu kullanarak modülün dış dosyasını belirtin: ./{SCRIPT PATH AND FILENAME (.js)} , burada:
    • Dosyanın doğru statik varlık yolunu oluşturmak için geçerli dizin ( ./ ) için yol kesimi JS gereklidir.
    • Yer {SCRIPT PATH AND FILENAME (.js)} tutucu, altındaki yol ve dosya adıdır. wwwroot
  • içinde atık IJSObjectReference toplama için atıyor. IAsyncDisposable.DisposeAsync

Bir modülü dinamik olarak içeri aktarma işlemi için bir ağ isteği gerekir, bu nedenle yalnızca çağrılarak zaman uyumsuz olarak elde InvokeAsync edilebilir.

IJSInProcessObjectReference , işlevleri zaman JS uyumlu olarak çağrılan bir nesneye başvuru temsil eder.

Not

Dış dosya JS bir sınıf kitaplığı tarafından Razor sağlanmalıdır,kararlı statik web varlık yolunu kullanarak JS modülün dosyasını belirtin: ./_content/{PACKAGE ID}/{SCRIPT PATH AND FILENAME (.js)}

  • Dosyanın doğru statik varlık yolunu oluşturmak için geçerli dizin ( ./ ) için yol kesimi JS gereklidir.
  • Yer {PACKAGE ID} tutucu, kitaplığın paket kimliğidir. Proje dosyasında belirtilmezse paket kimliği <PackageId> varsayılan olarak projenin derleme adını kullanır. Aşağıdaki örnekte, kitaplığın derleme adı ve ComponentLibrary kitaplığın proje dosyası belirtmez. <PackageId>
  • Yer {SCRIPT PATH AND FILENAME (.js)} tutucu, altındaki yol ve dosya adıdır. wwwroot Aşağıdaki örnekte, dış JS dosya ( script.js ) sınıf kitaplığının klasörüne wwwroot yerleştirilir.
var module = await js.InvokeAsync<IJSObjectReference>(
    "import", "./_content/ComponentLibrary/scripts.js");

Daha fazla bilgi için bkz. Razorsınıf kitaplıklarından ASP.NET Core bileşenleri Razor kullanma.

Öğelere başvuru yakalama

Bazı JavaScript ( JS ) birlikte çalışma senaryoları, HTML öğelerine başvuru gerektirir. Örneğin, kullanıcı arabirimi kitaplığı başlatma için öğe başvurusu gerektirebilir veya veya gibi bir öğe üzerinde komut gibi API'leri çağırmanız click play gerekir.

Aşağıdaki yaklaşımı kullanarak bir bileşendeki HTML öğelerine başvuruları yakalama:

  • HTML @ref öğesine bir öznitelik ekleyin.
  • Adı özniteliğin ElementReference değeriyle eşleşen türde bir alan @ref tanımlayın.

Aşağıdaki örnek, öğesine bir başvuru yakalamayı username <input> gösterir:

<input @ref="username" ... />

@code {
    private ElementReference username;
}

Uyarı

Yalnızca ile etkileşim kurmamış boş bir öğenin içeriğinin mutadi için bir öğe başvurusu Blazor kullanın. Bu senaryo, üçüncü taraf BIR API öğeye içerik sağlarken yararlıdır. öğesiyle etkileşim kurmaz, çünkü öğesinin gösterimi ile öğenin gösterimi Blazor Blazor (DOM) arasında çakışma Belge Nesne Modeli yoktur.

Aşağıdaki örnekte, bu öğenin liste öğelerini ( ) nesneden doldurmak için DOM ile etkileşim kurduğundan sıralanmamış listenin içeriğini ( ) değiştirme ul Blazor <li> Todos tehlikelidir:

<ul @ref="MyList">
    @foreach (var item in Todos)
    {
        <li>@item.Text</li>
    }
</ul>

Birlikte çalışma, öğenin içeriğini değiştirmesi ve öğeye farkları uygulama denemesi olursa, farklar JS MyList Blazor DOM ile eşleşmez.

Daha fazla bilgi için bkz. Blazor JavaScript birlikte çalışabilirliği JS (birlikte çalışma).

birlikte ElementReference çalışma yoluyla JS koda JS geçirildi. Kod, JS HTMLElement normal DOM API'leriyle birlikte kullanabileceği bir örnek alır. Örneğin, aşağıdaki kod bir öğeye fare tıklaması gönderilmesini sağlayan bir .NET uzantı yöntemi ( TriggerClickEvent ) tanımlar.

işlevi JS clickElement geçirilen click HTML öğesinde bir olay oluşturur ( element ):

window.interopFunctions = {
  clickElement : function (element) {
    element.click();
  }
}

JSDeğer döndürmeyen bir işlevi çağırmak için kullanın JSRuntimeExtensions.InvokeVoidAsync . Aşağıdaki kod, click yakalanmış olan önceki işlevi çağırarak istemci tarafı olayını tetikler JS ElementReference :

@inject IJSRuntime JS

<button @ref="exampleButton">Example Button</button>

<button @onclick="TriggerClick">
    Trigger click event on <code>Example Button</code>
</button>

@code {
    private ElementReference exampleButton;

    public async Task TriggerClick()
    {
        await JS.InvokeVoidAsync(
            "interopFunctions.clickElement", exampleButton);
    }
}

Bir genişletme yöntemi kullanmak için, örneği alan bir statik genişletme yöntemi oluşturun IJSRuntime :

public static async Task TriggerClickEvent(this ElementReference elementRef, 
    IJSRuntime js)
{
    await js.InvokeVoidAsync("interopFunctions.clickElement", elementRef);
}

clickElementYöntemi doğrudan nesnesi üzerinde çağrılır. Aşağıdaki örnek, TriggerClickEvent yöntemin ad alanından kullanılabilir olduğunu varsayar JsInteropClasses :

@inject IJSRuntime JS
@using JsInteropClasses

<button @ref="exampleButton">Example Button</button>

<button @onclick="TriggerClick">
    Trigger click event on <code>Example Button</code>
</button>

@code {
    private ElementReference exampleButton;

    public async Task TriggerClick()
    {
        await exampleButton.TriggerClickEvent(JS);
    }
}

Önemli

exampleButtonDeğişken yalnızca bileşen işlendikten sonra doldurulur. Doldurulmamış bir ElementReference koda geçirilmemişse JS , JS kod değerini alır null . Bileşen işlemeyi tamamladıktan sonra öğe başvurularını işlemek için, OnAfterRenderAsync veya OnAfterRender bileşen yaşam döngüsü yöntemlerinikullanın.

Genel türlerle çalışırken ve bir değer döndürürken şunu kullanın ValueTask<TResult> :

public static ValueTask<T> GenericMethod<T>(this ElementReference elementRef, 
    IJSRuntime js)
{
    return js.InvokeAsync<T>("{JAVASCRIPT FUNCTION}", elementRef);
}

{JAVASCRIPT FUNCTION}Yer tutucu, JS işlev tanımlayıcısıdır.

GenericMethod , bir türü olan doğrudan nesnesi üzerinde çağrılır. Aşağıdaki örnek, GenericMethod ad alanından kullanılabilir olduğunu varsayar JsInteropClasses :

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

Bileşenler arasında başvuru öğeleri

ElementReferenceŞu nedenle bileşenler arasında geçirilebilir:

Bir üst bileşen için bir öğe başvurusunu diğer bileşenlere kullanılabilir hale getirmek için, üst bileşen şunları yapabilir:

  • Alt bileşenlerin geri çağırmaları kaydetmesine izin ver.
  • Geçirilen öğe başvurusuyla olay sırasında kayıtlı geri çağırmaları çağırın OnAfterRender . Bu yaklaşım dolaylı olarak, alt bileşenlerin üst öğenin öğe başvurusuyla etkileşime geçmesini sağlar.

Aşağıdaki stili <head> wwwroot/index.html ( Blazor WebAssembly ) veya Pages/_Layout.cshtml ( Blazor Server ) öğesine ekleyin:

<style>
    .red { color: red }
</style>

Aşağıdaki betiği </body> wwwroot/index.html ( Blazor WebAssembly ) veya Pages/_Layout.cshtml () kapatma etiketinin içine ekleyin Blazor Server :

<script>
  function setElementClass(element, className) {
    var myElement = element;
    myElement.classList.add(className);
  }
</script>

Pages/CallJsExample7.razor (üst bileşen):

@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?" />

Pages/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)
            {
                try
                {
                    subscription.OnNext(title);
                }
                catch (Exception)
                {
                    throw;
                }
            }
        }

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

Önceki örnekte, uygulamanın ad alanı, BlazorSample klasördeki bileşenlerdir Pages . Kodu yerel olarak test ediyorsanız, ad alanını güncelleştirin.

Shared/SurveyPrompt.razor (alt bileşen):

@inject IJSRuntime JS

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

Shared/SurveyPrompt.razor.cs:

using System;
using Microsoft.AspNetCore.Components;

namespace BlazorSample.Shared
{
    public partial class SurveyPrompt : 
        ComponentBase, IObserver<ElementReference>, IDisposable
    {
        private IDisposable? subscription = null;

        [Parameter]
        public IObservable<ElementReference>? Parent { 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();
        }
    }
}

Önceki örnekte, uygulamanın ad alanı, BlazorSample klasördeki paylaşılan bileşenlerdir Shared . Kodu yerel olarak test ediyorsanız, ad alanını güncelleştirin.

Harden JavaScript birlikte çalışma çağrıları

Bu bölüm öncelikle uygulamalar için geçerlidir Blazor Server , ancak Blazor WebAssembly koşullar bunu garanti altına JS alıyorsa, uygulamalar birlikte çalışma zaman aşımlarını de ayarlayabilir.

Blazor ServerUygulamalarda, JavaScript ( JS ) birlikte çalışması ağ hataları nedeniyle başarısız olabilir ve güvenilmez olarak değerlendirilmelidir. Varsayılan olarak, Blazor Server uygulamalar birlikte çalışma çağrıları için bir dakikalık zaman aşımı kullanır JS . Bir uygulama daha agresif bir zaman aşımına uğrayabileceği zaman aşımını aşağıdaki yaklaşımlardan birini kullanarak ayarlayın.

İle genel bir zaman aşımı ayarlayın Program.cs CircuitOptions.JSInteropDefaultCallTimeout :

builder.Services.AddServerSideBlazor(
    options => options.JSInteropDefaultCallTimeout = {TIMEOUT});

{TIMEOUT}Yer tutucu bir TimeSpan (örneğin, TimeSpan.FromSeconds(80) ).

Bileşen kodunda çağrı başına zaman aşımı ayarlayın. Belirtilen zaman aşımı, tarafından ayarlanan genel zaman aşımını geçersiz kılar JSInteropDefaultCallTimeout :

var result = await JS.InvokeAsync<string>("{ID}", {TIMEOUT}, new[] { "Arg1" });

Yukarıdaki örnekte:

  • {TIMEOUT}Yer tutucu bir TimeSpan (örneğin, TimeSpan.FromSeconds(80) ).
  • {ID}Yer tutucu, çağrılacak işlevin tanıtıcısıdır. Örneğin, değeri someScope.someFunction işlevini çağırır window.someScope.someFunction .

JSBirlikte çalışma hatalarının yaygın bir nedeni uygulamalarda ağ hatalarından, ancak Blazor Server JS uygulamalarda birlikte çalışabilirlik çağrıları için çağrı başına zaman aşımları ayarlanabilir Blazor WebAssembly . Uygulamada devre yok olsa da SignalR Blazor WebAssembly , JS birlikte çalışabilirlik çağrıları uygulamalarda uygulanan diğer nedenlerden dolayı başarısız olabilir Blazor WebAssembly .

Kaynak tükenmesi hakkında daha fazla bilgi için bkz Güvenlik açıkları için tehdit azaltma ASP.NET Core Blazor Server ..

Döngüsel nesne başvurularından kaçının

Döngüsel başvurular içeren nesneler, her biri için istemcide serileştirilemiyor:

  • .NET yöntemi çağrıları.
  • Dönüş türünün döngüsel başvuruları olduğunda C# ' den JavaScript Yöntem çağrıları.

Kullanıcı arabirimini işleyen JavaScript kitaplıkları

Bazen JS tarayıcı belge nesne modeli (DOM) içinde görünür kullanıcı arabirimi öğeleri üreten JavaScript () kitaplıklarını kullanmak isteyebilirsiniz. İlk bakışta, bu durum, Blazor Dağıtılmış Sistem, Dom öğelerinin ağacı üzerinde denetime sahip olma ve bazı dış kodlar Dom ağacını bir kez değiştiğinden ve bunların SLA 'ları uygulama mekanizmasını geçersiz kılacağından hatalara açık hale gelebilir. Bu, Blazor belirli bir kısıtlama değildir. Aynı zorluk, fark tabanlı kullanıcı arabirimi çerçevesiyle oluşur.

Neyse ki, dışarıdan oluşturulan kullanıcı arabirimini Razor bileşen Kullanıcı arabirimine güvenilir bir şekilde eklemek çok basittir. Önerilen yöntem, bileşenin kodunun ( .razor dosya) boş bir öğe üretmektir. BlazorYayılma sisteminin en çok ilgisi olduğu gibi, öğe her zaman boştur, bu nedenle işleyici öğesi için bir değer değil, onun içeriğini tek başına bırakır. Bu, öğesini rastgele dışarıdan yönetilen içerikle doldurmayı güvenli hale getirir.

Aşağıdaki örnek kavramı gösterir. ifOlduğunda ifadesinin içinde firstRender true , unmanagedElement Blazor birlikte çalışma kullanma dışında etkileşime geçin JS . Örneğin, JS öğesini doldurmak için bir dış kitaplık çağırın. Blazor Bu bileşen kaldırılana kadar öğenin içeriğini tek başına bırakır. Bileşen kaldırıldığında, bileşenin tüm DOM alt ağacı da kaldırılır.

<h1>Hello! This is a Blazor component rendered at @DateTime.Now</h1>

<div @ref="unmanagedElement"></div>

@code {
    private HtmlElement unmanagedElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            ...
        }
    }
}

Açık kaynaklı Mapbox API 'lerikullanarak etkileşimli bir harita işleyen aşağıdaki örneği göz önünde bulundurun.

Aşağıdaki JS Modül uygulamaya yerleştirildi veya bir sınıf kitaplığından kullanılabilir hale getirildi Razor .

Not

Mapbox haritasını oluşturmak Için, Mapbox oturum açma 'dan bir erişim belirteci alın ve {ACCESS TOKEN} aşağıdaki kodda göründüğü yere sağlayın.

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

Doğru stil oluşturmak için, ana bilgisayar HTML sayfasına aşağıdaki stil sayfası etiketini ekleyin.

wwwroot/index.html( Blazor WebAssembly ) Veya Pages/_Layout.cshtml () içinde Blazor Server , aşağıdaki <link> öğeyi <head> öğe biçimlendirmesine ekleyin:

<link href="https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css" 
    rel="stylesheet" />

Pages/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).AsTask();
        }
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (mapInstance is not null)
        {
            await mapInstance.DisposeAsync();
        }

        if (mapModule is not null)
        {
            await mapModule.DisposeAsync();
        }
    }
}

Yukarıdaki örnek, etkileşimli bir harita Kullanıcı arabirimi üretir. Kullanıcı:

  • , Kaydırmak veya yakınlaştırmak için sürükleyebilir.
  • Önceden tanımlanmış konumlara atlanacak düğmeleri seçin.

Esistol, Birleşik Krallık ve Tokyo, Japonya ' ı seçmek için bkz. Tokyo 'daki mapbox açık Haritası

Yukarıdaki örnekte:

  • , <div> İle ilişkili olduğu kadar @ref="mapElement" boş bırakılır Blazor . mapbox-gl.jsBetik, öğeyi güvenle doldurabilir ve içeriğini değiştirebilir. Bu tekniği JS Kullanıcı arabirimini işleyen herhangi bir kitaplıkla kullanın. Bir üçüncü taraf JS Spa çerçevesinden bileşenleri Blazor , sayfanın diğer bölümlerine ulaşmaya ve bunları değiştirmeye çalıştıklarında olduğu sürece ekleyebilirsiniz. Dış JS kodun Blazor boş olarak söz konusu olmayan öğeleri değiştirmesi güvenli değildir.
  • Bu yaklaşımı kullanırken, Blazor Dom öğelerinin nasıl koruduğunu veya yok olduğunu gösteren kuralları göz önünde bulundurun. Bileşen, düğme tıklama olayları ' nı güvenli bir şekilde işler ve var olan eşleme örneğini güncelleştirir, çünkü DOM öğeleri varsayılan olarak mümkün olduğunca korunur. Bir döngünün içindeki harita öğelerinin bir listesini işlerinizden @foreach , @key bileşen örneklerinin korunmasını sağlamak için kullanmak istersiniz. Aksi takdirde, liste verilerinde yapılan değişiklikler bileşen örneklerinin önceki örneklerin durumunu istenmeyen bir şekilde tutmasına neden olabilir. Daha fazla bilgi için bkz . @key öğeleri ve bileşenleri korumak için kullanma.
  • Örnek, JS BIR ES6 modülündeki mantığı ve bağımlılıkları kapsüller ve tanımlayıcıyı kullanarak modülü dinamik olarak yükler import . Daha fazla bilgi için bkz. JavaScript modüllerinde JavaScript yalıtımı.

Bayt dizisi desteği

BlazorJSbayt dizilerinin kodlanmasını/kodunu çözmeyi önleyen, iyileştirilmiş bayt dizisi birlikte çalışmasını destekler. Aşağıdaki örnek, JS bir bayt dizisini JavaScript 'e geçirmek için birlikte çalışabilirliği kullanır.

</body> wwwroot/index.html ( Blazor WebAssembly ) Veya () kapanış etiketinin içinde Pages/_Layout.cshtml Blazor Server bir receiveByteArray JS işlev sağlayın. İşlevi ile çağrılır InvokeVoidAsync ve bir değer döndürmez:

<script>
  window.receiveByteArray = (bytes) => {
    let utf8decoder = new TextDecoder();
    let str = utf8decoder.decode(bytes);
    return str;
  };
</script>

Pages/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">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);
    }
}

JavaScript 'ten .NET çağrılırken bir bayt dizisi kullanma hakkında daha fazla bilgi için bkz ASP.NET Core içindeki JavaScript işlevlerinden .NET yöntemlerini çağırın Blazor ..

JavaScript birlikte çalışma çağrılarında boyut sınırları

Bu bölüm yalnızca uygulamalar için Blazor Server geçerlidir. içinde, çerçeve JavaScript (JS) birlikte çalışma giriş Blazor WebAssembly ve çıkışlarının boyutuna bir sınır uygulamaz.

'de, JS birlikte çalışma çağrıları, hub yöntemleri için izin verilen en büyük gelen ileti boyutuyla sınırlıdır ve bu da Blazor Server SignalR tarafından HubOptions.MaximumReceiveMessageSize zorlanır (varsayılan olarak: 32 KB). JS'den SignalR .NET'e iletiler MaximumReceiveMessageSize hata verir. Çerçeve, hub'dan istemciye ileti SignalR boyutuna bir sınır dayatmaz.

Günlüğe SignalR kaydetme Hata Ayıklama veya İzleme olarak ayarlanmasa, yalnızca tarayıcının geliştirici araçları konsolunda bir ileti boyutu hatası görüntülenir:

Hata: Bağlantı şu hatayla kesildi: 'Hata: Sunucu kapatılırken bir hata döndürüldü: Bağlantı bir hatayla kapatıldı.'.

Sunucu SignalR tarafı günlüğe kaydetme Hata Ayıklama veya İzleme olarak ayarlanırsa, sunucu tarafı günlük kaydı ileti boyutu hatası için bir InvalidDataException gösterir.

appsettings.Development.json:

{
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.AspNetCore.SignalR": "Debug"
    }
  }
}

Hata:

System.IO.InvalidDataException: İleti boyutu üst sınırı 32768B aşıldı. İleti boyutu AddHubOptions içinde yalıtabilirsiniz.

içinde ayarını kullanarak sınırı MaximumReceiveMessageSize Program.cs artırabilirsiniz. Aşağıdaki örnek, en fazla alma iletisi boyutunu 64 KB (64 * 1024) olarak ayarlar:

builder.Services.AddServerSideBlazor()
    .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);

Gelen ileti boyutu sınırının artırılması, daha fazla sunucu kaynağı gerektirmenin maliyetine neden olur ve kötü amaçlı bir kullanıcının artan risklerine SignalR karşı sunucuyu ortaya çıkarır. Buna ek olarak, bellekte dize veya byte dizileri olarak büyük miktarda içeriğin okunması atık toplayıcı ile kötü şekilde çalışarak ek performans cezalarına neden olan ayırmalara da neden olabilir.

JS ile uygulamalar arasında büyük miktarda veri aktaran kod geliştirme konusunda aşağıdaki kılavuzu Blazor göz önünde Blazor Server bulundurabilirsiniz:

  • Gelen ileti boyutu sınırına göre daha büyük verileri aktarımı için yerel akış SignalR birlikte çalışma desteğini kullanın:
  • Genel ipuçları:
    • JS ve C# kodunda büyük nesneleri ayırmayın.
    • İşlem tamamlandığında veya iptal edilirken tüketilen belleği serbest bırak.
    • Güvenlik amacıyla aşağıdaki ek gereksinimleri zorunlu kılın:
      • Geçir konan en büyük dosya veya veri boyutunu bildirebilirsiniz.
      • İstemciden sunucuya en düşük karşıya yükleme oranını bildirin.
    • Veriler sunucu tarafından alındıktan sonra şu veriler olabilir:
      • Tüm segmentler toplanana kadar geçici olarak bir bellek arabelleğinde depolanır.
      • Hemen tüketilir. Örneğin, veriler hemen bir veritabanında depolanmış veya diske yazıldığı için her kesim alınmıştır.

Unmarshalled JavaScript birlikte çalışabilirliği

Blazor WebAssembly .NET nesneleri JavaScript ( JS ) birlikte çalışması için serileştirildiğinde ve aşağıdakilerden biri doğru olduğunda, bileşenler düşük performansa sahip olabilir:

  • Yüksek hacimli .NET nesneleri hızla serileştirilir. Örneğin, JS bir fare tekerleği dönme gibi bir giriş cihazını taşıma sırasında birlikte çalışma çağrıları yapıldığında kötü performans ortaya çıkabilir.
  • Büyük .NET nesneleri veya çok sayıda .NET nesnesi birlikte çalışma için serileştirilmelidir JS . Örneğin, JS birlikte çalışma çağrıları onlarca dosya serileştirmesini gerektirdiğinde kötü performans oluşabilir.

IJSUnmarshalledObjectReferenceJSişlevleri .net verilerinin serileştirilmesinin bir yükü olmadan çağrılabilen bir nesne başvurusunu temsil eder.

Aşağıdaki örnekte:

  • Dize ve tamsayı içeren bir struct , serileştirilmemiş olarak geçirilir JS .
  • JS işlevler, verileri işler ve çağırana bir Boole ya da dize döndürür.
  • JSDize doğrudan bir .net nesnesine dönüştürülebilir değildir string . unmarshalledFunctionReturnStringİşlevi BINDING.js_string_to_mono_string bir dizenin dönüştürülmesini yönetmek için çağırır JS .

Not

Aşağıdaki örnekler, geçirilen Yapı JS kötü bileşen performansına neden olmadığı için bu senaryo için tipik kullanım durumları değildir. Örnek, yalnızca serileştirilmiş .NET verilerini geçirme kavramlarını göstermek için küçük bir nesne kullanır.

Aşağıdaki <script> bloğu wwwroot/index.html ( Blazor WebAssembly ) veya Pages/_Layout.cshtml () içine yerleştirin Blazor Server . Alternatif olarak, öğesini JS kapatma etiketinin içinde başvurulan bir dış dosyaya ekleyin </body> . Örneğin, <script src="{SCRIPT PATH AND FILE NAME (.js)}"></script> {SCRIPT PATH AND FILE NAME (.js)} yer tutucunun, betiğin yolu ve dosya adı olduğu yerdir.

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

Uyarı

js_string_to_mono_stringİşlevin adı, davranışı ve varlığı gelecek .NET sürümünde değişebilir. Örneğin:

  • İşlevin yeniden adlandırılması olasıdır.
  • İşlevin kendisi, çerçeve tarafından dizelerin otomatik dönüştürülmesini tercih ederek kaldırılabilir.

Pages/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;
    }
}

IJSUnmarshalledObjectReferenceÖrnek, C# kodunda atılmazsa, ' de atılabilir JS . Aşağıdaki dispose işlev, öğesinden çağrıldığında nesne başvurusunu ortadan kaldırmaktadır JS :

window.exampleJSObjectReferenceNotDisposedInCSharp = () => {
  return {
    dispose: function () {
      DotNet.disposeJSObjectReference(this);
    },

    ...
  };
}

Dizi türleri JS , kullanarak nesnelerden .net nesnelerine dönüştürülebilir js_typed_array_to_array , ancak JS dizi türü belirlenmiş bir dizi olmalıdır. İçindeki diziler JS , C# kodunda bir .NET nesne dizisi () olarak okunabilir object[] .

Dize dizileri gibi diğer veri türleri dönüştürülebilirler, ancak yeni bir mono dizi nesnesi ( mono_obj_array_new ) oluşturulmasını ve değerini () ayarlamayı gerektirebilir mono_obj_array_set .

Uyarı

JS ,, Blazor ve gibi Framework tarafından sunulan işlevler js_typed_array_to_array mono_obj_array_new mono_obj_array_set ad değişikliklerine, davranış değişikliklerine tabidir veya gelecek .net sürümlerinde kaldırıllardır.

.NET 'ten JavaScript 'e akış

Blazor doğrudan .NET 'ten JavaScript 'e veri akışını destekler. Akışlar kullanılarak oluşturulur DotNetStreamReference .

DotNetStreamReference bir .NET akışını temsil eder ve aşağıdaki parametreleri kullanır:

  • stream: JavaScript 'e gönderilen akış.
  • leaveOpen: Akışın iletimden sonra açık olup olmadığını belirler. Değer sağlanmazsa, leaveOpen Varsayılan olarak olur false .

JavaScript 'te, verileri almak için bir dizi arabelleği veya okunabilir bir akış kullanın:

  • Şunu kullanarak ArrayBuffer :

    async function streamToJavaScript(streamRef) {
      const data = await streamRef.arrayBuffer();
    }
    
  • Şunu kullanarak ReadableStream :

    async function streamToJavaScript(streamRef) {
      const stream = await streamRef.stream();
    }
    

C# kodunda:

using var streamRef = new DotNetStreamReference(stream: {STREAM}, leaveOpen: false);
await JS.InvokeVoidAsync("streamToJavaScript", streamRef);

Yukarıdaki örnekte:

  • {STREAM}Yer tutucu, Stream JavaScript 'e gönderilen ' i temsil eder.
  • JS eklenen bir IJSRuntime örnek.

ASP.NET Core içindeki JavaScript işlevlerinden .NET yöntemlerini çağırın Blazor geriye doğru işlem, JavaScript 'ten .NET 'e akışı içerir.

ASP.NET Core Blazor Dosya indirmeleri içindeki bir dosyanın nasıl indirileceği ele alınmaktadır Blazor .

JavaScript özel durumlarını yakala

JSÖzel durumları yakalamak için JS birlikte çalışabilirliği bir try - catch blokta sarın ve yakalar JSException .

Aşağıdaki örnekte, nonFunction JS işlevi yok. İşlev bulunamadığında, JSException aşağıdaki hatayı gösteren bir ile birlikte kaydedilir Message :

Could not find 'nonFunction' ('nonFunction' was undefined).

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

Ek kaynaklar

Bu makalede, .NET 'teki JavaScript () işlevlerini çağırma ele alınmaktadır JS . .NET yöntemlerinin ' den nasıl çağrılacağını öğrenmek için JS bkz ASP.NET Core içindeki JavaScript işlevlerinden .NET yöntemlerini çağırın Blazor ..

JS.Net 'ten çağırmak için, IJSRuntime soyutlamayı ekleyin ve aşağıdaki yöntemlerden birini çağırın:

İşlevleri çağıran önceki .NET yöntemleri için JS :

  • İşlev tanımlayıcısı ( String ) genel kapsama ( window ) göredir. Çağırmak için window.someScope.someFunction tanımlayıcı olur someScope.someFunction . Çağrılmadan önce işlevi kaydetmeniz gerekmez.
  • İçindeki herhangi bir sayıda JS seri hale getirilebilir bağımsız değişkeni Object[] bir JS işleve geçirin.
  • İptal belirteci ( CancellationToken ), işlemlerin iptal edilmesi gerektiğini belirten bir bildirim yayar.
  • TimeSpan bir işlem için zaman sınırını temsil eder JS .
  • TValueDönüş türü de JS seri hale getirilebilir üzerinde olmalıdır. TValue döndürülen türü ile en iyi eşleşen .NET türüyle eşleşmelidir JS .
  • Yöntemler JS Promise için bir döndürülür InvokeAsync . InvokeAsync ' nin sarmalanmış olduğunu kaldırır Promise ve tarafından beklemiş değeri döndürür Promise .

Blazor ServerPrerendering etkin olan uygulamalar için, JS ilk prerendering sırasında çağırma yapılamaz. JS birlikte çalışma çağrılarının, tarayıcıyla bağlantı kurulana kadar ertelenmesi gerekir. Daha fazla bilgi için bkz. bir uygulamanın ne zaman Blazor Server prerendering bölümüne bakın.

Aşağıdaki örnek TextDecoder , tabanlı bir JS kod çözücüsüne dayalıdır. Örnek, JS Geliştirici kodundan mevcut bir API 'ye bir gereksinimi boşaltan bir C# yönteminden bir işlevin nasıl çağrılacağını gösterir JS . JSİşlevi bir C# yönteminden bir bayt dizisi kabul eder, dizinin kodunu çözer ve görüntülenecek metni bileşene döndürür.

Aşağıdaki JS kodu </body> wwwroot/index.html ( Blazor WebAssembly ) veya Pages/_Host.cshtml () kapanış etiketinin içine ekleyin Blazor Server :

<script>
  window.convertArray = (win1251Array) => {
    var win1251decoder = new TextDecoder('windows-1251');
    var bytes = new Uint8Array(win1251Array);
    var decodedArray = win1251decoder.decode(bytes);
    console.log(decodedArray);
    return decodedArray;
  };
</script>

Aşağıdaki CallJsExample1 bileşen:

  • convertArray JS InvokeAsync Düğme () seçilirken işlevi çağırır Convert Array .
  • JSİşlev çağrıldıktan sonra, geçirilen dizi bir dizeye dönüştürülür. Dize, Display () için bileşene döndürülür text .

Pages/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 &copy;2005 <a href="https://www.uphe.com">Universal Pictures</a>: 
    <a href="https://www.uphe.com/movies/serenity">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));
    }
}

Döndürülen bir değeri okumadan JavaScript işlevlerini çağırma ( InvokeVoidAsync )

Şu InvokeVoidAsync durumlarda kullanın:

  • Bir çağrının sonucunu okumak için .NET gerekli değildir JS .
  • JS işlevler void (0)/void 0 veya tanımsızdöndürür.

</body> wwwroot/index.html ( Blazor WebAssembly ) Veya () kapanış etiketinin içinde Pages/_Host.cshtml Blazor Server bir displayTickerAlert1 JS işlev sağlayın. İşlevi ile çağrılır InvokeVoidAsync ve bir değer döndürmez:

<script>
  window.displayTickerAlert1 = (symbol, price) => {
    alert(`${symbol}: $${price}!`);
  };
</script>

Component ( .razor ) örneği ( InvokeVoidAsync )

TickerChanged``handleTickerChanged1aşağıdaki bileşendeki yöntemini çağırır CallJsExample2 .

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

Class ( .cs ) örneği ( InvokeVoidAsync )

JsInteropClasses1.cs:

using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses1
{
    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``handleTickerChanged1aşağıdaki bileşendeki yöntemini çağırır CallJsExample3 .

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

JavaScript işlevlerini çağırın ve döndürülen bir değeri ( InvokeAsync ) okuyun

InvokeAsync.Net 'in bir çağrının sonucunu okuması gerektiğinde kullanın JS .

</body> wwwroot/index.html ( Blazor WebAssembly ) Veya () kapanış etiketinin içinde Pages/_Host.cshtml Blazor Server bir displayTickerAlert2 JS işlev sağlayın. Aşağıdaki örnek, çağıran tarafından görüntülenmek üzere bir dize döndürür:

<script>
  window.displayTickerAlert2 = (symbol, price) => {
    if (price < 20) {
      alert(`${symbol}: $${price}!`);
      return "User alerted in the browser.";
    } else {
      return "User NOT alerted.";
    }
  };
</script>

Component ( .razor ) örneği ( InvokeAsync )

TickerChanged yöntemini çağırır handleTickerChanged2 ve döndürülen dizeyi aşağıdaki CallJsExample4 bileşende görüntüler.

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

Class ( .cs ) örneği ( InvokeAsync )

JsInteropClasses2.cs:

using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses2
{
    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 yöntemini çağırır handleTickerChanged2 ve döndürülen dizeyi aşağıdaki CallJsExample5 bileşende görüntüler.

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

Dinamik içerik oluşturma senaryoları

Buildrendertreeile dinamik içerik oluşturma için [Inject] özniteliğini kullanın:

[Inject]
IJSRuntime JS { get; set; }

Bir uygulamanın ne zaman Blazor Server prerendering olduğunu Algıla

Bu bölüm Blazor Server , ve için Blazor WebAssembly PreRender bileşenleri tarafından barındırılan uygulamalar için geçerlidir Razor . Prerendering, içinde ele alınmıştır ASP.NET Core bileşenlerini önceden ASP.NET Core Razor ve tümleştirin .

Bir uygulama prerendering olduğunda, JavaScript 'e çağırma gibi bazı eylemler mümkün değildir. Bileşenler, ön işlenmiş olduğunda farklı şekilde işlenmesi gerekebilir.

Aşağıdaki örnekte, setElementText1 işlevi <head> wwwroot/index.html ( Blazor WebAssembly ) veya Pages/_Layout.cshtml () öğesinin içine yerleştirilir Blazor Server . İşlevi ile çağrılır JSRuntimeExtensions.InvokeVoidAsync ve bir değer döndürmez:

<script>
  window.setElementText1 = (element, text) => element.innerText = text;
</script>

Uyarı

Yukarıdaki örnek yalnızca tanıtım amacıyla Belge Nesne Modeli (DOM) değiştirir. JavaScript ile doğrudan JavaScript 'in değiştirilmesi, JavaScript 'in değişiklik izlemesini kesintiye uğradığı için çoğu senaryoda önerilmez Blazor . Daha fazla bilgi için bkz. Blazor JavaScript birlikte çalışabilirliği JS (birlikte çalışma).

Bu tür çağrıların çalışacağı bir nokta kadar JavaScript birlikte çalışma çağrılarını geciktirmek için OnAfterRender{Async} yaşam döngüsü olayınıgeçersiz kılın. Bu olay yalnızca uygulama tamamen işlendikten sonra çağırılır.

Pages/PrerenderedInterop1.razor:

@page "/prerendered-interop-1"
@using Microsoft.JSInterop
@inject IJSRuntime JS

<div @ref="divElement">Text during render</div>

@code {
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JS.InvokeVoidAsync(
                "setElementText1", divElement, "Text after render");
        }
    }
}

Not

Önceki örnek, istemciyi genel yöntemlerle pollutes. Üretim uygulamalarında daha iyi bir yaklaşım için bkz. JavaScript modüllerinde JavaScript yalıtımı.

Örnek:

export setElementText1 = (element, text) => element.innerText = text;

Aşağıdaki bileşen, prerendering ile uyumlu bir şekilde bileşenin başlatma mantığının bir parçası olarak JavaScript birlikte çalışabilirinin nasıl kullanılacağını göstermektedir. Bileşen içinden bir işleme güncelleştirmesi tetiklemenin mümkün olduğunu gösterir OnAfterRenderAsync . Bu senaryoda bir sonsuz döngü oluşturmaktan kaçınmak için geliştirici dikkatli olmalıdır.

Aşağıdaki örnekte, setElementText2 işlevi <head> wwwroot/index.html ( Blazor WebAssembly ) veya Pages/_Layout.cshtml () öğesinin içine yerleştirilir Blazor Server . İşlevi ile çağrılır IJSRuntime.InvokeAsync ve bir değer döndürür:

<script>
  window.setElementText2 = (element, text) => {
    element.innerText = text;
    return text;
  };
</script>

Uyarı

Yukarıdaki örnek yalnızca tanıtım amacıyla Belge Nesne Modeli (DOM) değiştirir. JavaScript ile doğrudan JavaScript 'in değiştirilmesi, JavaScript 'in değişiklik izlemesini kesintiye uğradığı için çoğu senaryoda önerilmez Blazor . Daha fazla bilgi için bkz. Blazor JavaScript birlikte çalışabilirliği JS (birlikte çalışma).

Burada JSRuntime.InvokeAsync çağrılır, ElementReference OnAfterRenderAsync bileşen Işlenene kadar JavaScript öğesi olmadığından, yalnızca ' de kullanılır.

StateHasChanged JavaScript birlikte çalışma çağrısından alınan yeni durumla birlikte bileşeni yeniden yüklemek için çağrılır (daha fazla bilgi için bkz BlazorASP.NET Core bileşen işleme .). Kod sonsuz döngü oluşturmaz çünkü StateHasChanged yalnızca olduğu zaman çağrılır data null .

Pages/PrerenderedInterop2.razor:

@page "/prerendered-interop-2"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS

<p>
    Get value via JS interop call:
    <strong id="val-get-by-interop">@(data ?? "No value yet")</strong>
</p>

<p>
    Set value via JS interop call:
</p>

<div id="val-set-by-interop" @ref="divElement"></div>

@code {
    private string? data;
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && data == null)
        {
            data = await JS.InvokeAsync<string>(
                "setElementText2", divElement, "Hello from interop call!");

            StateHasChanged();
        }
    }
}

Not

Önceki örnek, istemciyi genel yöntemlerle pollutes. Üretim uygulamalarında daha iyi bir yaklaşım için bkz. JavaScript modüllerinde JavaScript yalıtımı.

Örnek:

export setElementText2 = (element, text) => {
  element.innerText = text;
  return text;
};

JavaScipt konumu

JavaScript () JS JS birlikte çalışabilirlik (Interop) genel bakış makalesitarafından tanımlanan yaklaşımlardan birini kullanarak JavaScript () kodunu yükleyin:

JS Modüllerdekibetikleri yalıtma hakkında daha fazla bilgi Için JavaScript modüllerinde JavaScript yalıtım bölümüne bakın.

Uyarı

<script> .razor <script> Etiket dinamik olarak güncelleştirilemediğinden bir bileşen dosyasına () etiket yerleştirmeyin.

JavaScript modüllerinde JavaScript yalıtımı

BlazorJSStandart JavaScript modüllerinde JavaScript () yalıtımına (ECMAScript belirtimi) izin vermez.

JS yalıtım aşağıdaki avantajları sağlar:

  • İçeri aktarılan JS genel ad alanı artık pollutes.
  • Bir kitaplığın ve bileşenlerin tüketicilerinin ilgili 'i içeri aktarması JS gerekmez.

Örneğin, aşağıdaki modül JS tarayıcı penceresi JS istemini göstermek için bir işlevi dışarı aktarıyor. Aşağıdaki kodu JS bir dış dosyaya JS girin.

wwwroot/scripts.js:

export function showPrompt(message) {
  return prompt(message, 'Type anything here');
}

Önceki modülü bir uygulama veya sınıf kitaplığına klasöründe statik web varlığı olarak ekleyin ve ardından örneği çağırarak JS wwwroot modülü .NET InvokeAsync koduna IJSRuntime aktarın.

IJSRuntime , .NET kodundan IJSObjectReference bir nesneye başvuru temsil eden JS modülünü olarak içeri aktarıyor. Modülden IJSObjectReference dışarı aktaran işlevleri JS çağırmak için kullanın.

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

Yukarıdaki örnekte:

  • Kural gereği import tanımlayıcı, özellikle bir modülü içeri aktarma için kullanılan özel bir JS tanımlayıcıdır.
  • Kararlı statik web varlığı JS yolunu kullanarak modülün dış dosyasını belirtin: ./{SCRIPT PATH AND FILENAME (.js)} , burada:
    • Dosyanın doğru statik varlık yolunu oluşturmak için geçerli dizin ( ./ ) için yol kesimi JS gereklidir.
    • Yer {SCRIPT PATH AND FILENAME (.js)} tutucu, altındaki yol ve dosya adıdır. wwwroot

Bir modülü dinamik olarak içeri aktarma işlemi için bir ağ isteği gerekir, bu nedenle yalnızca çağrılarak zaman uyumsuz olarak elde InvokeAsync edilebilir.

IJSInProcessObjectReference , işlevleri zaman JS uyumlu olarak çağrılan bir nesneye başvuru temsil eder.

Not

Dış dosya JS bir sınıf kitaplığı tarafından Razor sağlanmalıdır,kararlı statik web varlık yolunu kullanarak JS modülün dosyasını belirtin: ./_content/{PACKAGE ID}/{SCRIPT PATH AND FILENAME (.js)}

  • Dosyanın doğru statik varlık yolunu oluşturmak için geçerli dizin ( ./ ) için yol kesimi JS gereklidir.
  • Yer {PACKAGE ID} tutucu, kitaplığın paket kimliğidir. Proje dosyasında belirtilmezse paket kimliği <PackageId> varsayılan olarak projenin derleme adını kullanır. Aşağıdaki örnekte, kitaplığın derleme adı ve ComponentLibrary kitaplığın proje dosyası belirtmez. <PackageId>
  • Yer {SCRIPT PATH AND FILENAME (.js)} tutucu, altındaki yol ve dosya adıdır. wwwroot Aşağıdaki örnekte, dış JS dosya ( script.js ) sınıf kitaplığının klasörüne wwwroot yerleştirilir.
var module = await js.InvokeAsync<IJSObjectReference>(
    "import", "./_content/ComponentLibrary/scripts.js");

Daha fazla bilgi için bkz. Razorsınıf kitaplıklarından ASP.NET Core bileşenleri Razor kullanma.

Öğelere başvuru yakalama

Bazı JavaScript ( JS ) birlikte çalışma senaryoları, HTML öğelerine başvuru gerektirir. Örneğin, kullanıcı arabirimi kitaplığı başlatma için öğe başvurusu gerektirebilir veya veya gibi bir öğe üzerinde komut gibi API'leri çağırmanız click play gerekir.

Aşağıdaki yaklaşımı kullanarak bir bileşendeki HTML öğelerine başvuruları yakalama:

  • HTML @ref öğesine bir öznitelik ekleyin.
  • Adı özniteliğin ElementReference değeriyle eşleşen türde bir alan @ref tanımlayın.

Aşağıdaki örnek, öğesine bir başvuru yakalamayı username <input> gösterir:

<input @ref="username" ... />

@code {
    private ElementReference username;
}

Uyarı

Yalnızca ile etkileşim kurmamış boş bir öğenin içeriğinin mutadi için bir öğe başvurusu Blazor kullanın. Bu senaryo, üçüncü taraf BIR API öğeye içerik sağlarken yararlıdır. öğesiyle etkileşim kurmaz, çünkü öğesinin gösterimi ile öğenin gösterimi Blazor Blazor (DOM) arasında çakışma Belge Nesne Modeli yoktur.

Aşağıdaki örnekte, bu öğenin liste öğelerini ( ) nesneden doldurmak için DOM ile etkileşim kurduğundan sıralanmamış listenin içeriğini ( ) değiştirme ul Blazor <li> Todos tehlikelidir:

<ul @ref="MyList">
    @foreach (var item in Todos)
    {
        <li>@item.Text</li>
    }
</ul>

Birlikte çalışma, öğenin içeriğini değiştirmesi ve öğeye farkları uygulama denemesi olursa, farklar JS MyList Blazor DOM ile eşleşmez.

Daha fazla bilgi için bkz. Blazor JavaScript birlikte çalışabilirliği JS (birlikte çalışma).

birlikte ElementReference çalışma yoluyla JS koda JS geçirildi. Kod, JS HTMLElement normal DOM API'leriyle birlikte kullanabileceği bir örnek alır. Örneğin, aşağıdaki kod bir öğeye fare tıklaması gönderilmesini sağlayan bir .NET uzantı yöntemi ( TriggerClickEvent ) tanımlar.

işlevi JS clickElement geçirilen click HTML öğesinde bir olay oluşturur ( element ):

window.interopFunctions = {
  clickElement : function (element) {
    element.click();
  }
}

Değer JS kullanmayan bir işlevi çağırmak için JSRuntimeExtensions.InvokeVoidAsync kullanın. Aşağıdaki kod, yakalanan ile önceki işlevi click çağırarak bir istemci JS tarafı olayı ElementReference tetikler:

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

Uzantı yöntemini kullanmak için örneği alan statik bir uzantı yöntemi IJSRuntime oluşturun:

public static async Task TriggerClickEvent(this ElementReference elementRef, 
    IJSRuntime js)
{
    await js.InvokeVoidAsync("interopFunctions.clickElement", elementRef);
}

clickElementyöntemi doğrudan nesne üzerinde çağrılır. Aşağıdaki örnekte, yöntemin ad TriggerClickEvent alanıyla kullanılabilir olduğu varsay JsInteropClasses gelir:

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

Önemli

Değişkeni exampleButton yalnızca bileşen işdikten sonra doldurulur. Koda ElementReference doldurulmamış bir JS geçiri varsa, JS kod değerini null alır. Bileşen işlemeyi bitirdikten sonra öğe başvurularını işlemek için veya bileşen OnAfterRenderAsync yaşam döngüsü yöntemlerini OnAfterRender kullanın.

Genel türlerle çalışırken ve bir değer döndürerek ValueTask<TResult> kullanın:

public static ValueTask<T> GenericMethod<T>(this ElementReference elementRef, 
    IJSRuntime js)
{
    return js.InvokeAsync<T>("{JAVASCRIPT FUNCTION}", elementRef);
}

Yer {JAVASCRIPT FUNCTION} tutucu, işlev JS tanımlayıcısıdır.

GenericMethod doğrudan nesne üzerinde türüyle çağrılır. Aşağıdaki örnekte, ad alanı GenericMethod tarafından kullanılabilir olduğu varsay JsInteropClasses gelir:

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

Bileşenler arasında başvuru öğeleri

Bileşenleri ElementReference arasında geçirilene bir, çünkü:

Bir üst bileşenin öğe başvurularını diğer bileşenler için kullanılabilir hale göndermesi için, üst bileşen şunları olabilir:

  • Alt bileşenlerin geri çağırmaları kaydetmesine izin ver.
  • Geçirilen öğe başvurusuyla olay OnAfterRender sırasında kayıtlı geri çağırmaları çağırma. Dolaylı olarak, bu yaklaşım alt bileşenlerin üst öğe başvurusuyla etkileşim kurmasını sağlar.

( ) veya <head> wwwroot/index.html ( ) için aşağıdaki Blazor WebAssembly stili Pages/_Host.cshtml Blazor Server ekleyin:

<style>
    .red { color: red }
</style>

Kapanış etiketi ( ) veya </body> wwwroot/index.html ( içine aşağıdaki Blazor WebAssembly betiği Pages/_Host.cshtml Blazor Server ekleyin:

<script>
  function setElementClass(element, className) {
    var myElement = element;
    myElement.classList.add(className);
  }
</script>

Pages/CallJsExample7.razor (üst bileşen):

@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?" />

Pages/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)
            {
                try
                {
                    subscription.OnNext(title);
                }
                catch (Exception)
                {
                    throw;
                }
            }
        }

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

Önceki örnekte, uygulamanın ad alanı BlazorSample klasöründeki bileşenlerle Pages birliktedir. Kodu yerel olarak test ediyorsanız ad alanını güncelleştirin.

Shared/SurveyPrompt.razor (alt bileşen):

@inject IJSRuntime JS

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

Shared/SurveyPrompt.razor.cs:

using System;
using Microsoft.AspNetCore.Components;

namespace BlazorSample.Shared
{
    public partial class SurveyPrompt : 
        ComponentBase, IObserver<ElementReference>, IDisposable
    {
        private IDisposable subscription = null;

        [Parameter]
        public IObservable<ElementReference> Parent { 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();
        }
    }
}

Önceki örnekte, uygulamanın ad alanı klasöründe BlazorSample paylaşılan bileşenlere Shared sahiptir. Kodu yerel olarak test ediyorsanız ad alanını güncelleştirin.

JavaScript birlikte çalışma çağrılarını sağlamlaştırma

Bu bölüm öncelikli olarak uygulamalar için geçerlidir, ancak koşullar bunu garanti ediyorsa uygulamalar birlikte çalışma Blazor Server Blazor WebAssembly zaman aşımı da JS ayarlayabilen uygulamalardır.

Uygulamalarda Blazor Server JavaScript ( JS ) birlikte çalışma, ağ hataları nedeniyle başarısız olabilir ve güvenilir değil olarak kabul edilmelidir. Varsayılan olarak, Blazor Server uygulamalar birlikte çalışma çağrıları için bir dakikalık zaman aşımı JS kullanır. Bir uygulama daha agresif bir zaman aşımına tolere ediyorsa, aşağıdaki yaklaşımlardan birini kullanarak zaman aşımını ayarlayın.

ile yönteminde genel bir Startup.ConfigureServices zaman aşımı Startup.cs CircuitOptions.JSInteropDefaultCallTimeout ayarlayın:

services.AddServerSideBlazor(
    options => options.JSInteropDefaultCallTimeout = {TIMEOUT});

Yer {TIMEOUT} tutucu bir TimeSpan (örneğin, TimeSpan.FromSeconds(80) ).

Bileşen kodunda çağırma başına zaman aşımı ayarlayın. Belirtilen zaman aşımı, tarafından ayarlanmış genel zaman aşımını geçersiz JSInteropDefaultCallTimeout kılar:

var result = await JS.InvokeAsync<string>("{ID}", {TIMEOUT}, new[] { "Arg1" });

Yukarıdaki örnekte:

  • Yer {TIMEOUT} tutucu bir TimeSpan (örneğin, TimeSpan.FromSeconds(80) ).
  • Yer {ID} tutucu, çağrılan işlevin tanımlayıcısıdır. Örneğin, değeri someScope.someFunction işlevini window.someScope.someFunction çağırır.

Birlikte çalışma hatalarının yaygın bir nedeni uygulamalarda ağ hataları olsa da, uygulamalarda birlikte çalışma çağrıları için çağrı başına JS Blazor Server zaman aşımı JS Blazor WebAssembly ayarlandırabilirsiniz. Bir uygulamada SignalR bağlantı hattı mevcut olsa Blazor WebAssembly da, uygulamalarda geçerli olan diğer nedenlerle JS birlikte çalışma çağrıları başarısız Blazor WebAssembly olabilir.

Kaynak tükenmesi hakkında daha fazla bilgi için bkz. Güvenlik açıkları için tehdit azaltma ASP.NET Core Blazor Server .

Döngüsel nesne başvurularından kaçının

Döngüsel başvurular içeren nesneler istemcide şu iki için seri hale getirilemiyor:

  • .NET yöntem çağrıları.
  • Dönüş türünde döngüsel başvurular olduğunda JavaScript yöntemi C# çağrısında bulundu.

Kullanıcı arabirimini işleyici JavaScript kitaplıkları

Bazen tarayıcıda görünen kullanıcı arabirimi öğeleri üreten JavaScript ( JS ) kitaplıklarını (DOM) Belge Nesne Modeli kullanabilirsiniz. İlk bakışta bu zor görünebilir çünkü fark sistemi DOM öğelerinin ağacı üzerinde denetim sahibi olur ve bazı dış kod DOM ağacını geçersiz hale gelir ve farkları uygulama mekanizmasını geçersiz hale gelirse hatalara neden Blazor olur. Bu belirli bir Blazor sınırlama değildir. Aynı zorluk, fark tabanlı ui çerçeveleriyle de ortaya çıkar.

Neyse ki dışarıdan oluşturulan kullanıcı arabirimini bir bileşen kullanıcı arabirimine güvenilir bir şekilde Razor eklemek oldukça kolaydır. Önerilen teknik, bileşenin kodunun ( .razor dosyası) boş bir öğe üretmesidir. 'nin fark sistemi söz konusu olduğunda öğe her zaman boştur, bu nedenle işleyici öğeye özyinelemez ve bunun yerine içeriğini Blazor tek başına bırakır. Bu, öğenin harici olarak yönetilen rastgele içerikle doldurmak için güvenlidir.

Aşağıdaki örnekte kavramlar ve bilgiler yer almaktadır. olduğunda if deyiminin firstRender true içinde, birlikte çalışma unmanagedElement kullanmanın dışında Blazor ile JS etkileşime geçme. Örneğin, öğesini doldurmak JS için bir dış kitaplık çağırabilirsiniz. Blazor , bu bileşen kaldırılana kadar öğenin içeriğini tek başına bırakır. Bileşen kaldırıldığı zaman, bileşenin tüm DOM alt ağacı da kaldırılır.

<h1>Hello! This is a Blazor component rendered at @DateTime.Now</h1>

<div @ref="unmanagedElement"></div>

@code {
    private HtmlElement unmanagedElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            ...
        }
    }
}

Açık kaynaklı Mapbox API 'lerikullanarak etkileşimli bir harita işleyen aşağıdaki örneği göz önünde bulundurun.

Aşağıdaki JS Modül uygulamaya yerleştirildi veya bir sınıf kitaplığından kullanılabilir hale getirildi Razor .

Not

Mapbox haritasını oluşturmak Için, Mapbox oturum açma 'dan bir erişim belirteci alın ve {ACCESS TOKEN} aşağıdaki kodda göründüğü yere sağlayın.

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

Doğru stil oluşturmak için, ana bilgisayar HTML sayfasına aşağıdaki stil sayfası etiketini ekleyin.

wwwroot/index.html( Blazor WebAssembly ) Veya Pages/_Host.cshtml () içinde Blazor Server , aşağıdaki <link> öğeyi <head> öğe biçimlendirmesine ekleyin:

<link href="https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css" 
    rel="stylesheet" />

Pages/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)
        => await mapModule.InvokeVoidAsync("setMapCenter", mapInstance, latitude, 
            longitude).AsTask();

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (mapInstance is not null)
        {
            await mapInstance.DisposeAsync();
        }

        if (mapModule is not null)
        {
            await mapModule.DisposeAsync();
        }
    }
}

Yukarıdaki örnek, etkileşimli bir harita Kullanıcı arabirimi üretir. Kullanıcı:

  • , Kaydırmak veya yakınlaştırmak için sürükleyebilir.
  • Önceden tanımlanmış konumlara atlanacak düğmeleri seçin.

Esistol, Birleşik Krallık ve Tokyo, Japonya ' ı seçmek için bkz. Tokyo 'daki mapbox açık Haritası

Yukarıdaki örnekte:

  • , <div> İle ilişkili olduğu kadar @ref="mapElement" boş bırakılır Blazor . mapbox-gl.jsBetik, öğeyi güvenle doldurabilir ve içeriğini değiştirebilir. Bu tekniği JS Kullanıcı arabirimini işleyen herhangi bir kitaplıkla kullanın. Bir üçüncü taraf JS Spa çerçevesinden bileşenleri Blazor , sayfanın diğer bölümlerine ulaşmaya ve bunları değiştirmeye çalıştıklarında olduğu sürece ekleyebilirsiniz. Dış JS kodun Blazor boş olarak söz konusu olmayan öğeleri değiştirmesi güvenli değildir.
  • Bu yaklaşımı kullanırken, Blazor Dom öğelerinin nasıl koruduğunu veya yok olduğunu gösteren kuralları göz önünde bulundurun. Bileşen, düğme tıklama olayları ' nı güvenli bir şekilde işler ve var olan eşleme örneğini güncelleştirir, çünkü DOM öğeleri varsayılan olarak mümkün olduğunca korunur. Bir döngünün içindeki harita öğelerinin bir listesini işlerinizden @foreach , @key bileşen örneklerinin korunmasını sağlamak için kullanmak istersiniz. Aksi takdirde, liste verilerinde yapılan değişiklikler bileşen örneklerinin önceki örneklerin durumunu istenmeyen bir şekilde tutmasına neden olabilir. Daha fazla bilgi için bkz . @key öğeleri ve bileşenleri korumak için kullanma.
  • Örnek, JS BIR ES6 modülündeki mantığı ve bağımlılıkları kapsüller ve tanımlayıcıyı kullanarak modülü dinamik olarak yükler import . Daha fazla bilgi için bkz. JavaScript modüllerinde JavaScript yalıtımı.

JavaScript birlikte çalışma çağrılarında boyut sınırları

Bu bölüm yalnızca uygulamalar için Blazor Server geçerlidir. içinde, çerçeve JavaScript (JS) birlikte çalışma giriş Blazor WebAssembly ve çıkışlarının boyutuna bir sınır uygulamaz.

'de, JS birlikte çalışma çağrıları, hub yöntemleri için izin verilen en büyük gelen ileti boyutuyla sınırlıdır ve bu da Blazor Server SignalR tarafından HubOptions.MaximumReceiveMessageSize zorlanır (varsayılan olarak: 32 KB). JS'den SignalR .NET'e iletiler MaximumReceiveMessageSize hata verir. Çerçeve, hub'dan istemciye ileti SignalR boyutuna bir sınır dayatmaz.

Günlüğe SignalR kaydetme Hata Ayıklama veya İzleme olarak ayarlanmasa, yalnızca tarayıcının geliştirici araçları konsolunda ileti boyutu hatası görüntülenir:

Hata: Bağlantı şu hatayla kesildi: 'Hata: Sunucu kapatılırken bir hata döndürüldü: Bağlantı bir hatayla kapatıldı.'.

Sunucu SignalR tarafı günlüğe kaydetme Hata Ayıklama veya İzleme olarak ayarlanırsa,sunucu tarafı günlük kaydı ileti boyutu hatası için bir ortaya InvalidDataException çıkar.

appsettings.Development.json:

{
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.AspNetCore.SignalR": "Debug"
    }
  }
}

Hata:

System.IO.InvalidDataException: İleti boyutu üst sınırı 32768B aşıldı. İleti boyutu AddHubOptions içinde yalıtabilirsiniz.

içinde ayarını kullanarak sınırı MaximumReceiveMessageSize Startup.ConfigureServices artırabilirsiniz:

services.AddServerSideBlazor()
    .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);

Gelen ileti boyutu sınırının artırılması, daha fazla sunucu kaynağı gerektirmenin maliyetine neden olur ve kötü amaçlı bir kullanıcının artan risklerine SignalR karşı sunucuyu ortaya çıkarır. Buna ek olarak, bellekte dize veya byte dizileri olarak büyük miktarda içeriğin okunması atık toplayıcı ile kötü şekilde çalışarak ek performans cezalarına neden olan ayırmalara da neden olabilir.

Büyük yüklerin okunması için bir seçenek, içeriği daha küçük öbekler halinde göndermek ve yükü olarak Stream işlemektir. Bu, büyük JSON yüklerini okurken veya veriler JS'de ham bayt olarak kullanılabilir durumda olduğunda kullanılabilir. bileşenine benzer teknikler kullanan içinde büyük ikili yük göndermeyi gösteren bir örnek Blazor Server InputFile içinbkz. İkili Gönderme örnek uygulaması.

Not

Belge, ASP.NET Core kaynağı yüklemesi için ürün biriminin bir sonraki sürümü için geçerli geliştirmeyi temsil eden deponun dal main ASP.NET Core. Farklı bir sürümün dallarını seçmek için Dalları veya etiketleri değiştir açılan listesini kullanarak dalı seçin. Örneğin, ASP.NET Core release/5.0 5.0 sürümü için dalı seçin.

JS ile uygulamalar arasında büyük miktarda veri aktaran kod geliştirme konusunda aşağıdaki kılavuzu Blazor göz önünde Blazor Server bulundurabilirsiniz:

  • Verileri daha küçük parçalara dilimler ve tüm veriler sunucu tarafından alınana kadar veri kesimlerini sırayla gönderin.
  • JS ve C# kodunda büyük nesneleri ayırmayın.
  • Veri gönderirken veya alırken ana kullanıcı arabirimi iş parçacığını uzun süre engellemeyebilirsiniz.
  • İşlem tamamlandığında veya iptal edilirken tüketilen belleği serbest bırak.
  • Güvenlik amacıyla aşağıdaki ek gereksinimleri zorunlu kılın:
    • Geçir konan en büyük dosya veya veri boyutunu bildirebilirsiniz.
    • İstemciden sunucuya en düşük karşıya yükleme oranını bildirebilirsiniz.
  • Veriler sunucu tarafından alındıktan sonra şu veriler olabilir:
    • Tüm segmentler toplanana kadar geçici olarak bir bellek arabelleğinde depolanır.
    • Hemen tüketilir. Örneğin, veriler hemen bir veritabanında depolanmış veya diske yazıldığı için her kesim alınmıştır.

Unmarshalled JavaScript birlikte çalışabilirliği

Blazor WebAssembly .NET nesneleri JavaScript ( JS ) birlikte çalışması için serileştirildiğinde ve aşağıdakilerden biri doğru olduğunda, bileşenler düşük performansa sahip olabilir:

  • Yüksek hacimli .NET nesneleri hızla serileştirilir. Örneğin, JS bir fare tekerleği dönme gibi bir giriş cihazını taşıma sırasında birlikte çalışma çağrıları yapıldığında kötü performans ortaya çıkabilir.
  • Büyük .NET nesneleri veya çok sayıda .NET nesnesi birlikte çalışma için serileştirilmelidir JS . Örneğin, JS birlikte çalışma çağrıları onlarca dosya serileştirmesini gerektirdiğinde kötü performans oluşabilir.

IJSUnmarshalledObjectReferenceJSişlevleri .net verilerinin serileştirilmesinin bir yükü olmadan çağrılabilen bir nesne başvurusunu temsil eder.

Aşağıdaki örnekte:

  • Dize ve tamsayı içeren bir struct , serileştirilmemiş olarak geçirilir JS .
  • JS işlevler, verileri işler ve çağırana bir Boole ya da dize döndürür.
  • JSDize doğrudan bir .net nesnesine dönüştürülebilir değildir string . unmarshalledFunctionReturnStringİşlevi BINDING.js_string_to_mono_string bir dizenin dönüştürülmesini yönetmek için çağırır JS .

Not

Aşağıdaki örnekler, geçirilen Yapı JS kötü bileşen performansına neden olmadığı için bu senaryo için tipik kullanım durumları değildir. Örnek, yalnızca serileştirilmiş .NET verilerini geçirme kavramlarını göstermek için küçük bir nesne kullanır.

Aşağıdaki <script> bloğu wwwroot/index.html ( Blazor WebAssembly ) veya Pages/_Host.cshtml () içine yerleştirin Blazor Server . Alternatif olarak, öğesini JS JS ile kapatma etiketinin içinde başvurulan bir dış dosyaya yerleştirebilirsiniz; </body> <script src="{SCRIPT PATH AND FILE NAME (.js)}></script> burada {SCRIPT PATH AND FILE NAME (.js)} yer tutucu, betiğin yolu ve dosya adıdır.

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

Uyarı

js_string_to_mono_stringİşlevin adı, davranışı ve varlığı gelecek .NET sürümünde değişebilir. Örneğin:

  • İşlevin yeniden adlandırılması olasıdır.
  • İşlevin kendisi, çerçeve tarafından dizelerin otomatik dönüştürülmesini tercih ederek kaldırılabilir.

Pages/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;
    }
}

IJSUnmarshalledObjectReferenceÖrnek, C# kodunda atılmazsa, ' de atılabilir JS . Aşağıdaki dispose işlev, öğesinden çağrıldığında nesne başvurusunu ortadan kaldırmaktadır JS :

window.exampleJSObjectReferenceNotDisposedInCSharp = () => {
  return {
    dispose: function () {
      DotNet.disposeJSObjectReference(this);
    },

    ...
  };
}

Dizi türleri JS , kullanarak nesnelerden .net nesnelerine dönüştürülebilir js_typed_array_to_array , ancak JS dizi türü belirlenmiş bir dizi olmalıdır. İçindeki diziler JS , C# kodunda bir .NET nesne dizisi () olarak okunabilir object[] .

Dize dizileri gibi diğer veri türleri dönüştürülebilirler, ancak yeni bir mono dizi nesnesi ( mono_obj_array_new ) oluşturulmasını ve değerini () ayarlamayı gerektirebilir mono_obj_array_set .

Uyarı

JS ,, Blazor ve gibi Framework tarafından sunulan işlevler js_typed_array_to_array mono_obj_array_new mono_obj_array_set ad değişikliklerine, davranış değişikliklerine tabidir veya gelecek .net sürümlerinde kaldırıllardır.

JavaScript özel durumlarını yakala

JSÖzel durumları yakalamak için JS birlikte çalışabilirliği bir try - catch blokta sarın ve yakalar JSException .

Aşağıdaki örnekte, nonFunction JS işlevi yok. İşlev bulunamadığında, JSException aşağıdaki hatayı gösteren bir ile birlikte kaydedilir Message :

Could not find 'nonFunction' ('nonFunction' was undefined).

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

Ek kaynaklar

Bu makalede, .NET 'teki JavaScript () işlevlerini çağırma ele alınmaktadır JS . .NET yöntemlerinin ' den nasıl çağrılacağını öğrenmek için JS bkz ASP.NET Core içindeki JavaScript işlevlerinden .NET yöntemlerini çağırın Blazor ..

JS.Net 'ten çağırmak için, IJSRuntime soyutlamayı ekleyin ve aşağıdaki yöntemlerden birini çağırın:

İşlevleri çağıran önceki .NET yöntemleri için JS :

  • İşlev tanımlayıcısı ( String ) genel kapsama ( window ) göredir. Çağırmak için window.someScope.someFunction tanımlayıcı olur someScope.someFunction . Çağrılmadan önce işlevi kaydetmeniz gerekmez.
  • İçindeki herhangi bir sayıda JS seri hale getirilebilir bağımsız değişkeni Object[] bir JS işleve geçirin.
  • İptal belirteci ( CancellationToken ), işlemlerin iptal edilmesi gerektiğini belirten bir bildirim yayar.
  • TimeSpan bir işlem için zaman sınırını temsil eder JS .
  • TValueDönüş türü de JS seri hale getirilebilir üzerinde olmalıdır. TValue döndürülen türü ile en iyi eşleşen .NET türüyle eşleşmelidir JS .
  • Yöntemler JS Promise için bir döndürülür InvokeAsync . InvokeAsync ' nin sarmalanmış olduğunu kaldırır Promise ve tarafından beklemiş değeri döndürür Promise .

Blazor ServerPrerendering etkin olan uygulamalar için, JS ilk prerendering sırasında çağırma yapılamaz. JS birlikte çalışma çağrılarının, tarayıcıyla bağlantı kurulana kadar ertelenmesi gerekir. Daha fazla bilgi için bkz. bir uygulamanın ne zaman Blazor Server prerendering bölümüne bakın.

Aşağıdaki örnek TextDecoder , tabanlı bir JS kod çözücüsüne dayalıdır. Örnek, JS Geliştirici kodundan mevcut bir API 'ye bir gereksinimi boşaltan bir C# yönteminden bir işlevin nasıl çağrılacağını gösterir JS . JSİşlevi bir C# yönteminden bir bayt dizisi kabul eder, dizinin kodunu çözer ve görüntülenecek metni bileşene döndürür.

Aşağıdaki JS kodu </body> wwwroot/index.html ( Blazor WebAssembly ) veya Pages/_Host.cshtml () kapanış etiketinin içine ekleyin Blazor Server :

<script>
  window.convertArray = (win1251Array) => {
    var win1251decoder = new TextDecoder('windows-1251');
    var bytes = new Uint8Array(win1251Array);
    var decodedArray = win1251decoder.decode(bytes);
    console.log(decodedArray);
    return decodedArray;
  };
</script>

Aşağıdaki CallJsExample1 bileşen:

  • convertArray JS InvokeAsync Düğme () seçilirken işlevi çağırır Convert Array .
  • JSİşlev çağrıldıktan sonra, geçirilen dizi bir dizeye dönüştürülür. Dize, Display () için bileşene döndürülür text .

Pages/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 &copy;2005 <a href="https://www.uphe.com">Universal Pictures</a>: 
    <a href="https://www.uphe.com/movies/serenity">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));
    }
}

Döndürülen bir değeri okumadan JavaScript işlevlerini çağırma ( InvokeVoidAsync )

Şu InvokeVoidAsync durumlarda kullanın:

  • Bir çağrının sonucunu okumak için .NET gerekli değildir JS .
  • JS işlevler void (0)/void 0 veya tanımsızdöndürür.

</body> wwwroot/index.html ( Blazor WebAssembly ) Veya () kapanış etiketinin içinde Pages/_Host.cshtml Blazor Server bir displayTickerAlert1 JS işlev sağlayın. İşlevi ile çağrılır InvokeVoidAsync ve bir değer döndürmez:

<script>
  window.displayTickerAlert1 = (symbol, price) => {
    alert(`${symbol}: $${price}!`);
  };
</script>

Component ( .razor ) örneği ( InvokeVoidAsync )

TickerChanged``handleTickerChanged1aşağıdaki bileşendeki yöntemini çağırır CallJsExample2 .

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

Class ( .cs ) örneği ( InvokeVoidAsync )

JsInteropClasses1.cs:

using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses1
{
    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``handleTickerChanged1aşağıdaki bileşendeki yöntemini çağırır CallJsExample3 .

Pages/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 işlevlerini çağırın ve döndürülen bir değeri ( InvokeAsync ) okuyun

InvokeAsync.Net 'in bir çağrının sonucunu okuması gerektiğinde kullanın JS .

</body> wwwroot/index.html ( Blazor WebAssembly ) Veya () kapanış etiketinin içinde Pages/_Host.cshtml Blazor Server bir displayTickerAlert2 JS işlev sağlayın. Aşağıdaki örnek, çağıran tarafından görüntülenmek üzere bir dize döndürür:

<script>
  window.displayTickerAlert2 = (symbol, price) => {
    if (price < 20) {
      alert(`${symbol}: $${price}!`);
      return "User alerted in the browser.";
    } else {
      return "User NOT alerted.";
    }
  };
</script>

Component ( .razor ) örneği ( InvokeAsync )

TickerChanged yöntemini çağırır handleTickerChanged2 ve döndürülen dizeyi aşağıdaki CallJsExample4 bileşende görüntüler.

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

Class ( .cs ) örneği ( InvokeAsync )

JsInteropClasses2.cs:

using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses2
{
    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 yöntemini çağırır handleTickerChanged2 ve döndürülen dizeyi aşağıdaki CallJsExample5 bileşende görüntüler.

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

Dinamik içerik oluşturma senaryoları

BuildRenderTreeile dinamik içerik oluşturma için özniteliğini [Inject] kullanın:

[Inject]
IJSRuntime JS { get; set; }

Bir Blazor Server uygulamanın ne zaman önceden işleyici olduğunu algılama

Bu bölüm Blazor Server , ve için Blazor WebAssembly PreRender bileşenleri tarafından barındırılan uygulamalar için geçerlidir Razor . Prerendering, içinde ele alınmıştır ASP.NET Core bileşenlerini önceden ASP.NET Core Razor ve tümleştirin .

Bir uygulama prerendering olduğunda, JavaScript 'e çağırma gibi bazı eylemler mümkün değildir. Bileşenler, ön işlenmiş olduğunda farklı şekilde işlenmesi gerekebilir.

Aşağıdaki örnekte, setElementText1 işlevi <head> wwwroot/index.html ( Blazor WebAssembly ) veya Pages/_Layout.cshtml () öğesinin içine yerleştirilir Blazor Server . İşlevi ile çağrılır JSRuntimeExtensions.InvokeVoidAsync ve bir değer döndürmez:

<script>
  window.setElementText1 = (element, text) => element.innerText = text;
</script>

Uyarı

Yukarıdaki örnek yalnızca tanıtım amacıyla Belge Nesne Modeli (DOM) değiştirir. JavaScript ile doğrudan JavaScript 'in değiştirilmesi, JavaScript 'in değişiklik izlemesini kesintiye uğradığı için çoğu senaryoda önerilmez Blazor . Daha fazla bilgi için bkz. Blazor JavaScript birlikte çalışabilirliği JS (birlikte çalışma).

Bu tür çağrıların çalışacağı bir nokta kadar JavaScript birlikte çalışma çağrılarını geciktirmek için OnAfterRender{Async} yaşam döngüsü olayınıgeçersiz kılın. Bu olay yalnızca uygulama tamamen işlendikten sonra çağırılır.

Pages/PrerenderedInterop1.razor:

@page "/prerendered-interop-1"
@using Microsoft.JSInterop
@inject IJSRuntime JS

<div @ref="divElement">Text during render</div>

@code {
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JS.InvokeVoidAsync(
                "setElementText1", divElement, "Text after render");
        }
    }
}

Not

Önceki örnek, istemciyi genel yöntemlerle pollutes. Üretim uygulamalarında daha iyi bir yaklaşım için bkz. JavaScript modüllerinde JavaScript yalıtımı.

Örnek:

export setElementText1 = (element, text) => element.innerText = text;

Aşağıdaki bileşen, prerendering ile uyumlu bir şekilde bileşenin başlatma mantığının bir parçası olarak JavaScript birlikte çalışabilirinin nasıl kullanılacağını göstermektedir. Bileşen içinden bir işleme güncelleştirmesi tetiklemenin mümkün olduğunu gösterir OnAfterRenderAsync . Bu senaryoda bir sonsuz döngü oluşturmaktan kaçınmak için geliştirici dikkatli olmalıdır.

Aşağıdaki örnekte, setElementText2 işlevi <head> wwwroot/index.html ( Blazor WebAssembly ) veya Pages/_Layout.cshtml () öğesinin içine yerleştirilir Blazor Server . İşlevi ile çağrılır IJSRuntime.InvokeAsync ve bir değer döndürür:

<script>
  window.setElementText2 = (element, text) => {
    element.innerText = text;
    return text;
  };
</script>

Uyarı

Yukarıdaki örnek yalnızca tanıtım amacıyla Belge Nesne Modeli (DOM) değiştirir. JavaScript ile doğrudan JavaScript 'in değiştirilmesi, JavaScript 'in değişiklik izlemesini kesintiye uğradığı için çoğu senaryoda önerilmez Blazor . Daha fazla bilgi için bkz. Blazor JavaScript birlikte çalışabilirliği JS (birlikte çalışma).

Burada JSRuntime.InvokeAsync çağrılır, ElementReference OnAfterRenderAsync bileşen Işlenene kadar JavaScript öğesi olmadığından, yalnızca ' de kullanılır.

StateHasChanged JavaScript birlikte çalışma çağrısından alınan yeni durumla birlikte bileşeni yeniden yüklemek için çağrılır (daha fazla bilgi için bkz BlazorASP.NET Core bileşen işleme .). Kod sonsuz döngü oluşturmaz çünkü StateHasChanged yalnızca olduğu zaman çağrılır data null .

Pages/PrerenderedInterop2.razor:

@page "/prerendered-interop-2"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS

<p>
    Get value via JS interop call:
    <strong id="val-get-by-interop">@(data ?? "No value yet")</strong>
</p>

<p>
    Set value via JS interop call:
</p>

<div id="val-set-by-interop" @ref="divElement"></div>

@code {
    private string? data;
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && data == null)
        {
            data = await JS.InvokeAsync<string>(
                "setElementText2", divElement, "Hello from interop call!");

            StateHasChanged();
        }
    }
}

Not

Önceki örnek, istemciyi genel yöntemlerle pollutes. Üretim uygulamalarında daha iyi bir yaklaşım için bkz. JavaScript modüllerinde JavaScript yalıtımı.

Örnek:

export setElementText2 = (element, text) => {
  element.innerText = text;
  return text;
};

JavaScipt'nin konumu

JSJavaScript ( ) birlikte çalışabilirlik (birlikte çalışma) genel bakış makalesinde açıklanan yaklaşımlardan birini kullanarak JavaScript JS ( ) kodunu yükleme:

Uyarı

Etiket dinamik olarak güncelleştirilenene kadar bir bileşen dosyasına <script> ( .razor ) etiket <script> ekleyemezsiniz.

Öğelere başvuru yakalama

Bazı JavaScript ( JS ) birlikte çalışma senaryoları, HTML öğelerine başvuru gerektirir. Örneğin, kullanıcı arabirimi kitaplığı başlatma için öğe başvurusu gerektirebilir veya veya gibi bir öğe üzerinde komut gibi API'leri çağırmanız click play gerekir.

Aşağıdaki yaklaşımı kullanarak bir bileşendeki HTML öğelerine başvuruları yakalama:

  • HTML @ref öğesine bir öznitelik ekleyin.
  • Adı özniteliğin ElementReference değeriyle eşleşen türde bir alan @ref tanımlayın.

Aşağıdaki örnek, öğesine bir başvuru yakalamayı username <input> gösterir:

<input @ref="username" ... />

@code {
    private ElementReference username;
}

Uyarı

Yalnızca ile etkileşim kurmamış boş bir öğenin içeriğinin mutadi için bir öğe başvurusu Blazor kullanın. Bu senaryo, üçüncü taraf BIR API öğeye içerik sağlarken yararlıdır. öğesiyle etkileşim kurmaz, çünkü öğesinin gösterimi ile öğenin gösterimi Blazor (DOM) arasında çakışma Blazor Belge Nesne Modeli yoktur.

Aşağıdaki örnekte, bu öğenin liste öğelerini ( ) nesneden doldurmak için DOM ile etkileşim kurduğundan sıralanmamış listenin içeriğini ( ) değiştirme ul Blazor <li> Todos tehlikelidir:

<ul @ref="MyList">
    @foreach (var item in Todos)
    {
        <li>@item.Text</li>
    }
</ul>

Birlikte çalışma, öğenin içeriğini değiştirmesi ve öğeye farkları uygulama denemesi olursa, farklar JS MyList Blazor DOM ile eşleşmez.

Daha fazla bilgi için bkz. Blazor JavaScript birlikte çalışabilirliği JS (birlikte çalışma).

birlikte ElementReference çalışma yoluyla JS koda JS geçirildi. Kod, JS HTMLElement normal DOM API'leriyle birlikte kullanabileceği bir örnek alır. Örneğin, aşağıdaki kod bir öğeye fare tıklaması gönderilmesini sağlayan bir .NET uzantı yöntemi ( TriggerClickEvent ) tanımlar.

işlevi, JS clickElement geçirilen HTML click öğesinde ( ) bir olay element oluşturur:

window.interopFunctions = {
  clickElement : function (element) {
    element.click();
  }
}

Değer JS kullanmayan bir işlevi çağırmak için JSRuntimeExtensions.InvokeVoidAsync kullanın. Aşağıdaki kod, yakalanan ile önceki işlevi click çağırarak bir istemci JS tarafı olayı ElementReference tetikler:

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

Uzantı yöntemini kullanmak için örneği alan statik bir uzantı yöntemi IJSRuntime oluşturun:

public static async Task TriggerClickEvent(this ElementReference elementRef, 
    IJSRuntime js)
{
    await js.InvokeVoidAsync("interopFunctions.clickElement", elementRef);
}

yöntemi clickElement doğrudan nesne üzerinde çağrılır. Aşağıdaki örnekte, yöntemin ad TriggerClickEvent alanıyla kullanılabilir olduğu varsay JsInteropClasses gelir:

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

Önemli

Değişkeni exampleButton yalnızca bileşen işdikten sonra doldurulur. Koda ElementReference doldurulmamış bir JS geçiri varsa, JS kod değerini null alır. Bileşen işlemeyi bitirdikten sonra öğe başvurularını işlemek için veya bileşen OnAfterRenderAsync yaşam döngüsü yöntemlerini OnAfterRender kullanın.

Genel türlerle çalışırken ve bir değer döndürerek ValueTask<TResult> kullanın:

public static ValueTask<T> GenericMethod<T>(this ElementReference elementRef, 
    IJSRuntime js)
{
    return js.InvokeAsync<T>("{JAVASCRIPT FUNCTION}", elementRef);
}

Yer {JAVASCRIPT FUNCTION} tutucu, işlev JS tanımlayıcısıdır.

GenericMethod doğrudan nesne üzerinde türüyle çağrılır. Aşağıdaki örnekte, ad alanı GenericMethod tarafından kullanılabilir olduğu varsay JsInteropClasses gelir:

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

Bileşenler arasında başvuru öğeleri

Bileşenleri ElementReference arasında geçirilene bir, çünkü:

Bir üst bileşenin öğe başvurularını diğer bileşenler için kullanılabilir hale göndermesi için, üst bileşen şunları olabilir:

  • Alt bileşenlerin geri çağırmaları kaydetmesine izin ver.
  • Geçirilen öğe başvurusuyla olay OnAfterRender sırasında kayıtlı geri çağırmaları çağırma. Dolaylı olarak, bu yaklaşım alt bileşenlerin üst öğe başvurusuyla etkileşim kurmasını sağlar.

( ) veya <head> wwwroot/index.html ( ) için aşağıdaki Blazor WebAssembly stili Pages/_Host.cshtml Blazor Server ekleyin:

<style>
    .red { color: red }
</style>

Kapanış etiketi ( ) veya </body> wwwroot/index.html ( içine aşağıdaki Blazor WebAssembly betiği Pages/_Host.cshtml Blazor Server ekleyin:

<script>
  function setElementClass(element, className) {
    var myElement = element;
    myElement.classList.add(className);
  }
</script>

Pages/CallJsExample7.razor (üst bileşen):

@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?" />

Pages/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)
            {
                try
                {
                    subscription.OnNext(title);
                }
                catch (Exception)
                {
                    throw;
                }
            }
        }

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

Önceki örnekte, uygulamanın ad alanı BlazorSample klasöründeki bileşenlerle Pages birliktedir. Kodu yerel olarak test ediyorsanız ad alanını güncelleştirin.

Shared/SurveyPrompt.razor (alt bileşen):

@inject IJSRuntime JS

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

Shared/SurveyPrompt.razor.cs:

using System;
using Microsoft.AspNetCore.Components;

namespace BlazorSample.Shared
{
    public partial class SurveyPrompt : 
        ComponentBase, IObserver<ElementReference>, IDisposable
    {
        private IDisposable subscription = null;

        [Parameter]
        public IObservable<ElementReference> Parent { 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();
        }
    }
}

Önceki örnekte, uygulamanın ad alanı klasöründe BlazorSample paylaşılan bileşenlere Shared sahiptir. Kodu yerel olarak test ediyorsanız ad alanını güncelleştirin.

JavaScript birlikte çalışma çağrılarını sağlamlaştırma

Bu bölüm öncelikli olarak uygulamalar için geçerlidir, ancak koşullar bunu garanti ediyorsa uygulamalar birlikte Blazor Server çalışma zaman aşımı da Blazor WebAssembly JS ayarlayabilen uygulamalardır.

Uygulamalarda Blazor Server JavaScript ( JS ) birlikte çalışma, ağ hataları nedeniyle başarısız olabilir ve güvenilir değil olarak kabul edilmelidir. Varsayılan olarak, Blazor Server uygulamalar birlikte çalışma çağrıları için bir dakikalık zaman aşımı JS kullanır. Bir uygulama daha agresif bir zaman aşımına tolere ediyorsa, aşağıdaki yaklaşımlardan birini kullanarak zaman aşımını ayarlayın.

ile yönteminde genel bir Startup.ConfigureServices zaman aşımı Startup.cs CircuitOptions.JSInteropDefaultCallTimeout ayarlayın:

services.AddServerSideBlazor(
    options => options.JSInteropDefaultCallTimeout = {TIMEOUT});

Yer {TIMEOUT} tutucu bir TimeSpan (örneğin, TimeSpan.FromSeconds(80) ).

Bileşen kodunda çağırma başına zaman aşımı ayarlayın. Belirtilen zaman aşımı, tarafından ayarlanmış genel zaman aşımını geçersiz JSInteropDefaultCallTimeout kılar:

var result = await JS.InvokeAsync<string>("{ID}", {TIMEOUT}, new[] { "Arg1" });

Yukarıdaki örnekte:

  • Yer {TIMEOUT} tutucu bir TimeSpan (örneğin, TimeSpan.FromSeconds(80) ).
  • Yer {ID} tutucu, çağrılan işlevin tanımlayıcısıdır. Örneğin, değeri someScope.someFunction işlevini window.someScope.someFunction çağırır.

Birlikte çalışma hatalarının yaygın bir nedeni uygulamalarda ağ hataları olsa da, uygulamalarda birlikte çalışma çağrıları için çağrı başına JS Blazor Server zaman aşımı JS Blazor WebAssembly ayarlandırabilirsiniz. Bir uygulamada SignalR bağlantı hattı mevcut olsa Blazor WebAssembly da, uygulamalarda geçerli olan diğer nedenlerle JS birlikte çalışma çağrıları başarısız Blazor WebAssembly olabilir.

Kaynak tükenmesi hakkında daha fazla bilgi için bkz. Güvenlik açıkları için tehdit azaltma ASP.NET Core Blazor Server .

Döngüsel nesne başvurularından kaçının

Döngüsel başvurular içeren nesneler istemcide şu iki için seri hale getirilemiyor:

  • .NET yöntem çağrıları.
  • Dönüş türünde döngüsel başvurular olduğunda JavaScript yöntemi C# çağrısında bulundu.

JavaScript birlikte çalışma çağrılarını boyut sınırları

Bu bölüm yalnızca uygulamalar için Blazor Server geçerlidir. içinde, çerçeve JavaScript (JS) birlikte çalışma giriş Blazor WebAssembly ve çıkışlarının boyutuna bir sınır uygulamaz.

'de, JS birlikte çalışma çağrıları, hub yöntemleri için izin verilen en büyük gelen ileti boyutuyla sınırlıdır ve bu da Blazor Server SignalR tarafından HubOptions.MaximumReceiveMessageSize zorlanır (varsayılan olarak: 32 KB). JS'den SignalR .NET'e iletiler MaximumReceiveMessageSize hata verir. Çerçeve, hub'dan istemciye ileti SignalR boyutuna bir sınır dayatmaz.

Günlüğe SignalR kaydetme Hata Ayıklama veya İzleme olarak ayarlanmasa, yalnızca tarayıcının geliştirici araçları konsolunda ileti boyutu hatası görüntülenir:

Hata: Bağlantı şu hatayla kesildi: 'Hata: Sunucu kapatılırken bir hata döndürüldü: Bağlantı bir hatayla kapatıldı.'.

Sunucu SignalR tarafı günlüğe kaydetme Hata Ayıklama veya İzleme olarak ayarlanırsa,sunucu tarafı günlük kaydı ileti boyutu hatası için bir ortaya InvalidDataException çıkar.

appsettings.Development.json:

{
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.AspNetCore.SignalR": "Debug"
    }
  }
}

Hata:

System.IO.InvalidDataException: İleti boyutu üst sınırı 32768B aşıldı. İleti boyutu AddHubOptions içinde yalıtabilirsiniz.

içinde ayarını kullanarak sınırı MaximumReceiveMessageSize Startup.ConfigureServices artırabilirsiniz:

services.AddServerSideBlazor()
    .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);

Gelen ileti boyutu sınırının artırılması, daha fazla sunucu kaynağı gerektirmenin maliyetine neden olur ve kötü amaçlı bir kullanıcının artan risklerine SignalR karşı sunucuyu ortaya çıkarır. Buna ek olarak, bellekte dize veya byte dizileri olarak büyük miktarda içeriğin okunması atık toplayıcı ile kötü şekilde çalışarak ek performans cezalarına neden olan ayırmalara da neden olabilir.

Büyük yüklerin okunması için bir seçenek, içeriği daha küçük öbekler halinde göndermek ve yükü olarak Stream işlemektir. Bu, büyük JSON yüklerini okurken veya veriler JS'de ham bayt olarak kullanılabilir durumda olduğunda kullanılabilir. bileşenine benzer teknikler kullanan içinde büyük ikili yük göndermeyi gösteren bir örnek Blazor Server InputFile içinbkz. İkili Gönderme örnek uygulaması.

Not

Belge, ASP.NET Core kaynağı yüklemesi için ürün biriminin bir sonraki sürümü için geçerli geliştirmeyi temsil eden deponun dal main ASP.NET Core. Farklı bir sürümün dallarını seçmek için Dalları veya etiketleri değiştir açılan listesini kullanarak dalı seçin. Örneğin, ASP.NET Core release/5.0 5.0 sürümü için dalı seçin.

JS ile uygulamalar arasında büyük miktarda veri aktaran kod geliştirme konusunda aşağıdaki kılavuzu Blazor göz önünde Blazor Server bulundurabilirsiniz:

  • Verileri daha küçük parçalara dilimler ve tüm veriler sunucu tarafından alınana kadar veri kesimlerini sırayla gönderin.
  • JS ve C# kodunda büyük nesneleri ayırmayın.
  • Veri gönderirken veya alırken ana kullanıcı arabirimi iş parçacığını uzun süre engellemeyebilirsiniz.
  • İşlem tamamlandığında veya iptal edilirken tüketilen belleği serbest bırak.
  • Güvenlik amacıyla aşağıdaki ek gereksinimleri zorunlu kılın:
    • Geçir konan en büyük dosya veya veri boyutunu bildirebilirsiniz.
    • İstemciden sunucuya en düşük karşıya yükleme oranını bildirin.
  • Veriler sunucu tarafından alındıktan sonra şu veriler olabilir:
    • Tüm segmentler toplanana kadar geçici olarak bir bellek arabelleğinde depolanır.
    • Hemen tüketilir. Örneğin, veriler hemen bir veritabanında depolanmış veya diske yazıldığı için her kesim alınmıştır.

JavaScript özel durumlarını yakalama

Özel durumları JS yakalamak için birlikte çalışabilirliği bir JS blokta try - catch sarmalar ve bir JSException yakalar.

Aşağıdaki örnekte işlevi nonFunction JS yoktur. İşlev bulunamasa, aşağıdaki JSException hatayı gösteren bir ile birlikte Message kısılmış olur:

Could not find 'nonFunction' ('nonFunction' was undefined).

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

Ek kaynaklar