You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/docs/reconciliation.md
+24-24Lines changed: 24 additions & 24 deletions
Original file line number
Diff line number
Diff line change
@@ -4,18 +4,18 @@ title: Rekoncyliacja
4
4
permalink: docs/reconciliation.html
5
5
---
6
6
7
-
Deklaratywność API Reacta sprawia, że nie musisz się martwić co dokładnie zmienia się przy każdej aktualizacji, dzięki czemu pisanie aplikacji staje się dużo prostsze. Dokładna implementacja nie jest jednak tak oczywista. W tym artykule wyjaśniamy decyzje, które podjęliśmy przy projektowaniu algorytmu różnicującego w Reakcie, mając na celu zapewnienie przewidywalności aktualizacji komponentów przy zachowaniu wysokiej wydajności.
7
+
Deklaratywność API Reacta sprawia, że nie musisz się martwić, co dokładnie zmienia się przy każdej aktualizacji, dzięki czemu pisanie aplikacji staje się dużo prostsze. Dokładna implementacja nie jest jednak tak oczywista. W tym artykule wyjaśniamy decyzje, które podjęliśmy przy projektowaniu algorytmu różnicującego w Reakcie, mając na celu zapewnienie przewidywalności aktualizacji komponentów przy zachowaniu wysokiej wydajności.
8
8
9
9
## Motywacja {#motivation}
10
10
11
-
Podczas korzystania z Reacta, w danym momencie możesz potraktować funkcję `render()` jako tworzącą drzewo elementów Reacta. Podczas następnej zmiany stanu bądź aktualizacji właściwości funkcja `render()` zwróci inne drzewo elementów React. Zadaniem Reacta jest wtedy opracować wydajny sposób na aktualizację UI, tak by dopasować je do najświeższego drzewa elementów.
11
+
Podczas korzystania z Reacta, w danym momencie możesz potraktować funkcję `render()` jako tworzącą drzewo elementów Reacta. Podczas następnej zmiany stanu bądź aktualizacji właściwości funkcja `render()` zwróci inne drzewo elementów. Zadaniem Reacta jest wtedy opracować wydajny sposób na aktualizację UI, tak by dopasować je do najświeższego drzewa elementów.
12
12
13
-
Istnieją ogólne rozwiązania algorytmicznego problemu generowania najmniejszej liczby operacji wymaganych do przekształcenia drzew elementów. Jednakże nawet [najlepsze znane algorytmy](https://grfia.dlsi.ua.es/ml/algorithms/references/editsurvey_bille.pdf) mają złożoność rzędu O(n<sup>3</sup>) gdzie n jest liczbą elementów w drzewie.
13
+
Istnieją ogólne rozwiązania algorytmicznego problemu generowania najmniejszej liczby operacji wymaganych do przekształcenia drzew elementów. Jednakże nawet [najlepsze znane algorytmy](https://grfia.dlsi.ua.es/ml/algorithms/references/editsurvey_bille.pdf) mają złożoność rzędu O(n<sup>3</sup>), gdzie n jest liczbą elementów w drzewie.
14
14
15
-
Gdybyśmy wykorzystali ten algorytm w Reakcie wyświetlenie 1000 elementów wymagałoby miliarda porównań. Jest to stanowczo zbyt kosztowne. Zamiast tego, React implementuje heurystyczny algorytm o złożoności O(n) bazujący na dwóch założeniach:
15
+
Gdybyśmy wykorzystali ten algorytm w Reakcie, wyświetlenie 1000 elementów wymagałoby miliarda porównań. Byłoby to stanowczo zbyt kosztowne. Zamiast tego, React implementuje heurystyczny algorytm o złożoności O(n), bazujący na dwóch założeniach:
16
16
17
17
1. Dwa elementy różnych typów produkują różne drzewa.
18
-
2. Programista może wskazać które elementy drzewa mogą być stabilne pomiędzy kolejnymi renderami używając właściwości `key`.
18
+
2. Programista może wskazać, które elementy drzewa mogą być stabilne pomiędzy kolejnymi renderowaniami, używając właściwości `key`.
19
19
20
20
W praktyce te założenia sprawdzają się w prawie wszystkich zastosowaniach.
21
21
@@ -25,9 +25,9 @@ Podczas różnicowania dwóch drzew React najpierw porównuje elementy nadrzędn
25
25
26
26
### Elementy różnych typów {#elements-of-different-types}
27
27
28
-
Zawsze, gdy elementy nadrzędne różnią się typem React pozbywa się starego drzewa i buduje nowe od podstaw. Zamiana `<a>` na `<img>`, czy `<Article>` na `<Comment>`, lub `<Button>` na `<div>` - każda z tych zmian spowoduje całkowite przebudowanie drzewa.
28
+
Zawsze, gdy elementy nadrzędne różnią się typem, React pozbywa się starego drzewa i buduje nowe od podstaw. Zamiana `<a>` na `<img>`, czy `<Article>` na `<Comment>` lub `<Button>` na `<div>` - każda z tych zmian spowoduje całkowite przebudowanie drzewa.
29
29
30
-
Gdy React pozbywa się starego drzewa, wszystkie przypisane do niego węzły DOM są niszczone. W instancjach komponentów wywoływane jest `componentWillUnmount()`. Podczas budowania nowego drzewa do DOMu dodawane są nowe węzły. W instancjach komponentów wywoływane jest `UNSAFE_componentWillMount()` a następnie `componentDidMount()`. Jakikolwiek stan przypisany do starego drzewa jest bezpowrotnie stracony.
30
+
Gdy React pozbywa się starego drzewa, wszystkie przypisane do niego węzły DOM są niszczone. W instancjach komponentów wywoływane jest `componentWillUnmount()`. Podczas budowania nowego drzewa do DOM-u dodawane są nowe węzły. W instancjach komponentów wywoływane jest `UNSAFE_componentWillMount()`, a następnie `componentDidMount()`. Jakikolwiek stan przypisany do starego drzewa jest bezpowrotnie tracony.
31
31
32
32
Jakiekolwiek komponenty poniżej elementu nadrzędnego również zostają odmontowane, a ich stan zniszczony. Na przykład, przy zmianie:
33
33
@@ -59,7 +59,7 @@ Na przykład:
59
59
<divclassName="after"title="stuff" />
60
60
```
61
61
62
-
Porównując te dwa elementy, React wie by zmienić jedynie `className` na dostępnym węźle DOMu.
62
+
Porównując te dwa elementy, React wie, że należy zmienić jedynie `className` na istniejącym węźle DOM-u.
63
63
64
64
Aktualizując `style`, React wie również, by aktualizować jedynie te właściwości, które uległy zmianie. Na przykład:
65
65
@@ -71,11 +71,11 @@ Aktualizując `style`, React wie również, by aktualizować jedynie te właści
71
71
72
72
Przy aktualizacji tych elementów React wie, by zmodyfikować jedynie `color`, a nie również `fontWeight`.
73
73
74
-
Po obsłużeniu danego węzła DOMu React rekursywnie wywołuje algorytm na kolejnych potomkach w drzewie.
74
+
Po obsłużeniu danego węzła DOM-u React rekursywnie wywołuje algorytm na kolejnych potomkach w drzewie.
75
75
76
76
### Komponenty tego samego typu {#component-elements-of-the-same-type}
77
77
78
-
Gdy komponent jest aktualizowany, jego instancja pozostaje bez zmian, dzięki czemu stan jest zachowany pomiędzy kolejnymi renderami. React aktualizuje właściwości instancji zgodnie z nowym elementem i wywołuje na niej `UNSAFE_componentWillReceiveProps()`, `UNSAFE_componentWillUpdate()` oraz `componentDidUpdate()`.
78
+
Gdy komponent jest aktualizowany, jego instancja pozostaje bez zmian, dzięki czemu stan jest zachowany pomiędzy kolejnymi renderowaniami. React aktualizuje właściwości instancji zgodnie z nowym elementem i wywołuje na niej `UNSAFE_componentWillReceiveProps()`, `UNSAFE_componentWillUpdate()` oraz `componentDidUpdate()`.
79
79
80
80
Następnie wywołana zostaje metoda `render()`, a algorytm różnicujący sięga dalej w głąb drzewa.
81
81
@@ -88,7 +88,7 @@ Następnie wywołana zostaje metoda `render()`, a algorytm różnicujący sięga
88
88
89
89
### Rekurencja po potomkach {#recursing-on-children}
90
90
91
-
Podczas rekurencji przez potomstwo (ang. *children*) węzła DOMu React iteruje po obu drzewach (starym i nowym) jednocześnie, generując zmiany gdy napotka na różnicę.
91
+
Podczas rekurencji przez potomstwo (ang. *children*) węzła DOM-u React iteruje po obu drzewach jednocześnie (starym i nowym), generując zmiany, gdy napotka na różnicę.
92
92
93
93
Na przykład, gdy dodamy element na koniec potomstwa, przejście pomiędzy tymi drzewami działa poprawnie:
94
94
@@ -121,12 +121,12 @@ Przy naiwnej implementacji algorytmu dodanie elementu na początek będzie miał
121
121
</ul>
122
122
```
123
123
124
-
React dokona zmian przy każdym potomku, gdyż nie jest świadomy, że `<li>Duke</li>` oraz `<li>Villanova</li>` nie uległy zmianie a jedynie przesunięciu. Może się to okazać problematyczne.
124
+
React dokona zmian przy każdym potomku, gdyż nie jest świadomy, że `<li>Duke</li>` oraz `<li>Villanova</li>` nie uległy zmianie, a jedynie przesunięciu. Może się to okazać problematyczne.
125
125
126
126
### Klucze {#keys}
127
127
128
-
W celu rozwiązania powyższego problemu React wprowadził atrybut `key`. Gdy potomstwo posiada klucz (ang. *key*) React używa go przy porównaniu poprzedniego drzewa z nowym.
129
-
Na przykład dodając `key` do poprzedniego przykładu pozbędziemy się problemu z wydajnością:
128
+
W celu rozwiązania powyższego problemu React wprowadził atrybut `key`. Gdy potomstwo posiada klucz (ang. *key*), React używa go przy porównaniu poprzedniego drzewa z nowym.
129
+
Na przykład, dodając `key` do poprzedniego przykładu pozbędziemy się problemu z wydajnością:
130
130
131
131
```xml
132
132
<ul>
@@ -142,30 +142,30 @@ Na przykład dodając `key` do poprzedniego przykładu pozbędziemy się problem
142
142
```
143
143
Teraz React wie, że element z kluczem `'2014'` jest nowy, a elementy o kluczach `'2015'` oraz `'2016'` jedynie zmieniły pozycję.
144
144
145
-
Dobranie stosownego klucza zazwyczaj nie nastręcza trudności. Element, który wyświetlamy może już posiadać unikalny ID, więc klucz może pochodzić bezpośrednio od twoich danych:
145
+
Dobranie stosownego klucza zazwyczaj nie przysparza trudności. Element, który wyświetlamy, może już posiadać unikalny ID, więc klucz może pochodzić bezpośrednio od twoich danych:
146
146
147
147
```js
148
148
<li key={item.id}>{item.name}</li>
149
149
```
150
150
151
-
W przeciwnym wypadku można dodać nowe id do modelu danych, bądź wykorzystać funkcję skrótu (ang. *hash function*) do wygenerowania klucza. Klucz musi być unikalny jedynie względem swego rodzeństwa.
151
+
W przeciwnym wypadku można dodać nowe ID do modelu danych bądź wykorzystać funkcję skrótu (ang. *hash function*) do wygenerowania klucza. Klucz musi być unikalny jedynie względem swego rodzeństwa.
152
152
153
-
W ostateczności jako klucza można użyć indeksu elementu w tablicy. Rozwiązanie to sprawdzi się jeśli kolejność elementów w tablicy jest stała, w przypadku zmiennej kolejności różnicowanie będzie mniej wydajne.
153
+
W ostateczności jako klucza można użyć indeksu elementu w tablicy. Rozwiązanie to sprawdzi się, jeśli kolejność elementów w tablicy jest stała; w przypadku zmiennej kolejności różnicowanie będzie mniej wydajne.
154
154
155
-
W przypadku użycia indeksu jako klucza, zmiany w kolejności elementów tablicy mogą również powodować problemy ze stanem komponentów. Instancje komponentów są aktualizowane bądź nie, na podstawie klucza. Jeśli klucz jest indeksem, każda zmiana pozycji elementu w tablicy powoduje zmianę klucza. W rezultacie stan komponentów może zostać zaktualizowany w nieprzewidywalny sposób i powodować trudne do zidentyfikowania błędy.
155
+
W przypadku użycia indeksu jako klucza, zmiany w kolejności elementów tablicy mogą również powodować problemy ze stanem komponentów. Instancje komponentów są aktualizowane bądź nie na podstawie klucza. Jeśli klucz jest indeksem, każda zmiana pozycji elementu w tablicy powoduje zmianę klucza. W rezultacie stan komponentów może zostać zaktualizowany w nieprzewidywalny sposób i powodować trudne do zidentyfikowania błędy.
156
156
157
157
Na podanym CodePenie można zapoznać się z [przykładowym problemem, jaki stwarza stosowanie indeksów jako kluczy](codepen://reconciliation/index-used-as-key), a z kolei tutaj pokazany jest [sposób, w jaki unikanie indeksów w kluczu rozwiązuje problemy z wstawianiem, sortowaniem oraz zmianą pozycji elementów](codepen://reconciliation/no-index-used-as-key).
158
158
159
159
## Kompromisy {#tradeoffs}
160
160
161
-
Należy pamiętać, że algorytm rekoncyliacji to szczegół implementacyjny. React mógłby rerenderować całą aplikację przy każdej akcji; rezultat byłby ten sam.
162
-
Dla jasności, przez rerender w tym kontekście rozumiemy wywołanie `render` dla wszystkich komponentów, nie oznacza to, że zostaną one odmontowane i zamontowane ponownie.
161
+
Należy pamiętać, że algorytm rekoncyliacji to szczegół implementacyjny. React mógłby ponownie renderować całą aplikację przy każdej akcji; rezultat byłby ten sam.
162
+
Dla jasności: przez ponowne renderowanie w tym kontekście rozumiemy wywołanie `render` dla wszystkich komponentów; nie oznacza to, że zostaną one odmontowane i zamontowane ponownie.
163
163
Oznacza jedynie zaaplikowanie zmian według reguł, które dotychczas przedstawiliśmy.
164
164
165
-
Regularnie usprawniamy algorytm heurystyczny, by zoptymalizować wydajność w najczęstszych przypadkach. W aktualnej implementacji możemy wyrazić fakt, iż poddrzewo zmieniło pozycję względem rodzeństwa, nie jesteśmy jednak w stanie wskazać, że zostało bez zmian przeniesione gdzie indziej. Algorytm wymusi rerender całego poddrzewa.
165
+
Regularnie usprawniamy algorytm heurystyczny, by zoptymalizować wydajność w najczęstszych przypadkach. W aktualnej implementacji możemy wyrazić fakt, iż poddrzewo zmieniło pozycję względem rodzeństwa, nie jesteśmy jednak w stanie wskazać, że zostało bez zmian przeniesione gdzie indziej. Algorytm wymusi ponowne renderowanie całego poddrzewa.
166
166
167
-
Ponieważ React opiera się na heurystyce, tracimy na wydajności zawsze gdy nie spełniamy jej założeń.
167
+
Ponieważ React opiera się na heurystyce, tracimy na wydajności zawsze wtedy, gdy nie spełniamy jej założeń.
168
168
169
-
1. Algorytm nie będzie dopasowywał poddrzew komponentów różnych typów. Jeśli naprzemiennie używasz komponentów różnego typu, a zwracających bardzo podobny wynik - prawdopodobnie powinieneś ujednolicić typ. W praktyce nie zdarzyło nam się by był to problem.
169
+
1. Algorytm nie będzie dopasowywał poddrzew komponentów różnych typów. Jeśli naprzemiennie używasz komponentów różnego typu, a zwracających bardzo podobny wynik - sugerujemy ujednolicić typ. W praktyce nie przysporzyło nam to nigdy problemów.
170
170
171
-
2. Klucze powinny być stabilne, przewidywalne i unikalne. Niestabilne klucze (na przykład generowane przez `Math.random()`) będą skutkować niepotrzebną przebudową wielu instancji komponentów i węzłów DOMu, powodując spadek wydajności i utratę stanu w komponentach potomnych.
171
+
2. Klucze powinny być stabilne, przewidywalne i unikalne. Niestabilne klucze (na przykład generowane przez `Math.random()`) będą skutkować niepotrzebną przebudową wielu instancji komponentów i węzłów DOM-u, powodując spadek wydajności i utratę stanu w komponentach potomnych.
0 commit comments