ASP.NET Core Blazor JavaScript mit statischem serverseitigen Rendering (statisches SSR)
Hinweis
Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
Wichtig
Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.
Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
In diesem Artikel wird erläutert, wie JavaScript (JS) in einer Blazor Web App mit statischem serverseitigen Rendering (statisches SSR) und erweiterter Navigation geladen wird.
Einige Anwendungen hängen von JS ab, um Initialisierungsaufgaben auszuführen, die für jede Seite spezifisch sind. Wenn Sie die erweiterte Navigationsfunktion Blazor verwenden, die es dem Benutzer ermöglicht, das Neuladen der gesamten Seite zu vermeiden, wird die seitenbezogene Funktion JS möglicherweise nicht wie erwartet jedes Mal erneut ausgeführt, wenn eine erweiterte Seitennavigation erfolgt.
Um dieses Problem zu vermeiden, empfiehlt es sich nicht, auf seitenspezifische <script>
-Elemente zu vertrauen, die außerhalb der Layout-Datei platziert wurden, die auf die Komponente angewendet wurde. Stattdessen sollten Skripts einen afterWebStarted
JSInitialisierer registrieren, um Initialisierungslogik auszuführen, und einen Ereignislistener verwenden, um auf Seitenaktualisierungen zu achten, die durch die erweiterte Navigation ausgelöst werden.
Ereignisse
In den folgenden Beispielen für Ereignislistener dient der Platzhalter {CALLBACK}
als Rückruffunktion.
Der Start der erweiterten Navigations (
enhancednavigationstart
) löst einen Rückruf aus, bevor eine erweiterte Navigation stattfindet:blazor.addEventListener("enhancednavigationstart", {CALLBACK});
Das Ende der erweiterten Navigation (
enhancednavigationend
) löst einen Rückruf aus, nachdem eine erweiterte Navigation stattfindet:blazor.addEventListener("enhancednavigationend", {CALLBACK});
Das Laden der Seite für die erweiterte Navigation (
enhancedload
) löst jedes Mal einen Rückruf aus, wenn die Seite aufgrund einer erweiterten Navigation aktualisiert wird, einschließlich Streamingupdates:blazor.addEventListener("enhancedload", {CALLBACK});
Um dieses Problem zu vermeiden, empfiehlt es sich nicht, auf seitenspezifische <script>
-Elemente zu vertrauen, die außerhalb der Layout-Datei platziert wurden, die auf die Komponente angewendet wurde. Stattdessen sollten Skripts einen afterWebStarted
JSInitialisierer registrieren, um Initialisierungslogik auszuführen, und einen Ereignislistener verwenden, um auf Seitenaktualisierungen zu achten, die durch die erweiterte Navigation ausgelöst werden:
blazor.addEventListener("enhancedload", {CALLBACK});
Im vorherigen Beispiel entspricht der Platzhalter {CALLBACK}
der Rückruffunktion.
Beispiel für ein erweitertes Seitenladeskript
Im folgenden Beispiel wird eine Möglichkeit zum Konfigurieren von JS-Code veranschaulicht, der ausgeführt wird, wenn eine statisch gerenderte Seite mit erweiterter Navigation anfänglich geladen oder aktualisiert wird.
Das folgende PageWithScript
-Komponentenbeispiel ist eine Komponente in der App, für die Skripts mit statischem SSR und erweiterter Navigation ausgeführt werden müssen. Das folgende Komponentenbeispiel enthält eine PageScript
-Komponente aus einer Razor-Klassenbibliothek (RCL), die weiter unten in diesem Artikel zur Projektmappe hinzugefügt wird.
Components/Pages/PageWithScript.razor
:
@page "/page-with-script"
@using BlazorPageScript
<PageTitle>Enhanced Load Script Example</PageTitle>
<PageScript Src="./Components/Pages/PageWithScript.razor.js" />
Welcome to my page.
Fügen Sie in der Blazor Web App die folgende zusammengestellte JS-Datei hinzu:
onLoad
wird aufgerufen, wenn das Skript der Seite hinzugefügt wird.onUpdate
wird aufgerufen, wenn das Skript nach einer erweiterten Aktualisierung noch auf der Seite vorhanden ist.onDispose
wird aufgerufen, wenn das Skript nach einer erweiterten Aktualisierung von der Seite entfernt wird.
Components/Pages/PageWithScript.razor.js
:
export function onLoad() {
console.log('Loaded');
}
export function onUpdate() {
console.log('Updated');
}
export function onDispose() {
console.log('Disposed');
}
Fügen Sie in einer Razor-Klassenbibliothek (RCL) (das Beispiel-RCL heißt BlazorPageScript
) das folgende Modul hinzu, das ein JS-Initialisiererist.
wwwroot/BlazorPageScript.lib.module.js
:
const pageScriptInfoBySrc = new Map();
function registerPageScriptElement(src) {
if (!src) {
throw new Error('Must provide a non-empty value for the "src" attribute.');
}
let pageScriptInfo = pageScriptInfoBySrc.get(src);
if (pageScriptInfo) {
pageScriptInfo.referenceCount++;
} else {
pageScriptInfo = { referenceCount: 1, module: null };
pageScriptInfoBySrc.set(src, pageScriptInfo);
initializePageScriptModule(src, pageScriptInfo);
}
}
function unregisterPageScriptElement(src) {
if (!src) {
return;
}
const pageScriptInfo = pageScriptInfoBySrc.get(src);
if (!pageScriptInfo) {
return;
}
pageScriptInfo.referenceCount--;
}
async function initializePageScriptModule(src, pageScriptInfo) {
if (src.startsWith("./")) {
src = new URL(src.substr(2), document.baseURI).toString();
}
const module = await import(src);
if (pageScriptInfo.referenceCount <= 0) {
return;
}
pageScriptInfo.module = module;
module.onLoad?.();
module.onUpdate?.();
}
function onEnhancedLoad() {
for (const [src, { module, referenceCount }] of pageScriptInfoBySrc) {
if (referenceCount <= 0) {
module?.onDispose?.();
pageScriptInfoBySrc.delete(src);
}
}
for (const { module } of pageScriptInfoBySrc.values()) {
module?.onUpdate?.();
}
}
export function afterWebStarted(blazor) {
customElements.define('page-script', class extends HTMLElement {
static observedAttributes = ['src'];
attributeChangedCallback(name, oldValue, newValue) {
if (name !== 'src') {
return;
}
this.src = newValue;
unregisterPageScriptElement(oldValue);
registerPageScriptElement(newValue);
}
disconnectedCallback() {
unregisterPageScriptElement(this.src);
}
});
blazor.addEventListener('enhancedload', onEnhancedLoad);
}
Fügen Sie kein <script>
-Tag zur Root-Komponente der App, typischerweise die App
-Komponente, für BlazorPageScript.lib.module.js
hinzu, da das Modul in diesem Fall ein JS-Initialisierer (afterWebStarted
) ist. JS Initialisierer werden vom Blazor Framework automatisch erkannt und geladen.
Fügen Sie in der RCL die folgende PageScript
-Komponente hinzu.
PageScript.razor
:
<page-script src="@Src"></page-script>
@code {
[Parameter]
[EditorRequired]
public string Src { get; set; } = default!;
}
Die PageScript
-Komponente funktioniert normalerweise auf der obersten Ebene einer Seite.
Wenn Sie die PageScript
-Komponente in ein App-Layout einfügen (z. B. MainLayout.razor
) und damit ein gemeinsam genutztes PageScript
für die Seiten erhalten, die das Layout verwenden, führt die Komponente onLoad
nur nach dem vollständigen erneuten Laden der Seite aus und onUpdate
, wenn eine erweiterte Seitenaktualisierung erfolgt (einschließlich erweiterter Navigation).
Wenn Sie dasselbe Modul auf verschiedenen Seiten wiederverwenden möchten, aber die Rückrufe onLoad
und onDispose
bei jeder Seitenänderung aufrufen, fügen Sie eine Abfragezeichenfolge an das Ende des Skripts an, damit es als ein anderes Modul erkannt wird. Sie können für eine App als Konvention festlegen, dass der Komponentenname als Abfragezeichenfolgenwert verwendet wird. Im folgenden Beispiel lautet die Abfragezeichenfolge „counter
“, da dieser PageScript
-Komponentenverweis in eine Counter
-Komponente eingefügt wurde. Dies ist lediglich ein Vorschlag, Sie können ein beliebiges Schema für Abfragezeichenfolgen verwenden.
<PageScript Src="./Components/Pages/PageWithScript.razor.js?counter" />
Verwenden Sie das MutationObserver
-Muster in JS auf dem Client, um Änderungen in bestimmten DOM-Elementen zu überwachen. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von Blazor in ASP.NET Core (JS-Interoperabilität).
Implementierung ohne Verwendung einer RCL
Der in diesem Artikel beschriebene Ansatz kann direkt in einem Blazor Web App implementiert werden, ohne eine Razor-Klassenbibliothek (RCL) zu verwenden, indem man die Assets der RCL in die App verschiebt. Die Verwendung einer RCL ist jedoch praktisch, weil die RCL in ein NuGet Paket gepackt werden kann, das von Blazor Apps in einer Organisation genutzt werden kann.
Wenn Sie die Assets in ein Blazor Web App verschieben, stellen Sie sicher, dass Sie das Modul (BlazorPageScript.lib.module.js
) so umbenennen, dass es zu der App gemäß den Regeln für die Benennung von JS Initialisierern passt. Wenn die Datei nicht ordnungsgemäß benannt wurde, kann Blazor das Modul nicht erkennen und laden, und das afterWebStarted
-Ereignis wird beim Starten der App nicht automatisch ausgeführt.