Reaksiyon fonksiyonel durumsuz bileşen, PureComponent, Bileşen; farklar nelerdir ve ne zaman kullanmalıyız?


189

Dan olduğunu bilmek geldi v15.3.0 tepki , yeni bir temel sınıf olarak adlandırılan sahip PureComponent ile genişletmek için PureRenderMixin yerleşik. Anladığım kadarıyla, kaputun altında bu, iç kısımdaki sahne malzemelerinin sığ bir karşılaştırmasını kullanıyor shouldComponentUpdate.

Şimdi bir React bileşenini tanımlamanın 3 yolu var:

  1. Hiçbir sınıfı genişletmeyen işlevsel durumsuz bileşen
  2. PureComponentSınıfı genişleten bir bileşen
  3. ComponentSınıfı genişleten normal bir bileşen

Bir süre önce vatansız bileşenleri Saf Bileşenler, hatta Aptal Bileşenler olarak adlandırıyorduk. Görünüşe göre "saf" kelimesinin tüm tanımı artık React'ta değişti.

Bu üç arasındaki temel farkları anlasam da, ne zaman seçeceğimi hala bilmiyorum . Ayrıca her birinin performans etkileri ve değişimleri nelerdir?


Güncelleme :

Bunlar netleştirilmeyi beklediğim soru:

  • Basit bileşenlerimi işlevsel (basitlik uğruna) veya PureComponentsınıf (performans uğruna) olarak tanımlamalı mıyım ?
  • Kaybettiğim basitlik için gerçek bir takas elde ettiğim performans artışı mı?
  • Daha iyi performans için Componenther zaman kullanabileceğim zaman normal sınıfı genişletmem gerekir mi PureComponent?

Yanıtlar:


315

Bileşenlerimizin amacına / boyutuna / desteklerine / davranışına göre bu üçü arasında nasıl seçim yaparsınız?

Özel bir yöntemden React.PureComponentveya bu yöntemle genişletmenin performans sonuçları vardır. Vatansız fonksiyonel bileşenleri kullanmak "mimari" bir seçimdir ve kutudan çıktığı haliyle performans avantajı yoktur (henüz).React.ComponentshouldComponentUpdate

  • Kolayca yeniden kullanılması gereken basit, yalnızca sunumsal bileşenler için durum bilgisi olmayan fonksiyonel bileşenleri tercih edin. Bu şekilde, gerçek uygulama mantığından ayrıldıklarından, test edilmeleri son derece kolay olduklarından ve beklenmedik yan etkilere sahip olmadıklarından emin olabilirsiniz. İstisna, bir nedenden ötürü çok fazla şeye sahipseniz veya gerçekten oluşturma yöntemlerini optimize etmeniz gerekiyorsa ( shouldComponentUpdatevatansız bir fonksiyonel bileşen için tanımlayamadığınız için).

  • PureComponentÇıktınızın basit sahne / duruma ("basit" anlamına gelir) genişletin (PureComponent sığ bir karşılaştırma gerçekleştirdiğinden iç içe veri yapıları yoktur) VE bazı performans geliştirmelerine ihtiyacınız var / alabilirsiniz.

  • Bir sonraki / mevcut sahne ve durum arasında özel karşılaştırma mantığı gerçekleştirerek performans kazançlarına ihtiyacınız varsa kendiniz genişletin Componentve uygulayın shouldComponentUpdate. Örneğin, lodash # isEqual kullanarak hızlı bir şekilde derin bir karşılaştırma yapabilirsiniz:

    class MyComponent extends Component {
        shouldComponentUpdate (nextProps, nextState) {
            return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
        }
    }
    

Ayrıca, kendiniz uygulamak shouldComponentUpdateveya genişletmek PureComponentoptimizasyonlardır ve her zamanki gibi bunu yalnızca performans sorunlarınız varsa ( erken optimizasyonlardan kaçının ) araştırmaya başlamalısınız . Genel bir kural olarak, bu optimizasyonları her zaman uygulama çalışır durumda ve özelliklerin çoğu zaten uygulandıktan sonra yapmaya çalışırım. Gerçekte yol aldıklarında performans sorunlarına odaklanmak çok daha kolay.

Daha fazla detay

Fonksiyonel vatansız bileşenler:

Bunlar sadece bir fonksiyon kullanılarak tanımlanır. Durum bilgisi olmayan bir bileşen için dahili bir durum olmadığından, çıktı (işlenenler) yalnızca bu işleve giriş olarak verilen desteklere bağlıdır.

Artıları:

  • React'ta bir bileşeni tanımlamanın mümkün olan en basit yolu. Herhangi bir durumu yönetmeniz gerekmiyorsa, neden sınıflar ve mirasla uğraşasınız ki? Bir işlev ve sınıf arasındaki ana farklardan biri, işlevle çıktının yalnızca girdiye bağlı olduğundan emin olmanızdır (önceki yürütmelerin herhangi bir geçmişine değil).

  • İdeal olarak uygulamanızda olabildiğince çok vatansız bileşenlere sahip olmayı hedeflemelisiniz, çünkü bu normalde mantığınızı görünüm katmanının dışına taşıdığınız ve redux gibi bir şeye taşıdığınız anlamına gelir; (test edilmesi çok daha kolay, daha fazla kullanılabilir vb.).

Eksileri:

  • Yaşam döngüsü yöntemi yok. Sizin componentDidMountve diğer arkadaşlarınızı tanımlamanın bir yolu yok . Normalde bunu, tüm çocukları vatansız olanlara dönüştürebilmeniz için hiyerarşide daha büyük bir üst bileşen içinde yaparsınız.

  • Yeniden oluşturma gerektiğinde manuel olarak kontrol etmenin bir yolu yoktur, çünkü tanımlayamazsınız shouldComponentUpdate. Yeniden oluşturma işlemi, bileşen her yeni sahne aldığında gerçekleşir (sığ karşılaştırmanın yolu yoktur). Gelecekte, React vatansız bileşenleri otomatik olarak optimize edebilir, çünkü şimdi kullanabileceğiniz bazı kütüphaneler var. Vatansız bileşenler sadece işlevler olduğundan, temelde "işlev belleği" nin klasik problemidir.

  • Referanslar desteklenmiyor: https://github.com/facebook/react/issues/4936

PureComponent sınıfını genişleten bir bileşen VS Bileşen sınıfını genişleten normal bir bileşen:

Bir için kullanılan tepki PureRenderMixinkullanmakta tanımlanan bir sınıfa eklemek olabilir React.createClasssözdizimi. Mixin, bir shouldComponentUpdateşeylerin değişip değişmediğini kontrol etmek için bir sonraki sahne ile bir sonraki durum arasında sığ bir karşılaştırma yapmayı tanımlar . Hiçbir şey değişmezse, yeniden oluşturma gerçekleştirmeye gerek yoktur.

ES6 sözdizimini kullanmak istiyorsanız, mixins kullanamazsınız. Kolaylık sağlamak için React PureComponentkullanmak yerine kalıtım yoluyla alabileceğiniz bir sınıf tanıttı Component. PureComponentsadece shouldComponentUpdateaynı şekilde uygular PureRendererMixin. Çoğunlukla kullanışlı bir şeydir, bu yüzden bunu kendiniz uygulamak zorunda kalmazsınız, çünkü mevcut / sonraki durum ve sahne arasında sığ bir karşılaştırma muhtemelen bazı hızlı performans kazançları sağlayabilecek en yaygın senaryodur.

Misal:

class UserAvatar extends Component {
    render() {
       return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
    }
} 

Gördüğünüz gibi çıktı props.imageUrlve props.username. Bir üst bileşende <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />aynı sahne ile oluşturursanız render, çıktı tam olarak aynı olsa bile React her seferinde çağrı yapacaktır. React'in dom farkını uyguladığını unutmayın, bu nedenle DOM aslında güncellenmez. Yine de, dom farkını gerçekleştirmek pahalı olabilir, bu nedenle bu senaryoda bir atık olacaktır.

Eğer UserAvatarbileşen uzanır PureComponentyerine, karşılaştırma sığ yapılır. Ve props ve nextProps aynı olduğundan render, hiç çağrılmaz.

React'te "pure" tanımı ile ilgili notlar:

Genel olarak, "saf işlev", aynı girdi göz önüne alındığında her zaman aynı sonucu değerlendiren bir işlevdir. Çıktı (React için, renderyöntem tarafından döndürülen budur ) herhangi bir geçmişe / duruma bağlı değildir ve herhangi bir yan etkisi yoktur (işlevin dışındaki "dünya" yı değiştiren işlemler).

React'te, vatansız bileşenler, asla çağırmayan this.setStateve kullanmayan bir bileşeni "vatansız" olarak adlandırırsanız, yukarıdaki tanıma göre mutlaka saf bileşenler değildir this.state.

Aslında, a'da, PureComponentyaşam döngüsü yöntemleri sırasında hala yan etkiler yapabilirsiniz. Örneğin, içine bir ajax isteği gönderebilir componentDidMountveya içindeki bir div'in yüksekliğini dinamik olarak ayarlamak için bazı DOM hesaplamaları yapabilirsiniz render.

"Aptal bileşenler" tanımının daha "pratik" bir anlamı vardır (en azından benim anlayışımda): aptal bir bileşen, "ana bileşen tarafından sahne aracılığıyla ne yapılacağını" anlatır ve bir şeylerin nasıl yapılacağını bilmez ama sahne kullanır yerine geri aramalar.

"Akıllı" bir örnek AvatarComponent:

class AvatarComponent extends Component {
    expandAvatar () {
        this.setState({ loading: true });
        sendAjaxRequest(...).then(() => {
            this.setState({ loading: false });
        });
    }        

    render () {
        <div onClick={this.expandAvatar}>
            <img src={this.props.username} />
        </div>
    }
}

"Aptal" örneği AvatarComponent:

class AvatarComponent extends Component {
    render () {
        <div onClick={this.props.onExpandAvatar}>
            {this.props.loading && <div className="spinner" />}
            <img src={this.props.username} />
        </div>
    }
}

Sonunda, "aptal", "vatansız" ve "saf" kelimelerin, çoğunlukla kullanım durumunuza bağlı olarak bazen üst üste gelebilecek, ancak zorunlu olarak değil, oldukça farklı kavramlar olduğunu söyleyebilirim.


1
Cevabınızı ve paylaştığınız bilgileri gerçekten takdir ediyorum. Ama asıl sorum ne zaman ne seçmeliyiz? . Cevabınızda bahsettiğinizle aynı örnek için nasıl tanımlamalıyım? İşlevsel vatansız bileşen (öyleyse neden?) Veya PureComponent genişletme (neden?) Veya Bileşen sınıfını genişletme (yine neden?) Olmalıdır. Bileşenlerimizin amacına / boyutuna / desteklerine / davranışına göre bu üçü arasında nasıl seçim yaparsınız ?
Yadhu Kiran

1
Sorun değil. İşlevsel vatansız bileşen için, bunun uygun olup olmayacağına karar verebileceğiniz bir artı / eksiler listesi var. Bu size ilk olarak cevap veriyor mu? Seçim sorusunu biraz daha ele almaya çalışacağım.
fabio.sussetto

2
Üst bileşenler hiç kullanılmasa bile, fonksiyonel bileşenler her zaman yeniden oluşturulur props. örnek .
AlexM

1
Bu, bir süredir okuduğum en kapsamlı cevaplardan biri. Harika iş. İlk cümle hakkında bir yorum: Genişlerken PureComponent, uygulamamalısınız shouldComponentUpdate(). Bunu gerçekten yaparsanız bir uyarı görmelisiniz.
jjramos

1
Gerçek performans kazançları PureComponentiçin, iç içe geçmiş nesne / dizi özelliklerine sahip olan bileşenler için kullanmaya çalışmalısınız . Tabii ki neler olduğunun farkında olmalısınız. Doğru anlıyorsam, sahne / durumu doğrudan mutasyona uğratmıyorsanız (React sizi uyarılarla yapmanızı engellemeye çalışır) veya harici bir kütüphane aracılığıyla, o zaman hemen hemen her yerde kullanmak PureComponentyerine iyi olmalısınız Component... istisna hariç kullanmak için daha hızlı olamayacağı çok basit bileşenlerin listesi - bkz. news.ycombinator.com/item?id=14418576
Matt Browne

28

ben bir tepkinin dahi değilim, ama benim anlayışımdan her bir bileşeni aşağıdaki durumlarda kullanabiliriz

  1. Durumsuz bileşen - bunlar yaşam döngüsüne sahip olmayan bileşendir, bu nedenle bu bileşenler, yalnızca bilgileri görüntüleyen ve gerçekleştirilecek herhangi bir eylemi olmayan metin listesini oluşturma gibi üst bileşenin yinelenen öğesinin oluşturulmasında kullanılmalıdır.

  2. Saf bileşen - bunlar yaşam döngüsüne sahip olan öğelerdir ve belirli bir sahne seti verildiğinde her zaman aynı sonucu döndürürler. Bu bileşenler, bir sonuç listesi veya karmaşık alt öğelere sahip olmayan belirli bir nesne verisi görüntülenirken ve yalnızca kendini etkileyen işlemleri gerçekleştirmek için kullanılabilir. kullanıcı kartlarının veya ürün kartlarının listesini gösteren (temel ürün bilgisi) ve kullanıcının gerçekleştirebileceği tek işlem ayrıntı sayfasını görüntülemek veya sepete eklemek için tıklamanızdır.

  3. Normal Bileşenler veya Karmaşık Bileşenler - Karmaşık bileşen terimini kullandım çünkü bunlar genellikle sayfa düzeyinde bileşenlerdir ve çok sayıda çocuk bileşeninden oluşur ve her çocuk kendi benzersiz şekilde davranabileceğinden,% 100 emin olamayacağınızdan verilen sonucu aynı sonucu verir. Dediğim gibi bunlar genellikle konteyner bileşenleri olarak kullanılmalıdır.


1
Bu yaklaşım işe yarayabilir, ancak büyük performans kazançlarını kaçırıyor olabilirsiniz. PureComponentKök düzeyindeki bileşenlerde ve hiyerarşinizin en üstündeki bileşenlerde kullanmak genellikle en büyük performans kazançlarını görebileceğiniz yerdir. Tabii ki, saf bileşenlerin doğru çalışması için doğrudan sahne değiştirmekten ve durumdan kaçınmanız gerekir, ancak nesneleri doğrudan mutasyona uğratmak React'te bir anti-kalıptır.
Matt Browne

5
  • React.Componentvarsayılan "normal" bileşendir. Bunları ve classanahtar kelimesini kullanarak beyan edersiniz extends React.Component. Bunları yaşam döngüsü yöntemleri, olay işleyicileri ve diğer yöntemlerle bir sınıf olarak düşünün.

  • React.PureComponent" ve" ifadeleri sığ bir karşılaştırma yapan bir işlevle React.Componentuygulayan ve . Bileşenin değişen iç içe geçmiş verileri veya durum bilgisi olduğunu ve yeniden oluşturmak istediğinizi biliyorsanız kullanmanız gerekir . Bu nedenle sahne olarak geçirdiğiniz veya durumunuzda ayarladığınız diziler veya nesneler yeniden oluşturulacak bileşenlere ihtiyacınız varsa harika değildir. shouldComponentUpdate()propsstateforceUpdate()

  • İşlevsel bileşenler, yaşam döngüsü işlevleri olmayan bileşenlerdir. Sözde vatansızlar, ama o kadar güzel ve temizler ki şimdi kancalarımız var (React 16.8'den beri), böylece hala bir durumunuz olabilir. Sanırım bunlar sadece "temiz bileşenler".

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.