Performansın Optimallaşdırılması

Daxildə, React, bir neçə ağıllı texnikadan istifadə edərək UI-ı yeniləmək üçün ağır DOM əməliyyatlarının sayını azaldır. Bir çox applikasiyada performansı xüsusi optimallaşdırmadan React-dən istifadə etdikdə itifadəçi interfeysi tez işləyəcək. Buna baxmayaraq React applikasiyasını tezləşdirməyin bir neçə yolu var.

Produksiya Qurulmasından İstifadə Edin

React applikasiyalarında performans problemləri olduqda minimallaşdırılmış produksiya qurulmasının istifadə edildiyindən əmin olun.

Normalda, React-də faydalı xəbərdarlıqlar var. Bu xəbərdarlıqlar development zamanı faydalıdır. Lakin, bu xəbərdarlıqlar React-i böyüdür və yavaşladır. Bu səbəbdən, applikasiyanı yerləşdirdikdə produksiya versiyasının istifadəsindən əmin olun.

Qurulma prosesinin düzgün işlədiyindən əmin olmadıqda Chrome üçün React Developer Alətlərindən istifadə edin. Produksiya modunda olan React səhifəsini ziyarət etdikdə alətin ikonu tünd fonda olacaq:

React-in produksiya versiyasını işlədən səhifədə React DevTools ikonu

Development modunda olan React səhifəsini ziyarət etdikdə isə alətin ikonu qırmızı fonda olacaq:

React-in development versiyasını işlədən səhifədə React DevTools ikonu

Applikasiyanı yazdıqda development modundan istifadə edin. Applikasiyanı istifadəçilər üçün yerləşdirdikdə isə produksiya modundan istifadə edin.

Applikasiyanın produksiya üçün qurulması üçün aşağıdakı təlimatlara baxın.

Create React App

Layihə Create React App üzərində qurulduqda aşağıdakı əmri icra etmək kifayətdir:

npm run build

Bu, applikasiyanın produksiya versiyasını layihənin build/ direktoriyasında yaradacaq.

Bunun yalnız produksiya zamanı işlədildiyini unutmayın. Normal development üçün npm start əmrindən istifadə edin.

Tək Fayl Qurulmaları

Biz, React və React DOM-un produksiyaya hazır versiyalarını tək fayl ilə təmin edirik:

<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

Produksiya üçün yalnız .production.min.js ilə bitən faylların uyğun olduğunu unutmayın.

Brunch

Ən səmərəli Brunch produksiya qurulması üçün terser-brunch plaginini yükləyin:

# npm işlədirsinizsə
npm install --save-dev terser-brunch

# Yarn işlədirsinizsə
yarn add --dev terser-brunch

Produksiya qurulması yaratmaq üçün build əmrinə -p arqumenti əlavə edin:

brunch build -p

Bunun yalnız produksiya zamanı işlədildiyini unutmayın. -p arqumentini və ya plagini development zamanı işlətməyin. Çünki, bu, qurulma əməliyyatını çox yavaşladacaq və React-in faydalı xəbərdarlıqlarını gizlədəcək.

Browserify

Ən səmərəli Browserify produksiya qurulması üçün bir neçə plagin yükləmək lazımdır:

# npm işlədirsinizsə
npm install --save-dev envify terser uglifyify 

# Yarn işlədirsinizsə
yarn add --dev envify terser uglifyify 

Produksiya qurulması yaratmaq üçün aşağıdakı çevirmələri əlavə edin (sıra vacibdir):

  • envify çevirməsi düzgün produksiya mühitinin təyin edilməsini siğortalayır. Bunu qlobal edin (-g).
  • uglifyify çevirməsi development idxallarını silir. Bunu qlobal edin (-g).
  • Sonda, nəticlənənən paket terser korlanmaq üçün (mangling) göndərilir (səbəbini buraxan oxuyun).

Məsələn:

browserify ./index.js \
  -g [ envify --NODE_ENV production ] \
  -g uglifyify \
  | terser --compress --mangle > ./bundle.js

Bunun yalnız produksiya zamanı işlədildiyini unutmayın. Bu plaginləri development zamanı işlətməyin. Çünki, bu, qurulma əməliyyatını çox yavaşladacaq və React-in faydalı xəbərdarlıqlarını gizlədəcək.

Rollup

Ən səmərəli Rollup qurulması üçün bəzi plaginləri yükləmək lazımdır:

# npm işlədirsinizsə
npm install --save-dev rollup-plugin-commonjs rollup-plugin-replace rollup-plugin-terser

# Yarn işlədirsinizsə
yarn add --dev rollup-plugin-commonjs rollup-plugin-replace rollup-plugin-terser

Produksiya qurulması yaratmaq üçün aşağıdakı plaginləri əlavə edin (sıra vacibdir):

  • replace plagini düzgün produksiya mühitinin təyin edilməsini siğortalayır.
  • commonjs plagini Rollup-da CommonJS-in işlədilməsinə imkan yaradır.
  • terser plagini son paketi kompresləyir və korlayır (mangles).
plugins: [
  // ...
  require('rollup-plugin-replace')({
    'process.env.NODE_ENV': JSON.stringify('production')
  }),
  require('rollup-plugin-commonjs')(),
  require('rollup-plugin-terser')(),
  // ...
]

Tam qurulma konfiqurasiyası üçün bu gist-ə baxın.

Bunun yalnız produksiya zamanı işlədildiyini unutmayın. Development zamanı terser və ya replace plaginini 'production' dəyəri ilə tətbiq etməyin. Çünki, bu, qurulma əməliyyatını çox yavaşladacaq və React-in faydalı xəbərdarlıqlarını gizlədəcək.

webpack

Qeyd:

Create React App işlədirsinizsə, yuxarıdakı təlimatlara baxın.
Bu bölmə, webpack-in birbaşa konfiqurasiyası üçün uyğundur.

Webpack v4+ versiyasının produksiya modu yazılmış kodu avtomatik minimallaşdıracaq.

const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  mode: 'production',
  optimization: {
    minimizer: [new TerserPlugin({ /* burada parametlər əlavə edə bilərsiniz */ })],
  },
};

Əlavə məlumat üçün webpack sənədlərinə baxın.

Bunun yalnız produksiya zamanı işlədildiyini unutmayın. TerserPlugin plaginini development zamanı işlətməyin. Çünki, bu, qurulma əməliyyatını çox yavaşladacaq və React-in faydalı xəbərdarlıqlarını gizlədəcək.

Chrome Performans Təbi ilə Komponentləri Profayl Edilməsi

Development modunda komponentlərin necə mount edildiyini, yeniləndiyini və unmount edildiyini dəstəklənən brauzerlərin performans alətləri ilə görüntüləmək mümkündür. Məsələn:

Chrome qrafikində React komponentləri

Chrome-da istifadə edə bilmək üçün:

  1. Müvəqqəti olaraq React DevTools xüsusi olmaqla bütün Chrome artırmalarını söndürün. Artırmalar nəticənin səhv göstərilməsinə səbəb ola bilər!

  2. Applikasiyanın development modunda olduğundan əmin olun.

  3. Chrome DevTools-un Performans təbinə daxil olun və Record düyməsini tıklayın.

  4. Profayl etmək istədiyiniz əməliyyatları icra edin. 20 saniyədən çox rekord etməyin. Əks halda Chrome ilişə bilər.

  5. Rekord etməni saxlayın.

  6. React hadisələri User Timing adı altında qruplanacaq.

Daha ətraflı izahat üçün Ben Şvarsın məqaləsini oxuyun.

Nəzərə alin ki, bu alətdə göstərilən rəqəmlər nisbidir. Produksiya zamanı komponentlər daha tez işləyəcək. Bu, lazımsız UI-ın səhvən yeniləndiyini və UI yeniliklərinin hansı dərinlikdə və tezlikdə olduğunu görməyə imkan yaradır.

İndiki zamanda, bu xüsusiyyəti dəstəkləyən brauzerlər Chrome, Edge və IE-dir. Lakin, bizim User Timing API-ından istifadə etdiyimizdən bu xüsusiyyətin gələcəkdə digər brauzerlərdə işləyəcəyini gözləyirik.

DevTools Profayleri ilə Komponentləri Profayl Edilməsi

react-dom 16.5+ və react-native 0.57+ versiyalarında React DevTools Profaylerin DEV modunda daha inkişaf etmiş qabiliyyətləri var. Profaylerin icmalına “React Profayler ilə Tanışlıq” bloq yazısından baxa bilərsiniz. Profaylerin video izahatına Youtube-dan baxa bilərsiniz.

React DevTools yükləməmisinizsə, aşağıdakı linklərə baxın:

Qeyd

react-dom-un produksiyada profayl edilməsi react-dom/profiling paketində mövcuddur. Bu paketin istifadəsi üçün fb.me/react-profiling səhifəsinə baxın.

Böyük Siyahıların Virtuallaşdırılması

Applikasiya böyük siyahılı məlumatlar (100-lərlə və ya 1000-lərlə sətrdən ibarət) render etdikdə “windowing” adlı texnikadan istifadə etməyi tövsiyyə edirik. Bu texnika ilə məlumatın yalnız kiçik hissəsi render olunaraq komponentlərin yenidən render edilməsi və yeni DOM nodların yaranması zamanını kəskin şəkildə azaldır.

react-windowreact-virtualized paketləri populyar windowing kitabxanalarıdır. Bu kitabxanalar siyahılar, qridlər, və cədvəllər göstərmək üçün bir neçə komponent təmin edirlər. Applikasiyanın xüsusi istifadəsinə uyğun olan windowing lazım olduqda Twitter-in yolu ilə gedərək öz windowing komponentinizi yarada bilərsiniz.

Rekonsilyasiyadan Çəkinmək

React, render olunan UI-ın daxili təmsilini yaradır və saxlayır. Bu təmsilə komponentlərdən qaytarılan React elementləri daxildir. Bu təmsil, lazım olmadıqda DOM nodlarının yaranmasını və istifadəsini azaltmağa imkan yaradır. DOM nodlarındə edilən əməliyyatlar, sadə JavaScript obyektlərində edilən əməliyyatlardan çox yavaşdır. Bu daxili təmsilin “virtual DOM” adlanmasına baxmayaraq eyni təmsil React Native-də də işlənilir.

Komponent propları və ya state-i dəyişdikdə yeni element ilə köhnə render olunmuş element müqayisə olunur. Elementlər eyni olmadıqda DOM yenilənir.

Yalnız dəyişən DOM nodların yenilənməsinə baxmayaraq yenidən render etmə əməliyyatı zaman çəkə bilər. Bir çox ssenaridə bu problem deyil. Lakin, yavaşlama nəzərə çarpan olduqda shouldComponentUpdate lifecycle funksiyasını (bu funksiya yenidən render etmə prosesi başlamamışdan öncə çağrılır) tətbiq edərək sürəti çoxalda bilərsiniz. Bu funksiyanın standart tətbiqi true qaytarır. Bu zaman, yeniləmələr React tərəfindən icra olunur:

shouldComponentUpdate(nextProps, nextState) {
  return true;
}

Əgər bildiyiniz bəzi situasiyalarda komponent yenilənməməlidirsə, shouldComponentUpdate funksiyasından false qaytara bilərsiniz. false qaytarıldıqda yenilənən komponentin render() funksiyası daxil olmaqla render etmə prosesi buraxılacaq.

Bir çox ssenaridə shouldComponentUpdate() funksiyasını əl ilə yazmaq əvəzinə komponenti React.PureComponent klasından gənişləndirə bilərsiniz. Bu, shouldComponentUpdate() funksiyasında cari və əvvəlki proplar və state-in dayaz müqayisə edilməsinə bərabərdir.

shouldComponentUpdate Funksiyası Fəaliyyətdə

Aşağıda komponentlər ağacı göstərilib. Hər komponentdə SCU dəyəri shouldComponentUpdate funksiyasından qaytarılan dəyəri, vDOMEq isə render olunan elementlərin bərabər olduğunu göstərir.  Yumrunun rəngi komponentin rekonsilyasiya olacağını göstərir.

should component update

C2-nin shouldComponentUpdate funksiyasının false qaytardığından C2 komponenti render edilmir. Bu səbəbdən, C4 və C5-in shouldComponentUpdate funksiyası çağrılmır.

C1 və C3-ün shouldComponentUpdate funksiyasının true qaytardığından komponentlərin bütün uşaqları yoxlanılır. C6-nın shouldComponentUpdate funksiyasının true qaytardığından və render olunan elementlərin eyni olmadığından DOM yenilənməli olur.

Ən sonuncu maraqlı komponent C8-dir. React bu komponenti render etməli olur. Lakin, qaytarılan React elementlərin əvvəlki render olunan elementlər ilə eyni olduğundan, DOM yenilənmir.

Nəzərə alın ki, React yalnız C6 üçün DOM-u yeniləyir. C8-də elementlər müqayisə edildikdən sonra DOM yenilənmir. C2 və C7-də isə shouldComponentUpdate funksiyasının false qaytardığından elementlərin müqayisəsi əməliyyatı baş vermir. Nəticədə, render funksiyası çağrılmır.

Nümunələr

Əgər komponent yalnız props.color və ya state.count dəyişənləri dəyişdikdə yenilənirsə, bunu shouldComponentUpdate ilə yoxlaya bilərsiniz:

class CounterButton extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.color !== nextProps.color) {
      return true;
    }
    if (this.state.count !== nextState.count) {
      return true;
    }
    return false;
  }

  render() {
    return (
      <button
        color={this.props.color}
        onClick={() => this.setState(state => ({count: state.count + 1}))}>
        Tıklama sayı: {this.state.count}
      </button>
    );
  }
}

Bu kodda, shouldComponentUpdate funksiyası props.color və ya state.count dəyərlərinin dəyişdiyini yoxlayır. Dəyərlər dəyişmədikdə komponent yenilənmir. Komponent mürəkkəbləşdikdə propsstate-in bütün dəyərlərini eyni formada “dayaz müqayisə” edərək komponentin yenilənməsini müəyyənləşdirə bilərsiniz. Bu müqayisə prinsipinin çox işlədildiyindən bunun üçün React-də faydalı klas var: React.PureComponent. Aşağıdakı kod, yuxarıdakı kodun daha sadə formasıdır:

class CounterButton extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  render() {
    return (
      <button
        color={this.props.color}
        onClick={() => this.setState(state => ({count: state.count + 1}))}>
        Count: {this.state.count}
      </button>
    );
  }
}

Bir çox halda shouldComponentUpdate əvəzinə React.PureComponent-dən istifadə edə bilərsiniz. Bu klas, proplar və state-i dayaz müqayisə edir. Bu səbəbdən, bu klası dayaz müqayisə ilə tam yoxlana bilməyən mürəkkəb dəyərləri yoxlamaq üçün istifadə etməyin.

Daha mürəkkəb məlumat strukturlarının dayaz müqayisəsi problem yarada bilər. Məsələn, vergül ilə ayrılmış sözləri render etmək üçün ListOfWords komponentinin və siyahıya söz əlavə etmək üçün WordAdder komponentinin olduğunu fikirləşin. Aşağıdakı kod düzgün işləməyəcək:

class ListOfWords extends React.PureComponent {
  render() {
    return <div>{this.props.words.join(',')}</div>;
  }
}

class WordAdder extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      words: ['marklar']
    };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    // Bu bölmə səhvdir və baqlara səbəb olacaq
    const words = this.state.words;
    words.push('marklar');
    this.setState({words: words});
  }

  render() {
    return (
      <div>
        <button onClick={this.handleClick} />
        <ListOfWords words={this.state.words} />
      </div>
    );
  }
}

PureComponent klası this.props.words propunu sadə müqayisə edəcək. WordAdder komponentinin handleClick funksiyası words massivini mutasiya etdiyindən massivdəki sözlərin fərqli olmasına baxmayaraq this.props.words propu eyni qalacaq. Bu səbəbdən, yeni sözlərin render edilməli olmasına baxmayaraq ListOfWords komponenti yenilənməyəcək.

Məlumatları Mutasiya Etməməyin Gücü

Bu problemi həll etməyin ən asan yolu props və state-in dəyərlərini mutasiya etməməkdir. Məsələn, handleClick metodu concat funksiyası ilə yazıla bilər:

handleClick() {
  this.setState(state => ({
    words: state.words.concat(['marklar'])
  }));
}

ES6-ın massivlər üçün yayma sintaksisi ilə də massivə elementi əlavə etmək mümkündür. Create React App işlədirsinizsə bu sintaksis işləyəcək.

handleClick() {
  this.setState(state => ({
    words: [...state.words, 'marklar'],
  }));
};

Siz, həmçinin obyektlərin mutasiya olunmaması üçün kodu dəyişə bilərsiniz. Məsələn, colormap obyektinin olduğunu və bu obyektin colormap.right dəyərini 'mavi'-yə dəyişmək istədiyimizi fərz edək:

function updateColorMap(colormap) {
  colormap.right = 'mavi';
}

Orijinal obyekti mutasiya etməmək üçün Object.assign funksiyasından istifadə edə bilərsiniz:

function updateColorMap(colormap) {
  return Object.assign({}, colormap, {right: 'mavi'});
}

İndi, updateColorMap funksiyası köhnə obyekti mutasiya etmək əvəzinə yeni obyekt qaytarır. Object.assign ES6-da işləyir və polifil tələb edir.

ES6-ın obyektlər üçün yayma sintaksisi ilə də obyektləri mutasiyasız yeniləmək mümkündür:

function updateColorMap(colormap) {
  return {...colormap, right: 'mavi'};
}

Create React App işlətdikdə Object.assign funksiyası və obyekt yayma sintaksisi işləcəyək.

Dərin obyektlərin mutasiyasız yenilənməsi çətin ola bilər. Bu problem ilə qarşılaşdıqda Immer və ya immutability-helper kitabxanalarına baxın. Bu kitabxanalar mutasiyasızlığın faydalarını itirmədən rahat oxuna bilən kodun yazılmasına imkan yaradır.