Isomorphic Javascript
Isomorphic Javascript, hem client hem de server tarafında çalışan Javascript uygulamalarını ifade etmek için kullanılan bir kavramdır. Bu yaklaşım, dinamik web uygulamalarında ortaya çıkan problemleri ortadan kaldırmayı amaçlamaktadır.
Dinamik web uygulamaları geliştirirken genelde baz aldığımız 2 yaklaşım var. Bunlardan birincisi web sayfalarını sunucu tabanlı uygulamalar üzerinde oluşturmaktır. Statik HTML sayfaları, Java, .NET, PHP vb. diller ile geliştirilen uygulamalar üzerinden dinamik bir yapıya çevirilmektedir. Örneğin Java tarafında klasik olarak kullandığımız JSP, JSF, Spring MVC gibi frameworkler ile tam olarak bunu yapmaktayız.
Bu yaklaşımın dezavantajı bakım zorluğudur. Her ne kadar kullanılan frameworkler view katmanını diğer katmanlardan ayırmaya çalışsa da, view katmanında bir template engine yapısı kurulmakta ve bu engine’in sağladığı web standartları dışında bir yapı kullanılmaktadır. Örneğin Java’da Thymeleaf gibi bir engine kullanıyosanız HTML’i dinamik hale getirmek için Thymeleaf’in sağladığı attribute ve tag’leri kullanmanız gerekmektedir.
Bununla birlikte, günümüz web uygulamalarında Ajax istekleri de sıklıkla kullanılmaktadır. Ajax istekleri ile sunucu tarafından alınan bilgiler Javascript ile işlenip HTML manipüle edilmektedir. Bu durumda HTML’in render edildiği iki yapı ortaya çıkmakta ve önyüzün bakımı çok daha karmaşık hale gelmektedir.
Web uygulamaları geliştirirken kullandığımız 2. yaklaşım ise Single Page Application(SPA) mimarisine uygun frameworkler kullanarak web uygulamaları oluşturmaktır. Bu yapılar ile önyüz tamamen Javascript ile oluşturulmaktadır. Kullanıcı browser’ı ile yapılan ilk istekte Javascript dosyası browser tarafından yüklenir ve Javascript gerektiğinde Ajax ile sunucudan datayı alıp HTML’i render eder. Browser tarafından yapılan ilk istekten sonra sayfa yenilenmez.
SPA ile önyüz tamamen Javascript ile yönetildiği için bakım daha kolay hale gelmektedir. Bununla birlikte web standartları kullanıldığı için herhangi bir sunucu tabanlı uygulama bağımlılığı da olmamaktadır. Ancak SPA tarafında da 2 temel problem vardır. Bunlar performans ve SEO problemleridir.
SPA’larda genellikle uygulamanın önyüz tarafı tek bir Javascript dosyası halinde paketlenir. Uygulama büyüdükçe doğal olarak pakette büyür. Bu paketin browser’a inmesi ve browser tarafından render edilmesi çok uzun zaman alabilmektedir. Javascript render olup gerekli sayfayı oluşturana kadar, sunucudan gelen boş bir sayfa kullanıcıya gösterilmektedir. Bununla birlikte ilk istekte boş bir HTML sayfası geldiği için arama motorları da uygulamayı doğru bir şekilde indexleyememektedir. (Doğru bir şekilde diyorum çünkü bazı arama motorlarının Javascript rendering yaptığı ancak framework bazlı problemler olduğu ile ilgili çalışmalar var.(https://moz.com/blog/search-engines-ready-for-javascript-crawling))
2 yaklaşımın da kendi içinde dezavantajları bulunmaktadır. Isomorphic Javascript, bu dezavantajları ortadan kaldırmayı hedefleyen bir mimari yaklaşımdır. Isomorphic Javascript, hem önyüz geliştirmelerinde web standartlarını kullanarak uygulama yapmayı, hem de sunucu tarafında render edilmiş kullanıma hazır bir sayfa sunarak arama motorlarına indexleyecek bir içeriği ve kullanıcıların Javascript’in yüklenmesini beklemeden içeriğe erişimini sağlamaktadır. Bunu sağlamak için browser’da çalışan Javascript’in sunucu tarafında da çalışması gerekmektedir.
Isomorphic Javascript, Node.js ile birlikte sunucu tarafında Javascript çalıştırabilmenin mümkün olmasıyla ortaya çıkmıştır. Bu anlamda Isomorphic Javascript yaklaşımının temelinde Node.js yer alıyor diyebiliriz. Ben bu yazıda ise Node.js yerine farklı bir environment kullanacağım. Java 8 ile birlikte gelen ve JVM üzerinde Javascript çalıştırmayı sağlayan Nashorn engine ile bir örnek yapacağım. Ancak yazacağımız Javascript Node.js veya herhangi başka bir Javascript engine’de de çalışacaktır.
Bu kadar teorik bilgiden sonra artık uygulamaya başlayabiliriz.
Bilgisayarımızda JDK 8 ve Node.js kurulu olduğunu varsayıyorum.
Projemizin sunucu kısmı Spring Boot tabanlı bir uygulama olacak. Client tarafında ise bir React.js uygulaması yapacağız. Javascript paketlemek için Webpack, Javascript yazarken de EcmaScript 6 özelliklerini kullanabilmek için Babel.js kullanacağız.
İlk adım olarak projemizi oluşturuyoruz. Bunun için https://start.spring.io/ adresini kullanabiliriz. Ekran görüntüsünde gözüken şekilde bir web uygulaması oluşturuyoruz. (Dependency olarak web’i eklememiz gerekiyor.)
Projeyi indirdikten sonra, zip’i bilgisayarımızdaki uygun bir yere açıyoruz ve proje içerisine client adında bir klasör oluşturuyoruz. Komut satırıyla client klasörü lokasyonuna gidiyoruz ve aşağıdaki komutları çalıştırıyoruz:
npm init -y
npm install babel-core babel-loader babel-polyfill babel-preset-env babel-preset-react html-webpack-plugin webpack webpack-cli — save-dev
npm install react react-dom — save
Client klasörü içerisinde .babelrc isimli bir dosya oluşturuyoruz ve aşağıdaki kodu yazıyoruz:
Yine client klasörü içerisinde webpack.config.js adında bir dosya oluşturuyoruz ve aşağıdaki içeriği yazıyoruz:
Client klasörü içerisinde src isimli bir klasör oluşturuyoruz ve bu klasör altında index.html isimli dosya oluşturuyoruz. Dosya içeriğini şu şekilde dolduruyoruz:
src klasörü altında App.js dosyası oluşturuyoruz:
src klasörü altında client.js dosyası oluşturuyoruz:
client klasörü altında bulunan packages.json dosyası içerisindeki scripts objesine yeni bir komut ekliyoruz:
Get Berker Narol’s stories in your inbox
Join Medium for free to get updates from this writer.
“build”: “webpack -p”
client klasöründeyken aşağıdaki komutu çalıştırıyoruz:
npm run build
İşlem bittikten sonra client klasörü altında dist klasörü oluşacaktır. Dist klasörü içerisindeki index.html ‘i browser ile açtığımızda Hello, berker çıktısını göreceğiz.
Bu noktada browser’da çalışan bir ReactJS uygulamasını tamamlamış oluyoruz.
Şimdi sıra Javascript’i server tarafında render etmeye geldi. Spring tarafındaki konfigürasyonlarımızı yapmadan önce Javascript tarafında da birkaç düzenleme yapmamız gerekiyor.
client.js içerisinde App bileşenini oluşturup bunu DOM üzerindeki root id’li elementin içerisinde göstermesini kodladık. Bunu server tarafında çalıştıramayız çünkü server’da bir DOM yok ve bizim App bileşenini render edip Html dosyasındaki ilgili alana yerleştirmemiz ve clienta hazır bir şekilde göndermemiz gerekiyor. Bunun için server.js isimli bir dosya oluşturuyoruz:
server.js içerisinde render isimli bir fonksyion oluşturuyoruz ve template ve model adında iki parametre alıyoruz. Bu parametrelerin ilki oluşturduğumuz statik Html sayfası, ikincisi ise server tarafında tuttuğumuz state bilgisini içermektedir. Fonksiyonun içerisinde App bileşenimizi render ediyoruz ve Html içerisine yerleştiriyoruz.
Bu noktada index.html dosyamızı tekrar düzenliyoruz:
Yeni oluşturduğumuz server.js’in de webpack tarafından derlenmesi için webpack.config.js tarafını aşağıdaki gibi değiştiriyoruz:
Browser üzerinde bulunan ancak server tarafındaki javascript ortamında olmayan objeler olabiliyor. Eğer javascript kodumuzda bu objeler kullanılıyorsa server tarafında hata alıyoruz. Bunu önlemek adına yapılan bir best practice var. Bu da polyfill.js adında bir javascript dosyası oluşturup bu objeleri onun içerisinde oluşturmak. Bu kapsamda biz de bir polyfill.js dosyası oluşturuyoruz ve içerisine aşağıdaki kodu yazıyoruz:
var window = this;
Şimdi Spring tarafındaki geliştirmeleri yapıyoruz. Öncelikle ScriptViewConfig isimli bir sınıf oluşturuyoruz:
IndexController :
Geliştirmemiz bitti. Spring uygulamamızı çalıştırmadan önce client klasöründe tekrar npm run buil komutunu çalıştırıp javascriptlerimizi derliyoruz. Daha sonra Springboot uygulamamızı çalıştırıyoruz. Browser üzerinden http://localhost:8080 sayfasını açıyoruz. Developer console üzerinden network ile gelen isteğe baktığımızda serverdan render edilmiş içeriğin geldiğini görüyoruz:
Sayfa yüklendikten sonra, “Hello, asd” yazısı “Hello, berker” olarak değişiyor. Bunun da nedeni sayfa yüklendikten sonra client.js’in çalışıp App bileşenini kendi değişkeni ile tekrar render etmesinden kaynaklanmaktadır. client.js’te de server’ın state’ini kullanacak şekilde değiştirmemiz gerekiyor:
Daha sonra tekrar npm run build çalıştırıp uygulamayı restart ettiğimizde mesajın değişmediğini görebiliriz.
Isomorphic Javascript, özellikle büyük çaplı SPA tabanlı uygulamalarda değerlendirilmesi gereken önemli bir yaklaşımdır. Bu yazı ile önemli noktalarına değinip basit bir örnekle ReactJS uygulamasının sunucu tarafında nasıl çalıştığını göstermeye çalıştım.
Örnek uygulamanın tamamına https://github.com/berker/isomorphic üzerinden erişebilirsiniz.








