React.js Ekosisteminde Gezinme
Yayınlanan: 2022-03-11JavaScript Land'deki yeniliğin hızı o kadar yüksektir ki, bazı insanlar bunun verimsiz olduğunu bile düşünür. Bir kütüphane, birkaç ay içinde erken evlat edinen bir oyuncaktan son teknoloji ürünü, eskimeye dönüşebilir. En az bir yıl daha alakalı kalacak bir araç belirleyebilmek, başlı başına bir sanat haline geliyor.
React.js iki yıl önce piyasaya sürüldüğünde, Angular'ı yeni öğreniyordum ve React'i belirsiz bir başka şablonlama kitaplığı olarak çabucak reddettim. Bu iki yıl boyunca, Angular JavaScript geliştiricileri arasında gerçekten bir yer edindi ve neredeyse modern JS geliştirme ile eş anlamlı hale geldi. Angular'ı çok muhafazakar kurumsal ortamlarda bile görmeye başladım ve parlak geleceğini kesin olarak kabul ettim.
Ama aniden, garip bir şey oldu. Görünüşe göre Angular, Osborne etkisinin veya “ön duyuru ile ölümün” kurbanı oldu. Ekip, ilk olarak, Angular 2'nin Angular 1'den net bir geçiş yolu olmadan tamamen farklı olacağını ve ikincisi, Angular 2'nin bir yıl kadar daha kullanılamayacağını duyurdu. Bu, yeni bir web projesi başlatmak isteyen birine ne anlatıyor? Yeni projenizi, yeni bir sürüm yayınlandığında geçersiz hale gelecek bir çerçeve içinde mi yazmak istiyorsunuz?
Geliştiriciler arasındaki bu endişe, kendisini toplulukta kurmaya çalışan React'in eline geçti. Ancak React, kendisini her zaman “MVC”de “V” olarak pazarladı. Bu, eksiksiz çerçevelerle çalışmaya alışmış web geliştiricileri arasında bir miktar hayal kırıklığına neden oldu. Eksik parçaları nasıl tamamlarım? Kendim yazmalı mıyım? Sadece mevcut bir kütüphaneyi kullanmalı mıyım? Eğer öyleyse, hangisi?
Tabii ki, Facebook (React.js yaratıcıları) delikte bir asa daha sahipti: Eksik “M” ve “C” işlevlerini doldurmayı vaat eden Flux iş akışı. İşleri daha da ilginç hale getirmek için Facebook, Flux'un bir çerçeve değil bir "kalıp" olduğunu ve Flux'u uygulamalarının kalıbın sadece bir örneği olduğunu belirtti. Sözlerine sadık kalarak, uygulamaları gerçekten basitti ve işleri ilerletmek için çok sayıda ayrıntılı, tekrarlayan ortak yazı yazmayı içeriyordu.
Açık kaynak topluluğu kurtarmaya geldi ve bir yıl sonra düzinelerce Flux kitaplığımız ve hatta onları karşılaştırmayı amaçlayan bazı meta projelerimiz var. Bu iyi birşey; Facebook, hazır bir kurumsal çerçeve yayınlamak yerine, topluluğun ilgisini çekmeyi başardı ve insanları kendi çözümlerini bulmaya teşvik etti.
Bu yaklaşımın ilginç bir yan etkisi vardır: tam çerçevenizi elde etmek için birçok farklı kitaplığı birleştirmeniz gerektiğinde, satıcı bağımlılığından etkili bir şekilde kurtulursunuz ve kendi çerçeve oluşturmanız sırasında ortaya çıkan yenilikler kolayca yeniden kullanılabilir. başka bir yerde.
Bu yüzden React'in etrafındaki yeni şeyler çok ilginç; çoğu, diğer JavaScript ortamlarında kolayca yeniden kullanılabilir. React'i kullanmayı planlamıyor olsanız bile, ekosistemine bir göz atmak ilham verici. Güçlü, ancak yapılandırması nispeten kolay olan modül paketleyici Web paketini kullanarak yapı sisteminizi basitleştirmek veya Babel derleyicisiyle bugün ECMAScript 6 ve hatta ECMAScript 7 yazmaya başlamak isteyebilirsiniz.
Bu makalede, mevcut olan bazı ilginç özellikleri ve kitaplıkları gözden geçireceğim. Öyleyse, React ekosistemini keşfedelim!
Yapı Sistemi
Yeni bir web uygulaması oluştururken muhtemelen ilk dikkat etmeniz gereken şey derleme sistemidir. Derleme sistemi yalnızca komut dosyalarını çalıştırmak için bir araç değildir, JavaScript dünyasında genellikle uygulamanızın genel yapısını şekillendirir. Bir yapı sisteminin kapsaması gereken en önemli görevler şunlardır:
- Dış ve iç bağımlılıkları yönetme
- Derleyicileri ve önişlemcileri çalıştırma
- Varlıkları üretim için optimize etme
- Geliştirme web sunucusunu, dosya izleyicisini ve tarayıcı yeniden yükleyicisini çalıştırma
Son yıllarda, Bower ve Grunt ile Yeoman iş akışı, modern ön uç geliştirmenin kutsal üçlüsü olarak sunuldu ve son zamanlarda Grunt'tan Gulp'a daha ilerici geçişle birlikte sırasıyla standart oluşturma, paket yönetimi ve ortak görev çalıştırma sorunlarını çözdü.
React ortamında bunları güvenle unutabilirsiniz. Bunları kullanamayacağınız için değil, ancak Webpack ve eski güzel NPM'yi kullanmaktan kurtulma şansınız var. Bu nasıl mümkün olabilir? Webpack, Node.js dünyasında yaygın olan CommonJS modül sözdizimini tarayıcıda da uygulayan bir modül paketleyicidir. Front end için başka bir paket yöneticisi öğrenmeniz gerekmediğinden aslında işleri kolaylaştırır; sadece NPM'yi kullanır ve bağımlılıkları sunucu ile ön uç arasında paylaşırsınız. Ayrıca, JS dosyalarını doğru sırayla yükleme sorunuyla uğraşmanıza gerek yoktur, çünkü bu, her dosyada belirtilen bağımlılık içe aktarmalarından çıkarılır ve tüm sürü, yüklenebilir bir komut dosyasına doğru bir şekilde birleştirilir.
Daha da çekici hale getirmek için Webpack, eski kuzeni Browserify'ın aksine, diğer varlık türlerini de işleyebilir. Örneğin, yükleyicilerle, herhangi bir varlık dosyasını, başvurulan dosyayı satır içi veya yükleyen bir JavaScript işlevine dönüştürebilirsiniz. Bu nedenle, manuel olarak ön işlemeyi ve varlıkları HTML'den referans almayı unutun. JavaScript'ten CSS/SASS/LESS dosyalarınızı isteyin ve require
gerisini basit bir yapılandırma dosyasıyla halleder. Web paketi ayrıca bir geliştirme web sunucusu ve bir dosya izleyici içerir. Ayrıca, tek satırlık kabukları tanımlamak için package.json
"scripts"
anahtarını kullanabilirsiniz:
{ "name": "react-example-filmdb", "version": "0.0.1", "description": "Isomorphic React + Flux film database example", "main": "server/index.js", "scripts": { "build": "./node_modules/.bin/webpack --progress --stats --config ./webpack/prod.config.js", "dev": "node --harmony ./webpack/dev-server.js", "prod": "NODE_ENV=production node server/index.js", "test": "./node_modules/.bin/karma start --single-run", "postinstall": "npm run build" } ... }
Ve Gulp ve Bower'ı değiştirmek için ihtiyacınız olan tek şey bu. Tabii ki, Yeoman'ı uygulama ortak plakası oluşturmak için kullanabilirsiniz. İstediğiniz şeyler için Yeoman oluşturucu olmadığında cesaretiniz kırılmasın (en modern kitaplıklarda genellikle yoktur). Yine de GitHub'dan bazı ortak kalıpları klonlayabilir ve hackleyebilirsiniz.
Yarının, Bugünün ECMAScript'i
JavaScript dil geliştirme hızı son yıllarda önemli ölçüde artmıştır ve tuhaflıkları ortadan kaldıran ve dili stabilize eden bir dönemden sonra, şimdi güçlü yeni özelliklerin geldiğini görüyoruz. ECMAScript 6 (ES6) belirtim taslağı tamamlandı ve henüz resmileştirilmedi, zaten yaygın bir şekilde benimseniyor. ECMAScript 7 (ES7) üzerinde çalışmalar devam ediyor, ancak özelliklerinin çoğu daha son teknoloji kitaplıklar tarafından zaten benimseniyor.
Bu nasıl mümkün olabilir? Belki Internet Explorer'da desteklenene kadar bu parlak yeni JavaScript özelliklerinden yararlanamayacağınızı düşünüyorsunuz, ancak tekrar düşünün. ES aktarıcıları zaten o kadar yaygın hale geldi ki, uygun tarayıcı desteği olmadan bile yapabiliriz. Şu anda mevcut olan en iyi ES aktarıcısı Babel'dir: en yeni ES6+ kodunuzu alacak ve onu vanilya ES5'e dönüştürecektir, bu nedenle, herhangi bir yeni ES özelliğini icat edildiği anda kullanabilirsiniz (ve genellikle oldukça sık gerçekleşen Babel'de uygulanır). hızlıca).
En yeni JavaScript özellikleri, tüm ön uç çerçevelerde kullanışlıdır ve React, yakın zamanda ES6 ve ES7 spesifikasyonlarıyla iyi çalışacak şekilde güncellendi. Bu yeni özellikler, React ile geliştirme yaparken birçok baş ağrısını ortadan kaldırmalıdır. En faydalı eklemelerden bazılarına ve bir React projesine nasıl fayda sağlayabileceklerine bir göz atalım. Daha sonra, bu iyileştirilmiş sözdizimini kullanırken bazı yararlı araçları ve kitaplıkları React ile nasıl kullanacağımızı göreceğiz.
ES6 sınıfları
Nesne yönelimli programlama, güçlü ve yaygın olarak benimsenen bir paradigmadır, ancak JavaScript'in bunu ele alışı biraz egzotiktir. Backbone, Ember, Angular veya React gibi ön uç çerçevelerin çoğu, bu nedenle sınıfları tanımlamanın ve nesneler yaratmanın kendi özel yollarını benimsemiştir. Ancak ES6 ile artık JavaScript'te geleneksel sınıflarımız var ve kendi uygulamamızı yazmak yerine bunları kullanmak mantıklı. Yani, yerine:
React.createClass({ displayName: 'HelloMessage', render() { return <div>Hello {this.props.name}</div>; } })
yazabiliriz:
class HelloMessage extends React.Component { render() { return <div>Hello {this.props.name}</div>; } }
Daha ayrıntılı bir örnek için, eski sözdizimini kullanarak bu kodu göz önünde bulundurun:
React.createClass({ displayName: 'Counter', getDefaultProps: function(){ return {initialCount: 0}; }, getInitialState: function() { return {count: this.props.initialCount} }, propTypes: {initialCount: React.PropTypes.number}, tick() { this.setState({count: this.state.count + 1}); }, render() { return ( <div onClick={this.tick}> Clicks: {this.state.count} </div> ); } });
Ve ES6 sürümüyle karşılaştırın:
class Counter extends React.Component { static propTypes = {initialCount: React.PropTypes.number}; static defaultProps = {initialCount: 0}; constructor(props) { super(props); this.state = {count: props.initialCount}; } state = {count: this.props.initialCount}; tick() { this.setState({count: this.state.count + 1}); } render() { return ( <div onClick={this.tick.bind(this)}> Clicks: {this.state.count} </div> ); } }
Burada, getInitialState
getDefaultProps
yaşam döngüsü yöntemleri artık gerekli değildir. getDefaultProps
, defaultProps
statik sınıf değişkeni haline gelir ve ilk durum sadece yapıcıda tanımlanır. Tek dezavantajı, yöntemlerin artık otomatik olarak sınırlandırılmamasıdır, bu nedenle JSX'ten işleyicileri çağırırken bind
kullanmanız gerekir.
dekoratörler
Dekoratörler, ES7'nin kullanışlı bir özelliğidir. Bir işlevin veya sınıfın davranışını başka bir işlevin içine sararak artırmanıza izin verirler. Örneğin, bazı bileşenlerinizde aynı değişiklik işleyicisine sahip olmak istediğinizi, ancak miras antipattern'e bağlı kalmak istemediğinizi varsayalım. Bunun yerine bir sınıf dekoratörü kullanabilirsiniz. Dekoratörü şu şekilde tanımlayalım:
addChangeHandler: function(target) { target.prototype.changeHandler = function(key, attr, event) { var state = {}; state[key] = this.state[key] || {}; state[key][attr] = event.currentTarget.value; this.setState(state); }; return target; }
Burada önemli olan, addChangeHandler
işlevinin changeHandler
işlevini hedef sınıfın prototipine eklemesidir.
Dekoratörü uygulamak için şunu yazabiliriz:
MyClass = changeHandler(MyClass)
veya daha zarif bir şekilde, ES7 sözdizimi ile:
@addChangeHandler class MyClass { ... }
changeHandler
işlevinin içeriğine gelince, React'in iki yönlü veri bağlamasının olmaması nedeniyle, React'teki girdilerle çalışmak sıkıcı olabilir. changeHandler
işlevi bunu kolaylaştırmaya çalışır. İlk parametre, giriş için bir veri nesnesi olarak hizmet edecek olan durum nesnesi üzerindeki bir key
belirtir. İkinci parametre, giriş alanındaki değerin kaydedileceği niteliktir. Bu iki parametre, bind
anahtar sözcüğü kullanılarak JSX'ten ayarlanır.
@addChangeHandler class LoginInput extends React.Component { constructor(props) { super(props); this.state = { login: {} }; } render() { return ( <input type='text' value={this.state.login.username} onChange={this.changeHandler.bind(this, 'login', 'username')} /> <input type='password' value={this.state.login.password} onChange={this.changeHandler.bind(this, 'login', 'password')} /> ) } }
Kullanıcı kullanıcı adı alanını değiştirdiğinde, değeri, daha fazla özel işleyici tanımlamaya gerek kalmadan this.state.login.username
'ye kaydedilir.
Ok Fonksiyonları
JavaScript'in dinamik this
bağlamı, geliştiriciler için sürekli bir acı olmuştur, çünkü biraz sezgisel olmayan bir şekilde, iç içe geçmiş bir işlevin this
bağlamı, bir sınıf içinde bile global olarak sıfırlanır. Bunu düzeltmek için, genellikle this
bazı dış kapsam değişkenlerine (genellikle _this
) kaydetmek ve iç işlevlerde kullanmak gerekir:
class DirectorsStore { onFetch(directors) { var _this = this; this.directorsHash = {}; directors.forEach(function(x){ _this.directorsHash[x._id] = x; }) } }
Yeni ES6 sözdizimi ile function(x){
, (x) => {
olarak yeniden yazılabilir. Bu "ok" yöntemi tanımı, this
yalnızca dış kapsama doğru bir şekilde bağlamakla kalmaz, aynı zamanda oldukça kısadır, bu da çok sayıda eşzamansız kod yazarken kesinlikle önemlidir.
onFetch(directors) { this.directorsHash = {}; directors.forEach((x) => { this.directorsHash[x._id] = x; }) }
Tahsisleri İmha Etme
ES6'da tanıtılan yıkım atamaları, atamanın sol tarafında bileşik bir nesneye sahip olmanızı sağlar:
var o = {p: 42, q: true}; var {p, q} = o; console.log(p); // 42 console.log(q); // true
Bu güzel ama React'te bize gerçekten nasıl yardımcı oluyor? Aşağıdaki örneği göz önünde bulundurun:
function makeRequest(url, method, params) { var config = { url: url, method: method, params: params }; ... }
İmha etme ile birkaç tuş vuruşunu kaydedebilirsiniz. Anahtar değişmezi {url, method, params}
, kapsamdan anahtarlarla aynı ada sahip değerlere otomatik olarak atanır. Bu deyim oldukça sık kullanılır ve tekrarı ortadan kaldırmak kodu daha az hataya açık hale getirir.
function makeRequest(url, method, params) { var config = {url, method, params}; ... }
Yıkım, bir modülün yalnızca bir alt kümesini yüklemenize de yardımcı olabilir:
const {clone, assign} = require('lodash'); function output(data, optional) { var payload = clone(data); assign(payload, optional); }
Bağımsız Değişkenler: Varsayılan, Dinlenme ve Yayılma
ES6'da işlev argümanları daha güçlüdür. Son olarak, varsayılan argümanı ayarlayabilirsiniz:
function http(endpoint, method='GET') { console.log(method) ... } http('/api') // GET
Kullanışsız arguments
nesnesiyle uğraşmaktan bıktınız mı? Yeni spesifikasyonla, argümanların geri kalanını bir dizi olarak alabilirsiniz:
function networkAction(context, method, ...rest) { // rest is an array return method.apply(context, rest); }
apply()
öğesini çağırmayı sevmiyorsanız, bir diziyi işlev bağımsız değişkenlerine yayabilirsiniz :
myArguments = ['foo', 'bar', 123]; myFunction(...myArguments);
Jeneratörler ve Zaman Uyumsuz İşlevler
ES6, JavaScript oluşturucuları tanıttı. Bir üreteç, temel olarak, yürütmesi duraklatılabilen ve daha sonra durumunu hatırlayarak devam ettirilebilen bir JavaScript işlevidir. yield
anahtar sözcüğüyle her karşılaşıldığında, yürütme duraklatılır ve yield
bağımsız değişkeni, çağıran nesneye geri iletilir:
function* sequence(from, to) { console.log('Ready!'); while(from <= to) { yield from++; } }
İşte bu jeneratörün çalışırken bir örneği:
> var cursor = sequence(1,3) Ready! > cursor.next() { value: 1, done: false } > cursor.next() { value: 2, done: false } > cursor.next() { value: 3, done: false } > cursor.next() { value: undefined, done: true }
Jeneratör fonksiyonunu çağırdığımızda, ilk yield
olana kadar çalışır ve sonra durur. next()
öğesini çağırdıktan sonra, ilk değeri döndürür ve yürütmeye devam eder. Her yield
başka bir değer döndürür, ancak üçüncü çağrıdan sonra oluşturucu işlevi sona erer ve next()
öğesine yapılan her çağrı { value: undefined, done: true }
döndürür.
Elbette, üreteçlerin amacı ayrıntılı sayısal diziler oluşturmak değildir. Heyecan verici kısım, asenkron program akışını kontrol etmek ve sonunda bu sinir bozucu geri arama işlevlerinden kurtulmak için kullanılabilen, işlev yürütmeyi durdurma ve sürdürme yetenekleridir.
Bu fikri göstermek için önce asenkron bir fonksiyona ihtiyacımız var. Genellikle, bazı G/Ç işlemlerimiz olurdu, ancak basitlik için setTimeout
kullanalım ve bir söz verelim. (ES6'nın JavaScript'e yerel vaatler de sunduğunu unutmayın.)
function asyncDouble(x) { var deferred = Promise.defer(); setTimeout(function(){ deferred.resolve(x*2); }, 1000); return deferred.promise; }
Ardından, bir tüketici işlevine ihtiyacımız var:
function consumer(generator){ var cursor = generator(); var value; function loop() { var data = cursor.next(value); if (data.done) { return; } else { data.value.then(x => { value = x; loop(); }) } } loop(); }
Bu işlev herhangi bir üreteci argüman olarak alır ve yield
değerler olduğu sürece next()
öğesini çağırmaya devam eder. Bu durumda, elde edilen değerler vaatlerdir ve bu nedenle vaatlerin çözülmesini beklemek ve iç içe işlevler arasında döngü elde etmek için loop()
ile özyinelemeyi kullanmak gerekir.
Dönüş değeri, then()
işleyicisinde çözümlenir ve dış kapsamda tanımlanan ve next(value)
çağrısına aktarılacak olan value
öğesine iletilir. Bu çağrı, değeri karşılık gelen verim ifadesinin bir sonucu yapar. Bu, artık herhangi bir geri arama olmadan eşzamansız olarak yazabildiğimiz anlamına gelir:
function* myGenerator(){ const data1 = yield asyncDouble(1); console.log(`Double 1 = ${data1}`); const data2 = yield asyncDouble(2); console.log(`Double 2 = ${data2}`); const data3 = yield asyncDouble(3); console.log(`Double 3 = ${data3}`); } consumer(myGenerator);
myGenerator
, her bir yield
duraklatılacak ve tüketicinin çözülen taahhüdü sunmasını bekleyecektir. Gerçekten de hesaplanan sayıların birer saniyelik aralıklarla konsolda göründüğünü göreceğiz.
Double 1 = 2 Double 2 = 4 Double 3 = 6
Bu, temel konsepti gösterir, ancak bu kodu üretimde kullanmanızı önermiyorum. Bunun yerine, co gibi iyi test edilmiş bir kitaplık seçin. Bu, hata işleme de dahil olmak üzere verimlerle kolayca zaman uyumsuz kod yazmanıza olanak tanır:
co(function *(){ var a = yield Promise.resolve(1); console.log(a); var b = yield Promise.resolve(2); console.log(b); var c = yield Promise.resolve(3); console.log(c); }).catch(function(err){ console.error(err.stack); });
Bu nedenle, bu örnek, ES6 oluşturucuları kullanarak geri aramalar olmadan asenkron kodun nasıl yazılacağını gösterir. await
, zaman async
ve bekleme anahtar sözcüklerini tanıtarak ve bir oluşturucu kitaplığına olan ihtiyacı tamamen ortadan kaldırarak bu yaklaşımı bir adım daha ileri götürür. Bu yetenekle, önceki örnek şöyle görünür:
async function (){ try { var a = await Promise.resolve(1); console.log(a); var b = await Promise.resolve(2); console.log(b); var c = await Promise.resolve(3); console.log(c); } catch (err) { console.error(err.stack); } };
Bence bu, JavaScript'te asenkron kodla çalışmanın acısını ortadan kaldırıyor. Sadece React'te değil, başka her yerde de.
Jeneratörler sadece daha kısa ve anlaşılır olmakla kalmaz, aynı zamanda geri aramalarla uygulanması çok zor olan teknikleri kullanmamıza da izin verir. Jeneratör iyiliğinin göze çarpan bir örneği, Node.js için koa ara katman yazılımı kitaplığıdır. Express'in yerini almayı hedefliyor ve bu amaca yönelik olarak tek bir öldürücü özellik ile geliyor: Ara katman yazılımı zinciri yalnızca aşağı akış (istemci isteğiyle) değil, aynı zamanda yukarı akış , sunucunun yanıtında daha fazla değişiklik yapılmasına izin verir. Aşağıdaki koa sunucusu örneğini düşünün:
// Response time logger middleware app.use(function *(next){ // Downstream var start = new Date; yield next; // Upstream this.body += ' World'; var ms = new Date - start; console.log('%s %s - %s', this.method, this.url, ms); }); // Response handler app.use(function *(){ this.body = 'Hello'; }); app.listen(3000);

Yanıt ara yazılımı, yanıt gövdesini ayarlayan yanıt işleyicisine aşağı yield
yield
sonra), this.body
daha fazla değiştirilmesine ve ayrıca olası zaman kaydı gibi diğer işlevlere izin verilir. çünkü yukarı akış ve aşağı akış aynı işlev bağlamını paylaşır. Bu, aynı şeyi başarma girişiminin şu şekilde sonuçlanacağı Express'ten çok daha güçlüdür:
var start; // Downstream middleware app.use(function(req, res, next) { start = new Date; next(); // Already returned, cannot continue here }); // Response app.use(function (req, res, next){ res.send('Hello World') next(); }); // Upstream middleware app.use(function(req, res, next) { // res already sent, cannot modify var ms = new Date - start; console.log('%s %s - %s', this.method, this.url, ms); next(); }); app.listen(3000);
Muhtemelen burada neyin yanlış olduğunu zaten anlayabilirsiniz; "genel" bir start
değişkeni kullanmak, bir yarış koşuluna neden olacak ve eşzamanlı isteklerle saçma sapan sonuçlar verecektir. Çözüm, açık olmayan bir geçici çözümdür ve yukarı akıştaki yanıtı değiştirmeyi unutabilirsiniz.
Ayrıca, koa kullanırken, jeneratör eşzamansız iş akışını ücretsiz olarak alacaksınız:
app.use(function *(){ try { const part1 = yield fs.readFile(this.request.query.file1, 'utf8'); const part2 = yield fs.readFile(this.request.query.file2, 'utf8'); this.body = part1 + part2; } catch (err) { this.status = 404 this.body = err; } }); app.listen(3000);
Express'te bu küçük örneğin yeniden yaratılmasıyla ilgili vaatleri ve geri aramaları hayal edebilirsiniz.
Tüm bu Node.js konuşmalarının React ile nasıl bir ilgisi var? Node, React için uygun bir arka uç düşünüldüğünde ilk tercihtir. Node, JavaScript ile de yazıldığından, arka uç ve ön uç arasında kod paylaşımını destekleyerek izomorfik React web uygulamaları oluşturmamıza olanak tanır. Ancak, bu konuda daha sonra.
Akı Kitaplığı
React, şekillendirilebilir görünüm bileşenleri oluşturmakta harikadır, ancak tüm uygulama genelinde verileri ve durumu yönetmek için bir yola ihtiyacımız var. React'in en iyi Flux uygulama mimarisiyle tamamlandığı neredeyse evrensel olarak kabul edilmiştir. Flux'ta tamamen yeniyseniz, hızlı bir yenileme yapmanızı öneririm.
Evrensel olarak kabul edilmeyen şey, birçok Flux uygulamasından hangisinin seçileceğidir. Facebook Flux bariz bir seçim olurdu, ancak çoğu insan için çok ayrıntılı. Alternatif uygulamalar, çoğunlukla, yapılandırma üzerinden geleneksel yaklaşımla ve ayrıca daha yüksek dereceli bileşenler, sunucu tarafı oluşturma vb. için bazı kolaylık işlevleriyle gerekli standart metin miktarını azaltmaya odaklanır. Çeşitli popülerlik ölçütlerine sahip en iyi yarışmacılardan bazıları burada görülebilir. Alt, Reflux, Flummox, Fluxxor ve Marty.js'ye baktım.
Doğru kitaplığı seçme yöntemim hiçbir şekilde objektif değil, ancak yine de yardımcı olabilir. Fluxxor, kontrol ettiğim ilk kitaplıklardan biriydi, ama şimdi biraz eski görünüyor. Marty.js ilginçtir ve birçok özelliğe sahiptir, ancak yine de çok sayıda ortak bilgi içerir ve bazı işlevler gereksiz görünmektedir. Reflü harika görünüyor ve biraz çekiş gücü var, ancak yeni başlayanlar için biraz zor geliyor ve ayrıca uygun belgelerden yoksun. Flummox ve Alt birbirine çok benzer, ancak Alt'ta daha az standart, çok aktif geliştirme, güncel belgeler ve yardımcı bir Slack topluluğu var gibi görünüyor. Bu nedenle, Alt'ı seçtim.
Alternatif Akı
Alt ile Flux iş akışı, gücünden hiçbir şey kaybetmeden çok daha basit hale gelir. Facebook'un Flux belgeleri, gönderici hakkında çok şey söylüyor, ancak bunu görmezden gelmekte özgürüz, çünkü Alt'ta, gönderici sözleşmeye göre eylemlere dolaylı olarak bağlıdır ve genellikle herhangi bir özel kod gerektirmez. Bu bize sadece mağazalar , eylemler ve bileşenler bırakır. Bu üç katman, MVC düşünce modeliyle güzel bir şekilde eşleşecek şekilde kullanılabilir: Mağazalar Modellerdir , eylemler Denetleyicilerdir ve bileşenler Görünümlerdir . Ana fark, Flux modelinin merkezinde yer alan tek yönlü veri akışıdır; bu, denetleyicilerin (eylemlerin) görünümleri (bileşenleri) doğrudan değiştiremeyeceği, bunun yerine yalnızca görünümlerin pasif olarak bağlı olduğu model (depolama) değişikliklerini tetikleyebileceği anlamına gelir. Bu, bazı aydınlanmış Angular geliştiricileri için zaten en iyi uygulamaydı.
İş akışı aşağıdaki gibidir:
- Bileşenler eylemleri başlatır.
- Mağazalar eylemleri dinler ve verileri günceller.
- Bileşenler depolara bağlıdır ve veriler güncellendiğinde yeniden oluşturulur.
Hareketler
Alt Flux kitaplığını kullanırken, eylemler genellikle iki şekilde gelir: otomatik ve manuel. Otomatik eylemler, createActions işlevi kullanılarak generateActions
ve bunlar doğrudan göndericiye gider. Manuel yöntemler, eylem sınıflarınızın yöntemleri olarak tanımlanır ve ek bir payload ile göndericiye gidebilirler. Otomatik eylemlerin en yaygın kullanım durumu, uygulamadaki bazı olaylar hakkında mağazalara bilgi vermektir. Manuel eylemler, diğer şeylerin yanı sıra, sunucu etkileşimleriyle başa çıkmanın tercih edilen yoludur.
Yani REST API çağrıları eylemlere aittir. Tam iş akışı aşağıdaki gibidir:
- Bileşen bir eylemi tetikler.
- Eylem oluşturucu, eşzamansız bir sunucu isteği çalıştırır ve sonuç, dağıtımcıya bir yük olarak gider.
- Mağaza eylemi dinler, ilgili eylem işleyicisi sonucu bir argüman olarak alır ve mağaza durumunu buna göre günceller.
AJAX istekleri için, diğer şeylerin yanı sıra JSON verileri ve başlıklarıyla sorunsuz bir şekilde ilgilenen axios kitaplığını kullanabiliriz. Sözler veya geri aramalar yerine await
zaman async
/bekleme modelini kullanabiliriz. POST
yanıtı durumu 2XX değilse, bir hata atılır ve ya döndürülen verileri ya da alınan hatayı göndeririz.
Alt iş akışının basit bir örneği için bir giriş sayfasına bakalım. Oturum kapatma eyleminin özel bir şey yapmasına gerek yoktur, yalnızca mağazayı bilgilendirin, böylece otomatik olarak oluşturabiliriz. Oturum açma eylemi manueldir ve oturum açma verilerini eylem oluşturucu için bir parametre olarak bekler. Sunucudan yanıt aldıktan sonra ya başarı verilerini göndeririz ya da bir hata atılırsa alınan hatayı göndeririz.
class LoginActions { constructor() { // Automatic action this.generateActions('logout'); } // Manual action async login(data) { try { const response = await axios.post('/auth/login', data); this.dispatch({ok: true, user: response.data}); } catch (err) { console.error(err); this.dispatch({ok: false, error: err.data}); } } } module.exports = (alt.createActions(LoginActions));
Mağazalar
Flux deposu iki amaca hizmet eder: Eylem işleyicileri vardır ve durumu taşır. Bunun nasıl çalıştığını görmek için giriş sayfası örneğimize devam edelim.
İki durum özniteliği ile LoginStore
oluşturalım: mevcut oturum açmış user
için user ve mevcut oturum açmayla ilgili error
için error. Ortak levhayı azaltma ruhuyla Alt, tek bir işlevle bindActions
tek bir sınıftaki tüm eylemlere bağlanmamıza izin verir.
class LoginStore { constructor() { this.bindActions(LoginActions); this.user = null; this.error = null; } ...
İşleyici adları, karşılık gelen eylem adının önüne eklenerek, kurala on
tanımlanır. Bu nedenle, login
eylemi onLogin
vb. tarafından gerçekleştirilir. Eylem adının ilk harfinin camelCase stilinde büyük harfle yazılacağını unutmayın. LoginStore
, ilgili eylemler tarafından çağrılan aşağıdaki işleyicilere sahibiz:
... onLogin(data) { if (data.ok) { this.user = data.user; this.error = null; router.transitionTo('home'); } else { this.user = null; this.error = data.error } } onLogout() { this.user = null; this.error = null; } }
Bileşenler
Depoları bileşenlere bağlamanın genel yolu, bir çeşit React karışımı kullanmaktır. Ama mikslerin modası geçtiği için başka bir yol olmalı. Yeni yaklaşımlardan biri, daha yüksek dereceli bileşenleri kullanmaktır. Bileşenimizi alıyoruz ve mağazaları dinlemeyi ve yeniden oluşturmayı çağırmayı halledecek bir sarmalayıcı bileşenin içine koyuyoruz. Bileşenimiz, mağazanın durumunu props
olarak alacak. Bu yaklaşım, kodumuzu son zamanlarda moda olan akıllı ve aptal bileşenler halinde düzenlemeye de yardımcı olur. Alt için, bileşen sarmalayıcı AltContainer
tarafından uygulanır:
export default class Login extends React.Component { render() { return ( <AltContainer stores={{LoginStore: LoginStore}}> <LoginPage/> </AltContainer> )} }
LoginPage
bileşenimiz ayrıca daha önce tanıtılan changeHandler
dekoratörünü kullanır. LoginStore
alınan veriler, başarısız bir oturum açma durumunda hataları görüntülemek için kullanılır ve yeniden oluşturma AltContainer
tarafından halledilir. Oturum açma düğmesine tıklamak oturum login
işlemini yürütür ve Alt flux iş akışını tamamlar:
@changeHandler export default class LoginPage extends React.Component { constructor(props) { super(props); this.state = { loginForm: {} }; } login() { LoginActions.login(this.state.loginForm) } render() { return ( <Alert>{{this.props.LoginStore.error}}</Alert> <Input label='Username' type='text' value={this.state.login.username} onChange={this.changeHandler.bind(this, 'loginForm', 'username')} /> <Input label='Password' type='password' value={this.state.login.password} onChange={this.changeHandler.bind(this, 'loginForm', 'password')} /> <Button onClick={this.login.bind(this)}>Login</Button> )} }
izomorfik işleme
İzomorfik web uygulamaları, geleneksel tek sayfalı uygulamaların en büyük işlerinden bazılarını çözdükleri için bugünlerde sıcak bir konu. Bu uygulamalarda işaretleme, tarayıcıda JavaScript tarafından dinamik olarak oluşturulur. Sonuç, içeriğin JavaScript'in kapalı olduğu istemciler, özellikle de arama motoru web tarayıcıları tarafından kullanılamamasıdır. Bu, web sayfanızın dizine eklenmediği ve arama sonuçlarında görünmediği anlamına gelir. Bunu aşmanın yolları var, ancak bunlar optimal olmaktan uzak. İzomorfik yaklaşım, sunucuda tek sayfalık bir uygulamanın istenen URL'sini önceden oluşturarak bu sorunu çözmeye çalışır. Node.js ile sunucuda JavaScript'e sahipsiniz, yani React sunucu tarafında da çalışabilir. Bu çok zor olmamalı, değil mi?
Engellerden biri, bazı Flux kitaplıklarının, özellikle de singleton kullananların, sunucu tarafı oluşturmada zorluk yaşamasıdır. Sunucunuza tekil Flux depolarınız ve birden çok eşzamanlı isteğiniz olduğunda, veriler karışacaktır. Bazı kitaplıklar bunu Flux örneklerini kullanarak çözer, ancak bu, özellikle bu örnekleri kodunuzda aktarma ihtiyacı olmak üzere başka dezavantajlarla birlikte gelir. Alt, Flux örnekleri de sunar, ancak tek tonlarla sunucu tarafı oluşturma sorununu da çözmüştür; depoları her istekten sonra temizler, böylece her eşzamanlı istek temiz bir sayfa ile başlar.
Sunucu tarafı işleme işlevinin özü, React.renderToString
tarafından sağlanır. Tüm React ön uç uygulaması da sunucuda çalıştırılır. Bu şekilde, işaretlemeyi oluşturmak için istemci tarafı JavaScript'i beklememiz gerekmez; erişilen URL için sunucuda önceden oluşturulmuştur ve tarayıcıya HTML olarak gönderilir. İstemci JavaScript çalıştığında, sunucunun kaldığı yerden devam eder. Bunu desteklemek için Alt ile kullanılması amaçlanan Iso kitaplığını kullanabiliriz.
İlk olarak, alt.bootstrap kullanarak sunucuda alt.bootstrap
. Flux depolarını işleme için verilerle önceden doldurmak mümkündür. İstemci tarafı Router
işlevselliği olan hangi URL için hangi bileşenin oluşturulacağına da karar vermek gerekir. alt
öğesinin singleton sürümünü kullanıyoruz, bu nedenle her oluşturmadan sonra, mağazaların başka bir istek için temiz olması için alt.flush()
yapmamız gerekiyor. iso
eklentisini kullanarak, Flux'un durumu HTML işaretlemesine göre serileştirilir, böylece müşteri nereden alacağını bilir:
// We use react-router to run the URL that is provided in routes.jsx var getHandler = function(routes, url) { var deferred = Promise.defer(); Router.run(routes, url, function (Handler) { deferred.resolve(Handler); }); return deferred.promise; }; app.use(function *(next) { yield next; // We seed our stores with data alt.bootstrap(JSON.stringify(this.locals.data || {})); var iso = new Iso(); const handler = yield getHandler(reactRoutes, this.request.url); const node = React.renderToString(React.createElement(handler)); iso.add(node, alt.flush()); this.render('layout', {html: iso.render()}); });
İstemci tarafında, sunucu durumunu alırız ve verilerle alt
önyükleme yaparız. Ardından, sunucu tarafından oluşturulan işaretlemeyi gerektiği gibi güncelleyecek olan hedef kapsayıcıda Router
ve React.render
çalıştırırız.
Iso.bootstrap(function (state, _, container) { // Bootstrap the state from the server alt.bootstrap(state) Router.run(routes, Router.HistoryLocation, function (Handler, req) { let node = React.createElement(Handler) React.render(node, container) }) })
Sevimli!
Faydalı Ön Uç Kitaplıkları
React ekosistemine yönelik bir rehber, özellikle React ile iyi çalışan birkaç ön uç kitaplığından bahsetmeden tamamlanmış sayılmaz. Bu kitaplıklar, hemen hemen her web uygulamasında bulunan en yaygın görevlerin üstesinden gelir: CSS mizanpajları ve kapsayıcılar, biçimlendirilmiş formlar ve düğmeler, doğrulamalar, tarih seçme vb. Bu sorunlar zaten çözülmüşken tekerleği yeniden icat etmenin bir anlamı yok.
React-Bootstrap
Twitter's Bootstrap framework has become commonplace since it is of immense help to every web developer who does not want to spend a ton of time working in CSS. In the prototyping phase especially, Bootstrap is indispensable. To leverage the power of bootstrap in a React application, it's good to use it with React-Bootstrap, which comes with nice React syntax and re-implements Bootstrap's jQuery plugins using native React components. The resulting code is terse and easy to understand:
<Navbar brand='React-Bootstrap'> <Nav> <NavItem eventKey={1} href='#'>Link</NavItem> <NavItem eventKey={2} href='#'>Link</NavItem> <DropdownButton eventKey={3} title='Dropdown'> <MenuItem eventKey='1'>Action</MenuItem> <MenuItem eventKey='2'>Another action</MenuItem> <MenuItem eventKey='3'>Something else here</MenuItem> <MenuItem divider /> <MenuItem eventKey='4'>Separated link</MenuItem> </DropdownButton> </Nav> </Navbar>
Personally, I cannot escape the feeling that this is what HTML should always have been like.
If you want to use Bootstrap source with Webpack, consider using less-loader or bootstrap-sass-loader, depending on the preprocessor you prefer. It will allow you to easily customize which Bootstrap components to include, and also allow the usage of LESS or SASS global variables in your CSS code.
React Router
React Router has become the de-facto standard for routing in React. It allows nested routing, support for redirections, plays nicely with isomorphic rendering, and has a simple JSX-based syntax:
<Router history={new BrowserHistory}> <Route path="/" component={App}> <Route path="about" name="about" component={About}/> <Route path="users" name="users" component={Users} indexComponent={RecentUsers}> <Route path="/user/:userId" name="user" component={User}/> </Route> <Route path="*" component={NoMatch}/> </Route> </Router>
React Router also provides a Link
component that you can use for navigation in your application, specifying only the route name:
<nav> <Link to="about">About</Link> <Link to="users">Users</Link> </nav>
There is even a library for integration with React-Bootstrap, so if you are using Bootstrap's components and don't feel like setting the active
class on them manually all the time, you can use react-router-bootstrap and write code like this:
<Nav> <NavItemLink to="about">About</NavItemLink> <NavItemLink to="users">Users</NavItemLink> </Nav>
No additional setup is necessary. Active links will take care of themselves.
Formsy-React
Working with forms can be tedious, so let's get some help from the formsy-react library, which will help us manage validations and data models. The Formsy-React library, strangely enough, does not include the actual form inputs because users are encouraged to write their own (which is great). But if you are content with the common ones, just use formsy-react-components. Bootstrap classes are included:
import Formsy from 'formsy-react'; import {Input} from 'formsy-react-components'; export default class FormsyForm extends React.Component { enableButton() { this.setState({canSubmit: true}); } disableButton() { this.setState({canSubmit: true}); } submit(model) { FormActions.saveEmail(model.email); } render() { return ( <Formsy.Form onValidSubmit={this.submit} onValid={this.enableButton} onInvalid={this.disableButton}> <Input name="email" validations="isEmail" validationError="This is not a valid email" required/> <button type="submit" disabled={!this.state.canSubmit}>Submit</button> </Formsy.Form> )} }
Calendar and Typeahead
Calendar and typeahead are icing on the cake of every UI toolkit. Sadly, these two components were removed from Bootstrap 3, probably because they are too specialized for a general purpose CSS framework. Luckily, I've been able to find worthy replacements in react-pikaday and react-select. I've tested more than 10 libraries, and these two came out as the best. They are dead easy to use, as well:
import Pikaday from 'react-pikaday'; import Select from 'react-select'; export default class CalendarAndTypeahead extends React.Component { constructor(props){ super(props); this.options = [ { value: 'one', label: 'One' }, { value: 'two', label: 'Two' } ]; } dateChange(date) { this.setState({date: date}); }, selectChange(selected) { this.setState({selected: selected}); }, render() { return ( <Pikaday value={this.state.date} onChange={this.dateChange} /> <Select name="form-field-name" value={this.state.selected} options={this.options} onChange={selectChange} /> )} }
Conclusion - React.JS
In this article I've presented libraries and techniques that I consider some of the most productive in current web development. Some of them are React-specific, but due to React's open nature, many of them are usable in other environments as well. Technological progress is sometimes hindered by fear of the newest stuff, so I hope this article will help to dissipate doubts concerning React, Flux and the newest features in ECMAScript.
If you are interested, you can take a look at my example application built with these technologies. The source code is available on GitHub.
Okuduğunuz için teşekkürler!