ASP.NET Core'da Tek Sayfalı Uygulamalar Oluşturmak için JavaScript Hizmetlerini Kullanma
Tarafından Fiyaz Hasan
Uyarı
Bu makalede açıklanan özellikler, ASP.NET Core 3.0 itibarıyla kullanımdan kaldırılmaktadır. Microsoft.AspNetCore.SpaServices.Extensions NuGet paketinde daha basit bir SPA çerçeveleri tümleştirme mekanizması sağlanır. Daha fazla bilgi için bkz . [Duyuru] Microsoft.AspNetCore.SpaServices ve Microsoft.AspNetCore.NodeServices'i kullanımdan kaldırma.
Tek Sayfalı Uygulama (SPA), doğası gereği zengin kullanıcı deneyimi nedeniyle popüler bir web uygulaması türüdür. Angular veya React gibi istemci tarafı SPA çerçevelerini veya kitaplıklarını ASP.NET Core gibi sunucu tarafı çerçevelerle tümleştirmek zor olabilir. JavaScript Hizmetleri, tümleştirme sürecindeki uyuşmaları azaltmak için geliştirilmiştir. Farklı istemci ve sunucu teknolojisi yığınları arasında sorunsuz işlem sağlar.
JavaScript Hizmetleri nedir?
JavaScript Hizmetleri, ASP.NET Core için istemci tarafı teknolojilerinden oluşan bir koleksiyondur. Amacı, ASP.NET Core'un SPA'lar oluşturmak için geliştiricilerin tercih ettiği sunucu tarafı platformu olarak konumlandırmaktır.
JavaScript Hizmetleri iki ayrı NuGet paketinden oluşur:
- Microsoft.AspNetCore.NodeServices (NodeServices)
- Microsoft.AspNetCore.SpaServices (SpaServices)
Bu paketler aşağıdaki senaryolarda kullanışlıdır:
- Sunucuda JavaScript çalıştırma
- SPA çerçevesi veya kitaplığı kullanma
- Webpack ile istemci tarafı varlıkları oluşturma
Bu makaledeki odağın büyük kısmı SpaServices paketini kullanmaya odaklanır.
SpaServices nedir?
SpaServices, ASP.NET Core'ı geliştiricilerin SPA'ları oluşturmak için tercih ettiği sunucu tarafı platformu olarak konumlandırmak için oluşturulmuştur. SpaServices'in ASP.NET Core ile SPA geliştirmesi gerekmez ve geliştiricileri belirli bir istemci çerçevesine kilitlemez.
SpaServices aşağıdakiler gibi kullanışlı bir altyapı sağlar:
- Sunucu tarafı prerendering
- Webpack Geliştirici Ara Yazılımı
- Sık Erişimli Modül Değiştirme
- Yönlendirme yardımcıları
Bu altyapı bileşenleri toplu olarak hem geliştirme iş akışını hem de çalışma zamanı deneyimini geliştirir. Bileşenler ayrı ayrı benimsenebilir.
SpaServices'i kullanma önkoşulları
SpaServices ile çalışmak için aşağıdakileri yükleyin:
Düğüm.js npm ile (sürüm 6 veya üzeri)
Bu bileşenlerin yüklendiğini ve bulunabildiğini doğrulamak için komut satırından aşağıdakileri çalıştırın:
node -v && npm -v
Azure web sitesine dağıtılıyorsa hiçbir eylem gerekmez: Node.js yüklenir ve sunucu ortamlarında kullanılabilir.
-
- Visual Studio 2017 kullanan Windows'ta SDK, .NET Core platformlar arası geliştirme iş yükü seçilerek yüklenir.
Microsoft.AspNetCore.SpaServices NuGet paketi
Sunucu tarafı prerendering
Evrensel (izomorfik olarak da bilinir) uygulaması, hem sunucuda hem de istemcide çalıştırabilen bir JavaScript uygulamasıdır. Angular, React ve diğer popüler çerçeveler, bu uygulama geliştirme stili için evrensel bir platform sağlar. Fikir, önce Node.js aracılığıyla sunucuda çerçeve bileşenlerini işlemek ve ardından istemciye daha fazla yürütme temsilcisi atamaktır.
SpaServices tarafından sağlanan ASP.NET Çekirdek Etiket Yardımcıları , sunucudaki JavaScript işlevlerini çağırarak sunucu tarafı ön kayıt uygulamasını basitleştirir.
Sunucu tarafı ön kayıt önkoşulları
aspnet-prerendering npm paketini yükleyin:
npm i -S aspnet-prerendering
Sunucu tarafı ön kayıt yapılandırması
Etiket Yardımcıları, projenin _ViewImports.cshtml
dosyasındaki ad alanı kaydı aracılığıyla bulunabilir hale getirilir:
@using SpaServicesSampleApp
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
@addTagHelper "*, Microsoft.AspNetCore.SpaServices"
Bu Etiket Yardımcıları, görünümün içindeki HTML benzeri söz dizimlerinden yararlanarak alt düzey API'lerle doğrudan iletişim kurmanın karmaşıklıklarını Razor soyutlar:
<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
asp-prerender-module Etiket Yardımcısı
Önceki asp-prerender-module
kod örneğinde kullanılan Etiket Yardımcısı, Node.js. aracılığıyla sunucuda yürütülürClientApp/dist/main-server.js
. Netlik için dosya, main-server.js
Webpack derleme işlemindeki TypeScript'den JavaScript'e dönüştürme görevinin bir yapıtıdır. Webpack öğesinin main-server
giriş noktası diğer adını tanımlar ve bu diğer ad için bağımlılık grafiğinin çapraz geçişi dosyada ClientApp/boot-server.ts
başlar:
entry: { 'main-server': './ClientApp/boot-server.ts' },
Aşağıdaki Angular örneğindeClientApp/boot-server.ts
, dosya Node aracılığıyla sunucu işlemeyi createServerRenderer
yapılandırmak için npm paketinin aspnet-prerendering
işlevini ve RenderResult
türünü kullanır.js. Sunucu tarafı işlemeyi hedefleyen HTML işaretlemesi, kesin olarak türü belirlenmiş bir JavaScript Promise
nesnesine sarmalanan bir çözüm işlevi çağrısına geçirilir. Nesnenin Promise
önemi, DOM'un yer tutucu öğesine eklemek üzere sayfaya zaman uyumsuz olarak HTML işaretlemesi sağladığıdır.
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
export default createServerRenderer(params => {
const providers = [
{ provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } },
{ provide: 'ORIGIN_URL', useValue: params.origin }
];
return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
const appRef = moduleRef.injector.get(ApplicationRef);
const state = moduleRef.injector.get(PlatformState);
const zone = moduleRef.injector.get(NgZone);
return new Promise<RenderResult>((resolve, reject) => {
zone.onError.subscribe(errorInfo => reject(errorInfo));
appRef.isStable.first(isStable => isStable).subscribe(() => {
// Because 'onStable' fires before 'onError', we have to delay slightly before
// completing the request in case there's an error to report
setImmediate(() => {
resolve({
html: state.renderToString()
});
moduleRef.destroy();
});
});
});
});
});
asp-prerender-data Etiket Yardımcısı
Etiket Yardımcısı ile asp-prerender-module
birlikte kullanıldığında, asp-prerender-data
Etiket Yardımcısı görünümdeki Razor bağlamsal bilgileri sunucu tarafı JavaScript'e geçirmek için kullanılabilir. Örneğin, aşağıdaki işaretleme kullanıcı verilerini modüle main-server
geçirir:
<app asp-prerender-module="ClientApp/dist/main-server"
asp-prerender-data='new {
UserName = "John Doe"
}'>Loading...</app>
Alınan UserName
bağımsız değişken yerleşik JSON seri hale getiricisi kullanılarak serileştirilir ve nesnesinde params.data
depolanır. Aşağıdaki Angular örneğinde veriler, bir h1
öğe içinde kişiselleştirilmiş bir karşılama oluşturmak için kullanılır:
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
export default createServerRenderer(params => {
const providers = [
{ provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } },
{ provide: 'ORIGIN_URL', useValue: params.origin }
];
return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
const appRef = moduleRef.injector.get(ApplicationRef);
const state = moduleRef.injector.get(PlatformState);
const zone = moduleRef.injector.get(NgZone);
return new Promise<RenderResult>((resolve, reject) => {
const result = `<h1>Hello, ${params.data.userName}</h1>`;
zone.onError.subscribe(errorInfo => reject(errorInfo));
appRef.isStable.first(isStable => isStable).subscribe(() => {
// Because 'onStable' fires before 'onError', we have to delay slightly before
// completing the request in case there's an error to report
setImmediate(() => {
resolve({
html: result
});
moduleRef.destroy();
});
});
});
});
});
Etiket Yardımcıları'nda geçirilen özellik adları PascalCase gösterimiyle temsil edilir. Aynı özellik adlarının camelCase ile temsil edildiği JavaScript ile karşıtlık. Bu fark, varsayılan JSON serileştirme yapılandırmasından sorumludur.
Yukarıdaki kod örneğini genişletmek için, işleve sağlanan özelliğin nemlendirilmesiyle veriler sunucudan globals
görünüme resolve
geçirilebilir:
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
export default createServerRenderer(params => {
const providers = [
{ provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } },
{ provide: 'ORIGIN_URL', useValue: params.origin }
];
return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
const appRef = moduleRef.injector.get(ApplicationRef);
const state = moduleRef.injector.get(PlatformState);
const zone = moduleRef.injector.get(NgZone);
return new Promise<RenderResult>((resolve, reject) => {
const result = `<h1>Hello, ${params.data.userName}</h1>`;
zone.onError.subscribe(errorInfo => reject(errorInfo));
appRef.isStable.first(isStable => isStable).subscribe(() => {
// Because 'onStable' fires before 'onError', we have to delay slightly before
// completing the request in case there's an error to report
setImmediate(() => {
resolve({
html: result,
globals: {
postList: [
'Introduction to ASP.NET Core',
'Making apps with Angular and ASP.NET Core'
]
}
});
moduleRef.destroy();
});
});
});
});
});
postList
Nesnenin globals
içinde tanımlanan dizi, tarayıcının genel window
nesnesine eklenir. Genel kapsama yönelik bu değişken hoisting, özellikle de aynı verileri bir kez sunucuya ve yeniden istemciye yüklemeye yönelik olduğu için çabanın yinelenmesini ortadan kaldırır.
Webpack Geliştirici Ara Yazılımı
Webpack Dev Ara Yazılımı , Webpack'in kaynakları isteğe bağlı olarak derlediği kolaylaştırılmış bir geliştirme iş akışı sunar. Ara yazılım, tarayıcıda bir sayfa yeniden yüklendiğinde istemci tarafı kaynaklarını otomatik olarak derler ve hizmet eder. Alternatif yaklaşım, bir üçüncü taraf bağımlılığı veya özel kod değiştiğinde projenin npm derleme betiği aracılığıyla Webpack'i el ile çağırmaktır. Dosyadaki package.json
bir npm derleme betiği aşağıdaki örnekte gösterilmiştir:
"build": "npm run build:vendor && npm run build:custom",
Webpack Geliştirme Ara Yazılımı önkoşulları
aspnet-webpack npm paketini yükleyin:
npm i -D aspnet-webpack
Webpack Geliştirme Ara Yazılımı yapılandırması
Webpack Dev Ara Yazılımı, dosyanın Configure
yönteminde aşağıdaki kod aracılığıyla HTTP isteği işlem hattına Startup.cs
kaydedilir:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
// Call UseWebpackDevMiddleware before UseStaticFiles
app.UseStaticFiles();
UseWebpackDevMiddleware
Uzantı yöntemi aracılığıyla UseStaticFiles
statik dosya barındırma kaydedilmeden önce uzantı yöntemi çağrılmalıdır. Güvenlik nedeniyle ara yazılımı yalnızca uygulama geliştirme modunda çalıştığında kaydedin.
Dosyanın webpack.config.js
output.publicPath
özelliği ara yazılıma klasörü değişiklikler için izlemesini dist
söyler:
module.exports = (env) => {
output: {
filename: '[name].js',
publicPath: '/dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix
},
Sık Erişimli Modül Değiştirme
Webpack'in Sık Erişimli Modül Değiştirme (HMR) özelliğini Webpack Geliştirme Ara Yazılımının bir evrimi olarak düşünün. HMR tüm avantajları sunar, ancak değişiklikleri derledikten sonra sayfa içeriğini otomatik olarak güncelleştirerek geliştirme iş akışını daha da kolaylaştırır. Bunu tarayıcının yenilemesiyle karıştırmayın; bu, SPA'nın geçerli bellek içi durumu ve hata ayıklama oturumunu engeller. Webpack Geliştirme Ara Yazılımı hizmeti ile tarayıcı arasında canlı bağlantı vardır ve bu da değişikliklerin tarayıcıya gönderildiği anlamına gelir.
Sık Erişimli Modül Değiştirme önkoşulları
Webpack-hot-middleware npm paketini yükleyin:
npm i -D webpack-hot-middleware
Sık Erişimli Modül Değiştirme yapılandırması
HMR bileşeninin yönteminde MVC'nin HTTP isteği işlem hattına Configure
kaydedilmesi gerekir:
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
HotModuleReplacement = true
});
Webpack Dev Ara Yazılımı'nın UseWebpackDevMiddleware
doğru olduğu gibi, uzantı yöntemi uzantı yönteminden UseStaticFiles
önce çağrılmalıdır. Güvenlik nedeniyle ara yazılımı yalnızca uygulama geliştirme modunda çalıştığında kaydedin.
Dosya webpack.config.js
boş bıraksa bile bir plugins
dizi tanımlamalıdır:
module.exports = (env) => {
plugins: [new CheckerPlugin()]
Uygulamayı tarayıcıda yükledikten sonra geliştirici araçlarının Konsol sekmesi HMR etkinleştirme onayı sağlar:
Yönlendirme yardımcıları
Çoğu ASP.NET Çekirdek tabanlı SPA'larda, sunucu tarafı yönlendirmeye ek olarak genellikle istemci tarafı yönlendirmesi de istenir. SPA ve MVC yönlendirme sistemleri, müdahale olmadan bağımsız olarak çalışabilir. Ancak zorluklara neden olan bir uç durum vardır: 404 HTTP yanıtını tanımlama.
Uzantısız bir yolunun kullanıldığı senaryoyu /some/page
göz önünde bulundurun. İsteğin bir sunucu tarafı yolu ile desen eşleşmediğini, ancak deseninin istemci tarafı yoluyla eşleşdiğini varsayalım. Şimdi, genellikle sunucuda bir görüntü dosyası bulmayı bekleyen için gelen bir isteği /images/user-512.png
göz önünde bulundurun. İstenen kaynak yolu herhangi bir sunucu tarafı yolu veya statik dosyayla eşleşmiyorsa, istemci tarafı uygulamasının bunu işleme olasılığı düşüktür; genellikle 404 HTTP durum kodu döndürülmesi istenir.
Yönlendirme yardımcıları önkoşulları
İstemci tarafı yönlendirme npm paketini yükleyin. Örnek olarak Angular'ın kullanılması:
npm i -S @angular/router
Yönlendirme yardımcıları yapılandırması
yönteminde Configure
adlı MapSpaFallbackRoute
bir uzantı yöntemi kullanılır:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
Yollar, yapılandırıldıkları sırayla değerlendirilir. Sonuç olarak, default
önceki kod örneğindeki yol önce desen eşleştirme için kullanılır.
Yeni proje oluşturma
JavaScript Hizmetleri önceden yapılandırılmış uygulama şablonları sağlar. SpaServices bu şablonlarda Angular, React ve Redux gibi farklı çerçeveler ve kitaplıklarla birlikte kullanılır.
Bu şablonlar aşağıdaki komutu çalıştırarak .NET CLI aracılığıyla yüklenebilir:
dotnet new --install Microsoft.AspNetCore.SpaTemplates::*
Kullanılabilir SPA şablonlarının listesi görüntülenir:
Şablonlar | Kısa Ad | Dil | Etiketler |
---|---|---|---|
Angular ile MVC ASP.NET Core | açısal | [C#] | Web/MVC/SPA |
React ile MVC ASP.NET Core.js | Tepki | [C#] | Web/MVC/SPA |
React ile MVC ASP.NET Core.js ve Redux | reactredux | [C#] | Web/MVC/SPA |
SPA şablonlarından birini kullanarak yeni bir proje oluşturmak için dotnet new komutuna şablonun Kısa Adını ekleyin. Aşağıdaki komut, sunucu tarafı için yapılandırılmış ASP.NET Core MVC ile bir Angular uygulaması oluşturur:
dotnet new angular
Çalışma zamanı yapılandırma modunu ayarlama
İki birincil çalışma zamanı yapılandırma modu vardır:
- Geliştirme:
- Hata ayıklamayı kolaylaştırmak için kaynak eşlemeleri içerir.
- İstemci tarafı kodunu performans için iyileştirmez.
- Üretim:
- Kaynak eşlemeleri dışlar.
- Paketleme ve küçültme yoluyla istemci tarafı kodunu iyileştirir.
ASP.NET Core, yapılandırma modunu depolamak için adlı ASPNETCORE_ENVIRONMENT
bir ortam değişkeni kullanır. Daha fazla bilgi için bkz . Ortamı ayarlama.
.NET CLI ile çalıştırma
Proje kökünde aşağıdaki komutu çalıştırarak gerekli NuGet ve npm paketlerini geri yükleyin:
dotnet restore && npm i
Uygulamayı derleyin ve çalıştırın:
dotnet run
Uygulama, çalışma zamanı yapılandırma moduna göre localhost'ta başlar. Tarayıcıda adresine giderek http://localhost:5000
giriş sayfası görüntülenir.
Visual Studio 2017 ile çalıştırma
.csproj
dotnet new komutu tarafından oluşturulan dosyayı açın. Gerekli NuGet ve npm paketleri proje açıldığında otomatik olarak geri yüklenir. Bu geri yükleme işlemi birkaç dakika kadar sürebilir ve uygulama tamamlandığında çalışmaya hazırdır. Yeşil çalıştırma düğmesine tıklayın veya tuşuna basın Ctrl + F5
; tarayıcı uygulamanın giriş sayfasında açılır. Uygulama, çalışma zamanı yapılandırma moduna göre localhost üzerinde çalışır.
Uygulamayı test etme
SpaServices şablonları Karma ve Jasmine kullanarak istemci tarafı testleri çalıştırmak için önceden yapılandırılmıştır. Jasmine, JavaScript için popüler bir birim testi çerçevesidir, Karma ise bu testler için bir test çalıştırıcısıdır. Karma, Webpack Geliştirici Ara Yazılımı ile çalışacak şekilde yapılandırılır, böylece geliştiricinin her değişiklik yapıldığında testi durdurması ve çalıştırması gerekmez. İster test çalışmasına karşı çalışan kod ister test çalışmasının kendisi olsun, test otomatik olarak çalışır.
Angular uygulamasını örnek olarak kullanarak dosyasında için zaten iki Jasmine test çalışması sağlanmıştır CounterComponent
counter.component.spec.ts
:
it('should display a title', async(() => {
const titleText = fixture.nativeElement.querySelector('h1').textContent;
expect(titleText).toEqual('Counter');
}));
it('should start with count 0, then increments by 1 when clicked', async(() => {
const countElement = fixture.nativeElement.querySelector('strong');
expect(countElement.textContent).toEqual('0');
const incrementButton = fixture.nativeElement.querySelector('button');
incrementButton.click();
fixture.detectChanges();
expect(countElement.textContent).toEqual('1');
}));
ClientApp dizininde komut istemini açın. Şu komutu çalıştırın:
npm test
Betik, dosyada tanımlanan ayarları okuyan Karma test çalıştırıcısını karma.conf.js
başlatır. Diğer ayarlar arasında, karma.conf.js
dizisi aracılığıyla files
yürütülecek test dosyalarını tanımlar:
module.exports = function (config) {
config.set({
files: [
'../../wwwroot/dist/vendor.js',
'./boot-tests.ts'
],
Uygulamayı yayımlayın
Azure'da yayımlama hakkında daha fazla bilgi için bu GitHub sorununa bakın.
Oluşturulan istemci tarafı varlıklarını ve yayımlanan ASP.NET Core yapıtlarını dağıtıma hazır bir pakette birleştirmek zahmetli olabilir. Neyse ki, SpaServices bu yayın işleminin tamamını adlı RunWebpack
özel bir MSBuild hedefiyle düzenler:
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />
<Exec Command="node node_modules/webpack/bin/webpack.js --env.prod" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="wwwroot\dist\**; ClientApp\dist\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
MSBuild hedefi aşağıdaki sorumluluklara sahiptir:
- npm paketlerini geri yükleyin.
- Üçüncü taraf, istemci tarafı varlıkların üretim sınıfı bir derlemesini oluşturun.
- Özel istemci tarafı varlıklarının üretim sınıfı derlemesini oluşturun.
- Webpack tarafından oluşturulan varlıkları yayımlama klasörüne kopyalayın.
MSBuild hedefi çalıştırılırken çağrılır:
dotnet publish -c Release
Ek kaynaklar
ASP.NET Core