Дахин нэгтгэх

React зарлагдах API олгодог бөгөөд та шинэчлэлт болгон юу орсон талаар санаа зовох хэрэггүй. Энэ програм бичих илүү амар болгодог ч React дээр хэрхэн хэрэгжүүлэгдсэн нь тодорхой биш байж болно. Энэ нийтлэл нь өндөр хурд шаардах програмууд дээр компонент шинэчлэл хийхэд React “ялгааг” нь мэдрэх алгоримтын сонголтуудыг тайлбарласан юм.

Учир шалтгаан(motivation)

React-ын render() функц ажиллах мѳчид React элементүүдээс бүрдсэн мод үүсгэгддэг. Дараагийн төлөвт эсвэл шинж чанар шинэчлэгдэхэд render() функц нь өөр нэг React элементүүдийн мод үүсгэдэг. Тэгэхээр React нь хэрхэн хурдан дэлгэцийн загварыг хамгийн сүүлийн модоор шинэчлэхээ бодож олох хэрэгтэй болдог.

Эдгээрт хамгийн бага үйлдлээр нэг модыг өөр нэг мод болгон хувиргахад зориулсан алгоримтын ерөнхий шийдлүүд байдаг. Гэсэн хэдий ч state of the art algorithms ажилллах хугацаа(complexity) O(n3) бөгөөд n нь модонд байгаа элементүүдийн тоо юм.

Хэрэв бид үүнийг React дээр ашигласан бол 1000 элементийг дүрслэхэд нэг тэрбум харьцуулалт хийгдэх нь байна. Энэ нь хэтэрхий удаан болно. Үүний оронд React 2 тѳрлийн таамаглал дээр үндэслэн heuristic 0(n) алгоритм ашигладаг:

  1. Хоёр өөр төрлийн элементүүд нь өөр өөр мод үүсгэдэг.
  2. Хөгжүүлэгч нь ямар элементүүд тогтвортой байх талаар зааврыг key шинж чанар ашиглан өгсөн.

Бараг л ихэнх практикал хэрэглээнд эдгээр таамаглалууд нь зөв байдаг.

Ялгааг олох алгоритм

Хоёр модны ялгааг олоход React эхлээд хоёр үндсийг нь харьцуулдаг. Ажиллагаа нь үндэс дэх элементүүдийн төрлөөс хамааран өөр байдаг.

Өөр төрлийн элементүүд

Үндэс дэх элементүүд нь өөр төрлийнх бол React хуучин модыг буулган шинэ модыг эхнээс нь байгуулдаг. <a><img>-руу, эсвэл <Article><Comment>-руу, эсвэл <Button><div>-р гуу гэхчлэн эдгээр нь бүгд шинэ дахин байгуулалт руу хөтөлдөг.

Модыг буулгаж байхад хуучин DOM зангилаанууд устгагддаг. Компонентийн тохиолдол componentWillUnmount()-г хүлээн авна. Шинэ мод байгуулж байхад шинэ DOM зангилаанууд DOM руу нэмэгддэг. Компонентийн тохиолдлууд нь componentWillMount() хүлээж авах ба дараа нь componentDidMount()-г хүлээн авна. Хуучин модод байсан ямар төлөв энд алдагдана.

Үндсээс дооших ямар компонентууд салгагдах бөгөөд тэдгээрийн төлөв нь устгагдана. Жишээлбэл, дараахийг ялгааг олоход:

<div>
  <Counter />
</div>

<span>
  <Counter />
</span>

Энэ нь хуучин Counter-г устган шинийг залгах болно.

Ижил төрлийн DOM элементүүд

Ижил төрлийн хоёр React DOM элементүүдийг харьцуулахад react тэдгээрийн аттрибутуудыг харан ижил DOM зангилаанд хадгалдаг ба зөвхөн өөрчлөгдсөн аттрибутуудыг шинэчилдэг. Жишээлбэл:

<div className="before" title="stuff" />

<div className="after" title="stuff" />

Эдгээр хоёр элементүүдийг харьцуулахад React зөвхөн className-г нь өөрчлөгдснийг мэддэг.

style-г шинэчлэх үед React мөн л зөвхөн өөрчлөгдсөн шинж чанарыг нь шинэчилдэг.Жишээлбэл:

<div style={{color: 'red', fontWeight: 'bold'}} />

<div style={{color: 'green', fontWeight: 'bold'}} />

Эдгээр хоёр элементүүдийг хөрвүүлэхэд React зөвхөн color загвар нь өөрчлөгдөөд fontWeight нь өөрчлөгдөөгүйг мэддэг.

DOM зангилааг харьцуулсний дараа дэд элементүүдийн рекурс байдлаар харьцуулдаг.

Ижил төрлийн компонент элементүүд

Компонент шинэчлэгдэхэд тохиолдол(instance) нь хэвээр үлддэг учир төлөв нь дүрслэлүүдийн хооронд хадгалагдан үлддэг. React компонентийн тохиолдлын шинэ элементэд тохирох шинж чанарыг нь шинэчилдэг бөгөөд componentWillReceiveProps() болонcomponentWillUpdate()-г өөрчлөгдөж байгаа тохиодол дээр дууддаг.

Дараа нь render() функц дуудагддаг бөгөөд ялгаа олох алгоритм өмнөх үр дүн болох шинэ үр дүн дээр рекурс хийдэг.

Дэд элемент дээр рекурсив ажиллах

Анхнаасаа DOM зангилааны дэд элемент дээр рекурс хийхэд React дэд элементүүдийн жагсаалтаар дамжин явж ялгаа байвал хувирлийг бий болгодог.

Жишээлбэл дэд элементүүдийн төгсгөлд элемент нэмэхэд эдгээр хоёр модыг хувиргахад сайн ажилладаг:

<ul>
  <li>first</li>
  <li>second</li>
</ul>

<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

React эхний хоёр мод болох <li>first</li>, <li>second</li>-г тааруулдаг бөгөөд шинэ <li>third</li> мод нэмнэ.

Хэрэв та үүнийг натив аргаар хэрэгжүүлсэн бол шинэ элемент эхэнд нэмэхэд хурданд муугаар нөлөөлөх байсан. Жишээлбэл, дараах хоёр модыг хувиргахад ажиллагаа нь удаан:

<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

React нь <li>Duke</li> болон <li>Villanova</li> дэд модыг үлдээхийн оронд хувиргадаг. Энэхүү үр дүнгүй арга нь асуудал болж болно.

Түлхүүрүүд

Энэ асуудлыг шийдэхийн тулд React нь key аттрибут дэмждэг. Дэд элемент түлхүүртэй байхад React түлхүүрийг ашиглан дэд элементийг тааруулах бөгөөд үндсэн модтой тааруулдаг. Жишээлбэл, key бидний үр дүнгүй жишээ рүү нэмэх нь мод хувиргалтыг үр дүнтэй болгоно:

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

Одоо React нь '2014' элемент нь шинэ, '2015' болон '2016' түлхүүрүүд зөөгдсөн гэдгийг мэднэ.

Амьдрал дээр түлхүүр хайж олох нь тийм хэцүү биш юм. Таны дүрслэх гэж буй элемент аль хэдийн өөрийгөө тодорхойлох ID-тай тэр нь таний өгөгдлөөс ирж болох юм:

<li key={item.id}>{item.name}</li>

Хэрэв тийм биш бол та модель руугаа шинэ ID шинж чанар нэмэх эсвэл түлхүүр үгийг зарим хэсгүүдийг хэшлэн үүсгэж болно. Түлхүүр бол өөрийгөө бусад элементээс ялгахад л хангалттай бөгөөд глобалаар дахин давтагдашгүй байх хэрэггүй юм.

Бидний сүүлийн боломж нь жагсаалтын индексийн түлхүүр болгон ашиглах юм. Энэ нь жагсаалтын элементүүд эрэмбэлэгдэхгүй үед сайн ажиллаж болох ч эрэмблэгдвэл удаан ажиллана.

Индексийн түлхүүр үг болгон ашиглаж байгаа үед эрэмбэлэх нь компонентийн төлөв дээр асуудал үүсгэж болно. Компонентийн тохиолдлууд нь түлхүүр үг дээр тулгуурлан шинэчлэгдэх болон дахин ашигладдаг. Хэрэв индекс түлхүүр бол элементийг зөөхөд өөрчлөгдөнө. Үр дүнд нь удирдагдаагүй оролтод зориулсан компонентийн төлөв холилдон бидний хүсээгүй замаар шинэчлэгдэж болно.

Энд CodePen дээр индексийг түлхүүр болгон ашиглахад гарж болох асуудлыг харуулсан жишээ, харин энд адилхан жишээний шинэчлэгдсэн хувилбар болох индексийг түлхүүр болгон ашиглахгүйгээр хэрхэн эрэмблэх, дахин эрэмбэ оноох гэх мэт асуудлийг шийдсэн байна.

Tradeoffs

Дахин бүтээх алгоритм нь хэрэгжүүлэлтийн дэлгэрэнгүй гэдгийг санах нь чухал. React нь програмыг үйлдэл бүрд бүхэлд нь шинэчилж чадах бөгөөд эцсийн үр дүн ижил байна. Ойлгомжтой байх үүднээс энэ агуулгад дахин дүрслэх нь бүх компонентийн render-г дуудахийг хэлж байгаа бөгөөд энэ нь React тэдгээрийг салгаж, залгах гэсэн үг биш юм. Энэ нь зөвхөн өмнөх хэсэгт дурьдагдсан ялгаануудыг шинэчлэхэд мөрдөгдөнө.

Бид эдгээр тохиолдлуудыг хурдан болгох үүднээс байнга heuristic аргаа сайжруулдаг. Одоогийн хэрэгжүүлэлт дээр дэд мод нь дотроо зөөгдсөн боловч өөр газар зөөгдөөгүй юм. Алгоритм бүтэн дэд модыг дахин дүрслэх болно.

Учир нь React heuristic аргад тулгуурлаж байгаа учир аль нэг таамаглал нь биелэхгүй үед хурдны хувьд удааширна.

  1. Алгоритм өөр төрлийн компонентуудыг хооронд нь тааруулах гэж оролдохгүй. Тун ижилхэн гаралттай хоёр өөр компонентийн төрлийг өөрчлөх гэж байгаа бол та тэдгээрийг ижил төрөлтэй болгох хэрэгтэй. Амьдрал дээр бид энэ төрлийн асуудал олоогүй байна.

  2. Түлхүүрүүд нь тогтвортой, дахин давтагдашгүй, таамаглахуйц байх нь зүйтэй. Тогтворгүй түлхүүрүүд(Math.random()-н үр дүнгээс гарсан шиг) нь олон компонентийн тохиолдлууд болон DOM зангилаануудыг шаардлагагүйгээр дахин үүсгэх бөгөөд энэ хурдийг буруулахаас гадна дэд компонентүүд төлвөө алдахад хүргэнэ.