Omówienie aplikacji jednostronicowych (SPA) w usłudze ASP.NET Core
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Program Visual Studio udostępnia szablony projektów do tworzenia aplikacji jednostronicowych (SPA) opartych na strukturach Języka JavaScript, takich jak Angular, React i Vue , które mają zaplecze platformy ASP.NET Core. Te szablony:
- Utwórz rozwiązanie programu Visual Studio z projektem frontonu i projektem zaplecza.
- Użyj typu projektu programu Visual Studio dla języków JavaScript i TypeScript (esproj) dla frontonu.
- Użyj projektu ASP.NET Core dla zaplecza.
Projekty utworzone przy użyciu szablonów programu Visual Studio można uruchamiać z poziomu wiersza polecenia w systemach Windows, Linux i macOS. Aby uruchomić aplikację, użyj polecenia dotnet run --launch-profile https
, aby uruchomić projekt serwera. Uruchomienie projektu serwera powoduje automatyczne uruchomienie serwera programistycznego JavaScript frontonu. Profil uruchamiania https
jest obecnie wymagany.
Samouczki programu Visual Studio
Aby rozpocząć pracę, wykonaj jedną z samouczków w dokumentacji programu Visual Studio:
- Tworzenie aplikacji ASP.NET Core za pomocą platformy Angular
- Tworzenie aplikacji ASP.NET Core za pomocą platformy React
- Tworzenie aplikacji ASP.NET Core za pomocą programu Vue
Aby uzyskać więcej informacji, zobacz JavaScript i TypeScript w programie Visual Studio
szablony spa ASP.NET Core
Program Visual Studio zawiera szablony do tworzenia aplikacji ASP.NET Core z frontonem javaScript lub TypeScript. Te szablony są dostępne w programie Visual Studio 2022 w wersji 17.8 lub nowszej z zainstalowanym obciążeniem ASP.NET i tworzenie aplikacji internetowych.
Szablony programu Visual Studio do kompilowania aplikacji ASP.NET Core za pomocą frontonu JavaScript lub TypeScript oferują następujące korzyści:
- Czyszczenie separacji projektów dla frontonu i zaplecza.
- Bądź na bieżąco z najnowszymi wersjami platformy frontonu.
- Integracja z najnowszymi narzędziami wiersza polecenia platformy frontonu, takimi jak Vite.
- Szablony dla języków JavaScript i TypeScript (tylko TypeScript dla platformy Angular).
- Rozbudowane środowisko edytowania kodu w języku JavaScript i TypeScript.
- Integrowanie narzędzi kompilacji języka JavaScript z kompilacją platformy .NET.
- Interfejs użytkownika zarządzania zależnościami npm.
- Zgodne z konfiguracją debugowania i uruchamiania programu Visual Studio Code.
- Uruchamianie testów jednostkowych frontonu w Eksploratorze testów przy użyciu platform testowych JavaScript.
Starsze szablony spa ASP.NET Core
Starsze wersje zestawu .NET SDK zawierały starsze szablony do tworzenia aplikacji SPA za pomocą platformy ASP.NET Core. Aby uzyskać dokumentację dotyczącą tych starszych szablonów, zobacz artykuły ASP.NET Core 7.0 z omówieniem SPA oraz artykułami Angular i React.
Architektura szablonów aplikacji jednostronicowych
Szablony aplikacji jednostronicowej dla platformy Angular i React oferują możliwość tworzenia aplikacji Angular i React hostowanych na serwerze zaplecza platformy .NET.
W czasie publikowania pliki aplikacji Angular i React są kopiowane do wwwroot
folderu i są obsługiwane za pośrednictwem oprogramowania pośredniczącego plików statycznych.
Zamiast zwracać http 404 (Nie znaleziono), trasa rezerwowa obsługuje nieznane żądania do zaplecza i służy index.html
dla SPA.
Podczas programowania aplikacja jest skonfigurowana do używania serwera proxy frontonu. Platformy React i Angular używają tego samego serwera proxy frontonu.
Po uruchomieniu index.html
aplikacji strona zostanie otwarta w przeglądarce. Specjalne oprogramowanie pośredniczące, które jest włączone tylko w trakcie programowania:
- Przechwytuje żądania przychodzące.
- Sprawdza, czy serwer proxy jest uruchomiony.
- Przekierowuje do adresu URL serwera proxy, jeśli jest uruchomiony lub uruchamia nowe wystąpienie serwera proxy.
- Zwraca stronę do przeglądarki, która automatycznie odświeża się co kilka sekund, dopóki serwer proxy nie zostanie uruchomiony, a przeglądarka zostanie przekierowana.
Podstawową korzyścią zapewnia szablony spa ASP.NET Core:
- Uruchamia serwer proxy, jeśli jeszcze nie jest uruchomiony.
- Konfigurowanie protokołu HTTPS.
- Konfigurowanie niektórych żądań, które mają być proxied na serwerze zaplecza ASP.NET Core.
Gdy przeglądarka wysyła żądanie dla punktu końcowego zaplecza, na przykład /weatherforecast
w szablonach. Serwer proxy SPA odbiera żądanie i wysyła go z powrotem do serwera w sposób niewidoczny. Serwer odpowiada, a serwer proxy SPA wysyła żądanie z powrotem do przeglądarki:
Opublikowane aplikacje jednostronicowe
Po opublikowaniu aplikacji SPA staje się kolekcją plików w folderze wwwroot
.
Do obsługi aplikacji nie jest wymagany żaden składnik środowiska uruchomieniowego:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
app.MapFallbackToFile("index.html");
app.Run();
W poprzednim szablonie wygenerowany Program.cs
plik:
app.
UseStaticFiles umożliwia obsługiwanie plików.app.
MapFallbackToFile("index.html")
włącza obsługę dokumentu domyślnego dla dowolnego nieznanego żądania odbieranego przez serwer.
Po opublikowaniu aplikacji przy użyciu polecenia dotnet publish następujące zadania w csproj
pliku zapewniają, że npm restore
działa i że odpowiedni skrypt npm jest uruchamiany w celu wygenerowania artefaktów produkcyjnych:
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
</Target>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="$(SpaRoot)build\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>wwwroot\%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>
Tworzenie aplikacji jednostronicowych
Plik projektu definiuje kilka właściwości, które kontrolują zachowanie aplikacji podczas programowania:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>
<SpaRoot>ClientApp\</SpaRoot>
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
<SpaProxyServerUrl>https://localhost:44414</SpaProxyServerUrl>
<SpaProxyLaunchCommand>npm start</SpaProxyLaunchCommand>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="7.0.1" />
</ItemGroup>
<ItemGroup>
<!-- Don't publish the SPA source files, but do show them in the project files list -->
<Content Remove="$(SpaRoot)**" />
<None Remove="$(SpaRoot)**" />
<None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
</ItemGroup>
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
</Target>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="$(SpaRoot)build\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>wwwroot\%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>
SpaProxyServerUrl
: określa adres URL, pod którym serwer oczekuje uruchomienia serwera proxy SPA. Jest to adres URL:- Serwer pinguje po uruchomieniu serwera proxy, aby dowiedzieć się, czy jest gotowy.
- Gdzie przekierowuje przeglądarkę po pomyślnej odpowiedzi.
SpaProxyLaunchCommand
: Polecenie używane przez serwer do uruchamiania serwera proxy SPA, gdy wykryje, że serwer proxy nie jest uruchomiony.
Pakiet Microsoft.AspNetCore.SpaProxy
jest odpowiedzialny za poprzednią logikę wykrywania serwera proxy i przekierowywania przeglądarki.
Zestaw uruchamiania hostingu zdefiniowany w programie Properties/launchSettings.json
jest używany do automatycznego dodawania wymaganych składników podczas programowania niezbędnego do wykrywania, czy serwer proxy jest uruchomiony i uruchamia go w przeciwnym razie:
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:51783",
"sslPort": 44329
}
},
"profiles": {
"MyReact": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:7145;http://localhost:5273",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
}
}
}
}
Konfiguracja aplikacji klienckiej
Ta konfiguracja jest specyficzna dla struktury frontonu używanej przez aplikację, jednak wiele aspektów konfiguracji jest podobnych.
Konfiguracja platformy Angular
Wygenerowany ClientApp/package.json
plik szablonu:
{
"name": "myangular",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"prestart": "node aspnetcore-https",
"start": "run-script-os",
"start:windows": "ng serve --port 44483 --ssl --ssl-cert \"%APPDATA%\\ASP.NET\\https\\%npm_package_name%.pem\" --ssl-key \"%APPDATA%\\ASP.NET\\https\\%npm_package_name%.key\"",
"start:default": "ng serve --port 44483 --ssl --ssl-cert \"$HOME/.aspnet/https/${npm_package_name}.pem\" --ssl-key \"$HOME/.aspnet/https/${npm_package_name}.key\"",
"build": "ng build",
"build:ssr": "ng run MyAngular:server:dev",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "^14.1.3",
"@angular/common": "^14.1.3",
"@angular/compiler": "^14.1.3",
"@angular/core": "^14.1.3",
"@angular/forms": "^14.1.3",
"@angular/platform-browser": "^14.1.3",
"@angular/platform-browser-dynamic": "^14.1.3",
"@angular/platform-server": "^14.1.3",
"@angular/router": "^14.1.3",
"bootstrap": "^5.2.0",
"jquery": "^3.6.0",
"oidc-client": "^1.11.5",
"popper.js": "^1.16.0",
"run-script-os": "^1.1.6",
"rxjs": "~7.5.6",
"tslib": "^2.4.0",
"zone.js": "~0.11.8"
},
"devDependencies": {
"@angular-devkit/build-angular": "^14.1.3",
"@angular/cli": "^14.1.3",
"@angular/compiler-cli": "^14.1.3",
"@types/jasmine": "~4.3.0",
"@types/jasminewd2": "~2.0.10",
"@types/node": "^18.7.11",
"jasmine-core": "~4.3.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.1.1",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "^2.0.0",
"typescript": "~4.7.4"
},
"overrides": {
"autoprefixer": "10.4.5"
},
"optionalDependencies": {}
}
Zawiera skrypty uruchamiające serwer programistyczny angular:
Skrypt
prestart
wywołuje metodęClientApp/aspnetcore-https.js
, która jest odpowiedzialna za zapewnienie, że certyfikat HTTPS serwera deweloperów jest dostępny dla serwera proxy SPA.start:default
Istart:windows
:- Uruchom serwer programistyczny Angular za pomocą polecenia
ng serve
. - Podaj port, opcje użycia protokołu HTTPS oraz ścieżkę do certyfikatu i skojarzonego klucza. Numer portu podaj jest zgodny z numerem
.csproj
portu określonym w pliku.
- Uruchom serwer programistyczny Angular za pomocą polecenia
Wygenerowany ClientApp/angular.json
plik szablonu zawiera:
Polecenie
serve
.Element
proxyconfig
wdevelopment
konfiguracji wskazujący, żeproxy.conf.js
należy użyć go do skonfigurowania serwera proxy frontonu, jak pokazano w poniższym wyróżnionym formacie JSON:{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, "newProjectRoot": "projects", "projects": { "MyAngular": { "projectType": "application", "schematics": { "@schematics/angular:application": { "strict": true } }, "root": "", "sourceRoot": "src", "prefix": "app", "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "progress": false, "outputPath": "dist", "index": "src/index.html", "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", "allowedCommonJsDependencies": [ "oidc-client" ], "assets": [ "src/assets" ], "styles": [ "node_modules/bootstrap/dist/css/bootstrap.min.css", "src/styles.css" ], "scripts": [] }, "configurations": { "production": { "budgets": [ { "type": "initial", "maximumWarning": "500kb", "maximumError": "1mb" }, { "type": "anyComponentStyle", "maximumWarning": "2kb", "maximumError": "4kb" } ], "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" } ], "outputHashing": "all" }, "development": { "buildOptimizer": false, "optimization": false, "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true } }, "defaultConfiguration": "production" }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", "configurations": { "production": { "browserTarget": "MyAngular:build:production" }, "development": { "browserTarget": "MyAngular:build:development", "proxyConfig": "proxy.conf.js" } }, "defaultConfiguration": "development" }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { "browserTarget": "MyAngular:build" } }, "test": { "builder": "@angular-devkit/build-angular:karma", "options": { "main": "src/test.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.spec.json", "karmaConfig": "karma.conf.js", "assets": [ "src/assets" ], "styles": [ "src/styles.css" ], "scripts": [] } }, "server": { "builder": "@angular-devkit/build-angular:server", "options": { "outputPath": "dist-server", "main": "src/main.ts", "tsConfig": "tsconfig.server.json" }, "configurations": { "dev": { "optimization": true, "outputHashing": "all", "sourceMap": false, "namedChunks": false, "extractLicenses": true, "vendorChunk": true }, "production": { "optimization": true, "outputHashing": "all", "sourceMap": false, "namedChunks": false, "extractLicenses": true, "vendorChunk": false } } } } } }, "defaultProject": "MyAngular" }
ClientApp/proxy.conf.js
definiuje trasy, które muszą być proxied z powrotem do zaplecza serwera. Ogólny zestaw opcji jest definiowany w przypadku oprogramowania pośredniczącego http-proxy-react i angular, ponieważ oba używają tego samego serwera proxy.
Poniższy wyróżniony kod z logiki korzysta z ClientApp/proxy.conf.js
logiki opartej na zmiennych środowiskowych ustawionych podczas programowania w celu określenia portu, na którym działa zaplecze:
const { env } = require('process');
const target = env.ASPNETCORE_HTTPS_PORTS ? `https://localhost:${env.ASPNETCORE_HTTPS_PORTS}` :
env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'http://localhost:51951';
const PROXY_CONFIG = [
{
context: [
"/weatherforecast",
],
target: target,
secure: false,
headers: {
Connection: 'Keep-Alive'
}
}
]
module.exports = PROXY_CONFIG;
Konfiguracja platformy React
Sekcja
package.json
skryptów zawiera następujące skrypty uruchamiające aplikację react podczas programowania, jak pokazano w poniższym wyróżnionym kodzie:{ "name": "myreact", "version": "0.1.0", "private": true, "dependencies": { "bootstrap": "^5.2.0", "http-proxy-middleware": "^2.0.6", "jquery": "^3.6.0", "merge": "^2.1.1", "oidc-client": "^1.11.5", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-bootstrap": "^0.26.2", "react-router-dom": "^6.3.0", "react-scripts": "^5.0.1", "reactstrap": "^9.1.3", "rimraf": "^3.0.2", "web-vitals": "^2.1.4", "workbox-background-sync": "^6.5.4", "workbox-broadcast-update": "^6.5.4", "workbox-cacheable-response": "^6.5.4", "workbox-core": "^6.5.4", "workbox-expiration": "^6.5.4", "workbox-google-analytics": "^6.5.4", "workbox-navigation-preload": "^6.5.4", "workbox-precaching": "^6.5.4", "workbox-range-requests": "^6.5.4", "workbox-routing": "^6.5.4", "workbox-strategies": "^6.5.4", "workbox-streams": "^6.5.4" }, "devDependencies": { "ajv": "^8.11.0", "cross-env": "^7.0.3", "eslint": "^8.22.0", "eslint-config-react-app": "^7.0.1", "eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-react": "^7.30.1", "nan": "^2.16.0", "typescript": "^4.7.4" }, "overrides": { "autoprefixer": "10.4.5" }, "resolutions": { "css-what": "^5.0.1", "nth-check": "^3.0.1" }, "scripts": { "prestart": "node aspnetcore-https && node aspnetcore-react", "start": "rimraf ./build && react-scripts start", "build": "react-scripts build", "test": "cross-env CI=true react-scripts test --env=jsdom", "eject": "react-scripts eject", "lint": "eslint ./src/" }, "eslintConfig": { "extends": [ "react-app" ] }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } }
Skrypt
prestart
wywołuje następujące wywołania:aspnetcore-https.js
, który jest odpowiedzialny za zapewnienie, że certyfikat HTTPS serwera deweloperów jest dostępny dla serwera proxy SPA.aspnetcore-react.js
Wywołuje polecenie w celu skonfigurowania odpowiedniego.env.development.local
pliku w celu użycia lokalnego certyfikatu dewelopera HTTPS.aspnetcore-react.js
Konfiguruje lokalny certyfikat programowania HTTPS przez dodanieSSL_CRT_FILE=<certificate-path>
iSSL_KEY_FILE=<key-path>
do pliku.
Plik
.env.development
definiuje port serwera programistycznego i określa protokół HTTPS.
Konfiguruje src/setupProxy.js
serwer proxy SPA do przekazywania żądań do zaplecza. Ogólny zestaw opcji jest zdefiniowany w programie http-proxy-middleware.
Poniższy wyróżniony kod w programie ClientApp/src/setupProxy.js
używa logiki opartej na zmiennych środowiskowych ustawionych podczas programowania w celu określenia portu, na którym działa zaplecze:
const { createProxyMiddleware } = require('http-proxy-middleware');
const { env } = require('process');
const target = env.ASPNETCORE_HTTPS_PORTS ? `https://localhost:${env.ASPNETCORE_HTTPS_PORTS}` :
env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'http://localhost:51783';
const context = [
"/weatherforecast",
];
const onError = (err, req, resp, target) => {
console.error(`${err.message}`);
}
module.exports = function (app) {
const appProxy = createProxyMiddleware(context, {
target: target,
// Handle errors to prevent the proxy middleware from crashing when
// the ASP NET Core webserver is unavailable
onError: onError,
secure: false,
// Uncomment this line to add support for proxying websockets
//ws: true,
headers: {
Connection: 'Keep-Alive'
}
});
app.use(appProxy);
};
Obsługiwana wersja struktury SPA w szablonach spa ASP.NET Core
Szablony projektu SPA dostarczane z każdym ASP.NET Core odwołania do najnowszej wersji odpowiedniej struktury SPA.
Struktury SPA zwykle mają krótszy cykl wydawania niż .NET. Ze względu na dwa różne cykle wydania, obsługiwana wersja struktury SPA i .NET może wydostać się z synchronizacji: główna wersja struktury SPA, od których zależy główna wersja platformy .NET, może wyjść z pomocy technicznej, podczas gdy platforma SPA dostarczana z programem jest nadal obsługiwana.
Szablony ASP.NET Core SPA można zaktualizować w wydaniu poprawki do nowej wersji struktury SPA, aby zachować szablony w obsługiwanym i bezpiecznym stanie.