WebAssembly ve Blazor Nedir ?

Selamlar,

Bu makalede, 2018  MVP Global Summit’de karşıma çıkan Microsoft teknolojiler arasında beni ençok heycanlandıran ve etkiliyen WebAssembly üzerine detaylıca konuşacağız.

Peki WebAssembly nedir? Aslında 2015 yılında Google, Mozilla, Apple ve Microsoft’un bir araya gelerek WebAssembly Community Group’u kurmalarıyla başlayan bir oluşumdur. Peki neden bu group kuruldu, çünkü Web uygulamarı artık çok yaygınlaştı. Güncelleme ve kullanım kolaylıklarından dolayı, Desktop uygulamalarının nerede ise yerini aldı. Hali ile beklentiler de arttı. Standart Javascript kodları ihtiyaçları karşılayamaz ya da yazılsa da içinden çıkılamaz bir hal aldı. İşte bu ihtiyaçlara yönelik WebAssembly kavramı hayatımıza girdi. Düşünün artık sunucuya gitmeden tüm işlemlerimizi browser üzerinden yapabileceğiz.

Microsoft tarafından bakarsak, .NET web uygulamalarının şu an halen üzerinde çalışılan Blazor adında bir .NET web framework’ü ile, bildiğimiz browser üzerinde çalıştırılmasıdır. Kısaca backend’e gerek kalmadan, frontend tarafta yazılan C# kodlarının browser tarafında derlenip çalıştırılmasıdır. Bir de bu işin ucundan Steven Sanderson ve Daniel Roth’un tutması, bana iyice ümit verdi.  Sanırım heycanımı anladınız. Şimdi aklınıza eminim türlü türlü sorular geliyordur. Güvenlik, farklı dll’ler nasıl import olarak ekleniyor? Sonuçta gene bir dll’mi oluşuyor ? Performans. Şimdi sorulara soru ile cevap verelim :)

Browser + Razor = Blazor!

Bu Blazor kısaca nedir? WebAssembly altında çalışan, .NET’te yazılmış tarayıcı tabanlı (istemci tarafı) uygulamalar için bir frameworkdür. Biz developerlara, sunucu ve istemci arasında kullanılan tüm.Net kodlarını browser üzerede uçtan uca kullanmamıza olanak verir. Ayrıca aynı Angular ve React’da olduğu gibi SinglePageApplication(SPA) platformunun tüm avantajlarını bize sunar.

.NET tabanlı bir SPA framework oluşturmanın ilk adımı, web tarayıcısı içinde .NET kodunu çalıştırmanın bir yolunu bulmaktır. WebAssembly sayesinde, herhangi bir web tarayıcısı (eklentileri olmayan), ilgili dilleri yani yazılan kodları ve import edilen dll’leri derler. WebAssembly, mobil cihazlar da dahil olmak üzere tüm ana tarayıcılar tarafından desteklenmektedir. Performans amaçlı, minimum boyutlarda ve maksimum çalışma performansı için optimize edilmiş kompakt bir byte kod formatıdır. Client-Side’da çalışmasından dolayı, ilk akla gelen güvenlik endişelerine neden olmaz. Çünkü oluşturulan assembly kodları düzenli yani anlaşılır değildir. (ör. X86 / x64 veya benzeri). Bu aslında JavaScript ile yapılan yeni bir byte kod biçimidir.

WebAssembly ile ilgili bir diğer şey ise, Browser’ın çalıştırıldığı zaman kodlarda late-binding’e izin verilmesidir. Yani farklı modüller birleşerek bir uygulama oluşturabilir. Örneğin önceden hazırlanmış standart bir modül, başka bir modül içerisine referans verilerek kullanılabilir.

Peki bu .NET’i nasıl çalıştırıyor? Eh, Mono ekibi sağ olsun. WebAssembly’e, mono tam destek vermektedir. Mono, WebAssembly altında iki modda çalışmaktadır. Interpreted ve Ahead-of-time (AOT)

Interpreted: Mono runtime’da sadece WebAssembly için derleme işlemi yaparken, dll’leri bunun dışında bırakır. Esas .NET .dll dosyaları, klasik .Net tooları ile harici derlenir.

Ahead-of-time (AOT) : WebAssembly çalışma zamanında doğrudan binary koda dönüştürülür. Yani, içerisine dahil olan tüm Dll’lerde WebAssembly binaries’e dönüştürülür. Kısaca yazılan kod yorumlanmadan doğrudan WebAssembly kodu olarak çalıştırılır. Bu aşamada yine de Monoya sadece (garbage collection gibi) basit yapılar için ihtiyaç vardır.

Interpreted AOT’ye göre çok daha performanslı çalışmaktadır. Çünkü Interpret(yorumlanan) dillerden farklı olarak run-timeda herhangi bir parsing işlemi yapmaz. Bu neden ile başlangıç zamanı çok daha hızlıdır. Çünkü zaten kodun parsing veya optimizasyon yapılmasına ihtiyacı yoktur. Yani yazılan ve binnary formata dönüşen kod, direkt olarak platforma en uygun en optimize machine koduna derlenebilir.

Blazor’da Esas Bileşenler:

  • Layouts
  • Routing
  • Dependency injection
  • Lazy loading
  • Unit testing harness

Bu kadar PowerPoint açıklamadan sonra  :) şimdi gelin bu efsane teknolojiyi nasıl kullanacağımızı hep beraber örnekler ile inceleyelim:

KURULUM: Bu makaledeki çalışma ortamı macOS işletim sistemi, .NetCore ve Ise olarak VSCode’dur.

  1. .NET Core 2.1 Preview 1 SDK. buradan yüklenir.
  2. Yeni yaratılacak olan dotnet Blazor Template‘i aşağıdaki gibi yüklenir.
  3. Yeni WebAssembly projesi “BlazorToDoLiST” aşağıdaki gibi oluşturulur.

Karşımıza aşağıdaki gibi bir ekran gelir:

Öncelikle Blazor ile demo amaçlı gelen default projeleri kısaca inceleyelim:

Genel Blazor Proje yapısı : Ne Nerde

Routing : App.cshtml sayfası üzerinde tanımlanır. Başlangıç dosyası olarak “Program.cs” dosyası gösterilmiştir.

Program.cs: Aşağıda görüldüğü gibi aynı bir console uygulamasında olduğu gibi :) klasik bir Main() methodu ile yaşam döngüsüne başlanmaktadır. “Index” sayfasında kullanılacak “app” tagı ile render edileceği belirtilmiştir. Ayrıca diğer tüm kullanılacak servis tanımları burada yapılır.

wwwroot/index.html: Program.cs’de geçen “app” başlangıç sayfası olan “index.html”‘de kullanılır. Bu sayfada, proje içerisinde kullanılan “bootstrap” ve” blazor-boot” dosyaları include edilmesinden başka <app>Loading…</app> ile render edilecek sayfa “<body>” içerisine konur.

_ViewImports.cshtml: Tüm sayfalarda kullanılacak  .Net ve diğer kütüphaneler burada tanımlanır.

Layouts

Pages/_ViewImports.cshtml: Sayfalarda kullanılcak Layout yapıları yine burada ek olarak tanımlanır. Bu projede Ana layout MainLayout dosyasıdır.

Shared/MainLayout: Sayfanın bir başka Interface veya sayfadan türetilmesi ==> Blazor syntax => “@implements ILayoutComponent” şeklinde yapılır.

  • Sayfada kullanılacak Menu ==> “<NavMenu />” şeklinde, ana kısım  “@Body” tagı ile belirlenir.
  • “@functions { public RenderFragment Body { get; set; } }” : Sayfanın yüklendiğinde çalıştırılan C# kodu Blazor Syntax ==> “@function {}” içinde tanımlanır. Aşağıda “RenderFragment()” methodu ile “Body” render edilir.

Shared/NavMenu: Sayfanın solundaki menu, aşağıda görüldüğü gibi tanımlanmıştır. “<NavLink” ==> “/” tıklanınca herbir sayfada tanımlanan path’e gidilir. Bu şekilde bir tanımlama ==> “/” ana sayfa demektir. “/counter” Counter sayfasına git demektir. İlgili Router tanımlaması yine “counter” sayfası üzerinde belirtilmektedir. Kısaca erişilecek sayfanın fiziksel adı üzerine yazılır. Erişilmek istenen adres bu ve buna benzer şekilde tanımlanır.

index.cshtml: @page “/”‘ile sayfaya erişim şekli tanımlanmıştır. “<SurveyPrompt Title=”Blazor ‘ı nasıl buldunuz?” />” Başka bir yerde tanımlı bir UserControl yani başka bir sayfadır. Property’si de Title’dır. Bence bu da efsane bir kullanım. Angulardaki Directivelere denk gelmektedir :)

Shared/SurveyPropmt.cshtml: userControl olarak index sayfasında kullanılan bu sayfa aşağıda görüldüğü gibi “@functions{}” içerisine ilgili “Title” property’si tanımlanmıştır. Sayfa üzerinde parametre olarak girilen “@Title” değişkeni bir text ve tıklandığında gidilecek bir link bulunmaktadır.

Counter.cshtml: NavMenu’de “/counter” şeklinde tanımlı bir sayaç sayfasıdır. Örnek amaçlı yapılan bir değişkenin button’a tıklandığında, 1 arttırılıp o anki zamanla ekrana basıldığı sayfadır.

  • @page “/counter” : Routing amaçlı sayfaya nasıl erişileceği belirtilir.
  • @functions { } ilgili C# kodlarının ve hatta sınıfların tanımlandığı yerdir.
  • void IncrementCount() { time=DateTime.Now; currentCount++; } Button tıklanması ile çağrılacak olan methoddur. Güncel zaman ve “currentCount” değişkeni bir arttırılır.
  • <button @onclick(IncrementCount)>Tıkla Beni</button> : Button’un tıklanma eventinde çağrılacak method ==> “@onclick(IncrementCount)” şeklinde tanımlanır.
  • <p>Current count: @currentCount : @time</p> : C# tarafında tanımlı değişkenler RazorViewEngine’inden dolayı “@” ile “@currentCount : @time” şeklinde tanımlanır.

FetchData.cshtml: Bir servisden ya da json’dan çekilen hava bilgisinin, ekrana basıldığı örnek bir Razor sayfadır.

  • @page “/fetchdata” : Routing amaçlı sayfaya erişim bu şeklinde tanımlanmıştır.
  • “@inject HttpClient Http” : Sayfa içerisinde “Http.Get()” methodunun kullanılabilmesi için ilgili kütüphane  eklenir.
  • @functions { WeatherForecast[] forecasts; } : @function içerisinde tanımlı C# kodlarında, “Json”‘dan çekilecek data burda tanımlı WeatherForecast[] dizisinde saklanacaktır.
  • protected override async Task OnInitAsync() { forecasts = await Http.GetJsonAsync<WeatherForecast[]>(“/sample-data/weather.json”); } : Sayfa asenkron olarak yüklenirken “weather.json” datası yine asenkron çekilerek “<WeatherForecast[]>” tipinde “forecasts[ ]” değişkenine atanmaktadır. Ve işin en ilginç yanı bu kod client side tarafta yazılmakta ve browser tarafından backend’e ihtiyaç duyulmadan derlenmektedir.
  • İlgili sınıf ve 4 property’si yine “@function{}” içinde tanımlanmıştır.
  • Sayfanın yukarısında Razor ile çekilen “WeatherForecast[ ]” datası ekrana klasik yol ile basılır.
    • “@if (forecasts == null) { <p><em>Loading…</em></p> }” : Asenkron olarak henüz bir kayıt çekilmemiş ise sayfaya “Loading…” yazısı konur. Bunun yerine Loading bir gif de konabilirdi.
    • else{} : Kayıt var ise “@foreach (var forecast in forecasts)” json’dan çekilen tüm data tek tek gezilir.
    • “@forecast.Date.ToShortDateString()” : Herbir kayıta ait tüm propertyler ekrana “@” işareti ile basılır.

wwwroot/sample-data/weather.json: Örnek dummy Weather Json data.

Son ve en önemli örnek olarak gelin bir ToDo sayfası yapalım. Böylece (SAP) application nasıl WebAssembly ile kullanılıyormuş hep beraber inceleyelim.

ToDo.cshtml :

  1. Önce Navbar.cshtml’e yani menüye, ilgili sayfa link’ini aşağıdaki gibi eklenir:
  2. Pages/ToDo.cshtml oluşturulur.
  3. Sayfa yolu(Routing) ve başlık aşağıdaki gibi tanımlanır.

    1. “function{}” altına TodoItem class’ı, yani yapılacak işler sınıfı aşağıdaki gibi tanımlanır. 2 property’si var. 1 Title (adı), 2 IsDone (yapıldı mı).
  4. function içerisine, yapılacak işlerin tutulacağı “todos[ ]” dizisi aşağıdaki gibi tanımlanır.
  5. Yeni eklenecek işe karşılık gelen değişken “newTodo” ve bir buttona tıklanınca eğer yeni iş adı null değil ise, “lodos [ ]” dizisine ekleyen “AddTodo()” methodu aşağıdaki gibi tanımlanmıştır.
  6. Geldik Html tarafa. Sayfa üzerine yapılan işleri gezerek ekrana basan rapor kod parçacığı aşağıdaki gibidir:  “@foreach” ile tüm yapı gezilirken. <checkbox>’a “@bind(todo.IsDone)” property’si “@bind” ile bağlanmıştır. Böylece checkbox tıklandığı zaman sıradaki yani buna bağlı todo sınıfına ait IsDone”‘property’si otomatik olarak değişmektedir. ” Ayrıca “<input>” alana “@bind(todo.Title)” ile atanması gene ilgili todo item’ın Title propertysi otomatik olarak değişmektedir.
  7. Son olarak listenin sonuna, yeni görev textbox’ı ve bir de Ekle buttonu konulmuştur. Aşağıdaki  button’un “@onclick()” methodu ile textbox’a yazılan yeni yapılacak iş değeri, AddTodo() methodu ile ilgili todos[ ] dizisine eklenmekte ve sayfa yeniden render edilmektedir. Oluşan yeni string yapıda, değişen alanlar güncellenerek sayfanın render işlemi sadece değişen bu alanlar için tekrardan yapılmaktadır.
  8. Sayfanın başına yapılacak işlerin sayısı: Aşağıdaki gibi Linq kullanılarak yazdırılmıştır. Böylece listeye her eklenen iş için, bu değer henüz tamamlanmadığı için 1 artmaktadır. Ayrıca tamamlandı şeklindeki checkbox’ın, işaretlenmesi durumunda bu sayı aynı (SPA) olduğu gibi otomatik olarak 1 azalmaktadır.

ToDo.cshtml (Full):

Browser üzerinde backend taraflı herşeyi yapamazsınız. Mesela browser üzerinden TCP socket’i dinleyemessiniz. “System.Net.Sockets.TcpListener” bu durumda hiçbir işe yaramaz. Ya da “System.Data.SqlClien”‘ın doğrudan browser üzerinden kullanılması doğru değildir. Aksi halde “PlatformNotSupported exception” hatası alırsınız. Kısa vadede karşımıza, WebAssembly’nin uygun olmadı daha birçok durum çıkacaktır. Fakat NuGet package geliştiricileri ilerde bunlara da izin vericek düzenlemeler yapacaklardır.

Benim aklıma gelen bir diğer sorun ise var olan javascript kodlarının .Net kodları tarafından çağrılması idi. Ama bununda üzerinde aşağıdaki gibi halen çalışılmakta ve Javascript kodları C# içerisinden çağrılabilmektedir.

Sayfa içerisinde render işleminden sonra oluşan düz string değerin örneğin 20mb, 30mb gibi değerlere gelmesi, sonrasında cache’e alınsa da ilk çalışma anında performans kaybının yaşanmasına neden olucaktır. Halen üzerinde çalışılan ve şu an için 3 farklı yol ile çözüm aranan bu sorun, WebAssembly’nin üzerinden atlaması gereken en büyük basamaklardan biridir.

Değişiklik yapıldığı zaman, hızlıca tekrar derleme işleminin yapılıp sonuçlarının görülememesi, bunun için uygulamanın durdurulup yeniden derlenmesinin gereksinimi ve debug işlemlerinin henüz eklenmemesi, ilk göze çarpan eksikler olarak görülebilir. Ama bunlar aşılamayacak sorunlar değildir. Server Side kodların ClientSide tarafda, browserların gücü kullanılarak derleme işleminin yapılması, sayfadaki değişkenlerin Html nesnelere @bind edilerek basılması ve ilgili nesnelerin propertylerinin değişmesi durumunda, sayfanın (SPA) tekrar render edilerek değişimin direk görülmesi gerçekten muazzam. Unutulmamalıdır ki Blazor halen deneysel olarak geliştirilen bir .NET web frameworküdür. Ve bence webassembly web teknolojilerinin gelecekteki mihenk taşıdır. Çünkü browser istenen her dili derlemektedir. Bu da Javascript’e büyük bir destek sağlamakta ve front tarafı da dilden bağımsız bir hale getirmektedir.

Geldik bir makalenin daha sonuna. Yeni bir makalede görüşmek üzere hoşçakalın.

Source Code : https://github.com/borakasmer/WebAssemblyWithBlazor

Kaynaklar:

Herkes Görsün:

Bunlar da hoşunuza gidebilir...

8 Cevaplar

  1. Bülent erdem dedi ki:

    Ellerinize sağlık.Bu konudaki makalelerinizi sabırsızlıkla bekliyorum.

  2. Ali dedi ki:

    Eline saglik abi. Cok guzel bir yazi olmus. Sanirim webin gelecegi cok farkli bir hal alacak gibi.

  3. Tagi dedi ki:

    Böyle bir mükemmel mekaleyi okuduğum için size teşekkür ederim.Bu kadar tecrübeli insandan ancak bu kadar müthiş bir yazı gele bilirdi

  4. A OĞUZ dedi ki:

    Ellerinize sağlık çok güzel bir makale olmuş.

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir