Di bawah kap React. Kami menulis implementasi kami dari awal

Dalam seri artikel ini, kami akan membuat implementasi Bereaksi kami sendiri dari awal. Pada akhirnya, Anda akan memiliki pemahaman tentang bagaimana React bekerja, apa metode siklus hidup komponen yang disebutnya, dan mengapa. Artikel ini ditujukan bagi mereka yang telah menggunakan Bereaksi dan ingin belajar tentang perangkatnya, atau untuk yang sangat penasaran.

gambar

Artikel ini adalah terjemahan dari React Internal, Bagian Satu: rendering dasar

Ini sebenarnya adalah artikel pertama dari lima


  1. Dasar-dasar Rendering <- kami di sini
  2. ComponentWillMount dan componentDidMount
  3. Perbarui
  4. setState
  5. Transaksi

Materi dibuat ketika React 15.3 relevan, khususnya penggunaan ReactDOM dan stack reconciler. Bereaksi 16 dan di atas memiliki beberapa perubahan. Namun, bahan ini tetap relevan, karena memberikan gambaran umum tentang apa yang terjadi "di bawah tenda".

Bagian 1. Dasar-Dasar Rendering


Elemen dan komponen


Ada tiga jenis entitas dalam Bereaksi: elemen DOM asli, elemen Bereaksi virtual, dan komponen.

Elemen DOM Asli


Ini adalah elemen DOM yang digunakan browser untuk membuat halaman web, misalnya, div, span, h1. Bereaksi membuat mereka dengan memanggil document.createElement (), dan berinteraksi dengan halaman menggunakan metode DOM API berbasis browser seperti element.insertBefore (), element.nodeValue, dan lainnya.

Elemen reaksi virtual


Elemen Bereaksi virtual (sering disebut sebagai "elemen") adalah objek javascript yang berisi properti yang diperlukan untuk membuat atau memperbarui elemen DOM asli atau pohon elemen tersebut. Berdasarkan elemen Bereaksi virtual, elemen DOM asli dibuat, seperti div, span, h1, dan lainnya. Kita dapat mengatakan bahwa elemen Bereaksi virtual adalah turunan dari komponen komposit yang ditentukan pengguna, lebih lanjut tentang ini di bawah.

Komponen


Komponen adalah istilah yang cukup umum dalam Bereaksi. Komponen adalah entitas yang Bereaksi melakukan berbagai manipulasi. Komponen yang berbeda memiliki tujuan yang berbeda pula. Misalnya, ReactDomComponent dari perpustakaan ReactDom bertanggung jawab untuk menghubungkan antara elemen React dan elemen DOM asli yang sesuai.

Komponen Senyawa Ubahsuaian


Kemungkinan besar Anda telah menemukan komponen jenis ini. Saat Anda memanggil React.createClass () atau menggunakan kelas ES6 melalui extended React.Component, Anda membuat komponen komposit khusus. Komponen semacam itu memiliki metode siklus hidup, seperti componentWillMount, shouldComponentUpdate, dan lainnya. Kita dapat mendefinisikannya kembali untuk menambahkan semacam logika. Selain itu, metode lain dibuat, seperti mountComponent, acceptComponent. Metode ini hanya digunakan oleh Bereaksi untuk tujuan internalnya, kami tidak berinteraksi dengan mereka dengan cara apa pun.

ZanudaMode = aktif
Bahkan, komponen yang dibuat pengguna pada awalnya tidak lengkap. React membungkusnya dalam ReactCompositeComponentWrapper, yang menambahkan semua metode siklus hidup ke komponen kami, setelah itu Bereaksi dapat mengelolanya (masukkan, perbarui, dll.).

Bereaksi deklaratif


Ketika datang ke komponen kustom, tugas kami adalah menentukan kelas-kelas komponen ini, tetapi kami tidak membuat instance kelas-kelas ini. Bereaksi menciptakan mereka saat dibutuhkan.

Selain itu, kami tidak secara eksplisit membuat elemen menggunakan gaya imperatif; sebagai gantinya, kami menulis dalam gaya deklaratif menggunakan JSX:

class MyComponent extends React.Component { render() { return <div>hello</div>; } } 

Kode ini dengan markup JSX diterjemahkan oleh kompiler ke dalam yang berikut:

 class MyComponent extends React.Component { render() { return React.createElement('div', null, 'hello'); } } 

Artinya, pada dasarnya, itu berubah menjadi konstruksi imperatif untuk membuat elemen melalui panggilan eksplisit ke React.createElement (). Tetapi konstruksi ini ada di dalam metode render (), yang tidak kami panggil secara eksplisit, Bereaksi akan memanggil metode ini sendiri bila diperlukan. Karena itu, memahami React sama deklaratifnya: kita menggambarkan apa yang ingin kita terima, dan React menentukan cara melakukannya.

Tulis Bereaksi kecil Anda


Setelah menerima dasar teknis yang diperlukan, kami akan mulai membuat implementasi Bereaksi kami sendiri. Ini akan menjadi versi yang sangat disederhanakan, sebut saja Feact.

Misalkan kita ingin membuat aplikasi Feact sederhana yang kodenya akan terlihat seperti ini:

 Feact.render(<h1>hello world</h1>, document.getElementById('root')); 

Pertama, mari kita ngelantur tentang BEJ. Ini justru "mundur," karena penguraian JSX adalah topik besar terpisah yang akan kami hilangkan sebagai bagian dari implementasi Feact. Jika kami berurusan dengan JSX yang diproses, kami akan melihat kode berikut:

 Feact.render( Feact.createElement('h1', null, 'hello world'), document.getElementById('root') ); 

Artinya, kami menggunakan Feact.createElement bukan JSX. Jadi kami menerapkan metode ini:

 const Feact = { createElement(type, props, children) { const element = { type, props: props || {} }; if (children) { element.props.children = children; } return element; } }; 

Elemen yang dikembalikan adalah objek sederhana yang mewakili apa yang ingin kita render.

Apa yang dilakukan Feact.render ()?


Dengan memanggil Feact.render (), kami memberikan dua parameter: apa yang ingin kami render dan di mana. Ini adalah titik awal dari setiap aplikasi Bereaksi. Mari kita menulis implementasi metode render () untuk Feact:

 const Feact = { createElement() { /*   */ }, render(element, container) { const componentInstance = new FeactDOMComponent(element); return componentInstance.mountComponent(container); } }; 

Setelah render () selesai, kami mendapatkan halaman web yang selesai. Elemen DOM dibuat oleh FeactDOMComponent. Mari kita tulis implementasinya:

 class FeactDOMComponent { constructor(element) { this._currentElement = element; } mountComponent(container) { const domElement = document.createElement(this._currentElement.type); const text = this._currentElement.props.children; const textNode = document.createTextNode(text); domElement.appendChild(textNode); container.appendChild(domElement); this._hostNode = domElement; return domElement; } } 

Metode mountComponent membuat elemen DOM dan menyimpannya di this._hostNode. Kami tidak akan menggunakannya sekarang, tetapi kami akan kembali ke ini di bagian berikut.

Versi aplikasi saat ini dapat dilihat secara acak .

Secara harfiah 40 baris kode sudah cukup untuk membuat implementasi React yang primitif. The Feact yang kami buat tidak mungkin menaklukkan dunia, tetapi mencerminkan esensi dari apa yang terjadi di balik tudung React.

Menambahkan komponen khusus


Feact kami harus dapat membuat tidak hanya elemen dalam HTML (div, span, dll.), Tetapi juga komponen komposit yang ditentukan pengguna:
Metode Feact.createElement () yang dijelaskan sebelumnya saat ini baik-baik saja, jadi saya tidak akan mengulanginya dalam daftar kode.
 const Feact = { createClass(spec) { function Constructor(props) { this.props = props; } Constructor.prototype.render = spec.render; return Constructor; }, render(element, container) { //      //   , //    } }; const MyTitle = Feact.createClass({ render() { return Feact.createElement('h1', null, this.props.message); } }; Feact.render({ Feact.createElement(MyTitle, { message: 'hey there Feact' }), document.getElementById('root') ); 

Biarkan saya mengingatkan Anda bahwa jika JSX tersedia, memanggil metode render () akan terlihat seperti ini:

 Feact.render( <MyTitle message="hey there Feact" />, document.getElementById('root') ); 

Kami melewati kelas komponen khusus ke createElement. Elemen Bereaksi virtual dapat mewakili elemen DOM biasa atau komponen kustom. Kami akan membedakan mereka sebagai berikut: jika kami melewati tipe string, maka ini adalah elemen DOM; Jika suatu fungsi, maka elemen ini mewakili komponen kustom.

Meningkatkan Feact.render ()


Jika Anda memperhatikan kode saat ini, Anda akan melihat bahwa Feact.render () tidak dapat memproses komponen kustom. Mari kita perbaiki ini:

 Feact = { render(element, container) { const componentInstance = new FeactCompositeComponentWrapper(element); return componentInstance.mountComponent(container); } } class FeactCompositeComponentWrapper { constructor(element) { this._currentElement = element; } mountComponent(container) { const Component = this._currentElement.type; const componentInstance = new Component(this._currentElement.props); const element = componentInstance.render(); const domComponentInstance = new FeactDOMComponent(element); return domComponentInstance.mountComponent(container); } } 

Kami telah membuat pembungkus untuk item yang diteruskan. Di dalam bungkus, kami membuat turunan dari kelas komponen pengguna dan memanggil metode componentInstance.render (). Hasil dari metode ini dapat diteruskan ke komponen FeactDOMComponent, di mana elemen DOM yang sesuai akan dibuat.

Sekarang kita dapat membuat dan merender komponen khusus. Feact akan membuat simpul DOM berdasarkan komponen khusus, dan mengubahnya tergantung pada properti (properti) komponen khusus kami. Ini merupakan peningkatan signifikan dalam Feact kami.
Perhatikan bahwa FeactCompositeComponentWrapper secara langsung membuat FeactDOMComponent. Hubungan dekat seperti itu buruk. Kami akan memperbaikinya nanti. Jika Bereaksi memiliki koneksi dekat yang sama, maka hanya aplikasi web yang dapat dibuat. Menambahkan lapisan tambahan ReactCompositeComponentWrapper memungkinkan Anda untuk memisahkan logika Bereaksi untuk mengelola elemen virtual dan tampilan akhir dari elemen asli, yang memungkinkan Anda untuk menggunakan Bereaksi tidak hanya saat membuat aplikasi web, tetapi juga, misalnya, Bereaksi Asli untuk ponsel.

Peningkatan komponen khusus


Komponen khusus yang dibuat hanya dapat mengembalikan elemen DOM asli, jika kami mencoba mengembalikan komponen khusus lainnya, kami mendapatkan kesalahan. Perbaiki kekurangan ini. Bayangkan kami ingin menjalankan kode berikut tanpa kesalahan:

 const MyMessage = Feact.createClass({ render() { if (this.props.asTitle) { return Feact.createElement(MyTitle, { message: this.props.message }); } else { return Feact.createElement('p', null, this.props.message); } } } 

Metode render () komponen kustom dapat mengembalikan elemen DOM asli atau komponen kustom lainnya. Jika properti asTitle benar, maka FeactCompositeComponentWrapper akan mengembalikan komponen kustom untuk FeactDOMComponent di mana kesalahan akan terjadi. Perbaiki FeactCompositeComponentWrapper:

 class FeactCompositeComponentWrapper { constructor(element) { this._currentElement = element; } mountComponent(container) { const Component = this._currentElement.type; const componentInstance = new Component(this._currentElement.props); let element = componentInstance.render(); while (typeof element.type === 'function') { element = (new element.type(element.props)).render(); } const domComponentInstance = new FeactDOMComponent(element); domComponentInstance.mountComponent(container); } } 

Sebenarnya, kami sekarang telah membuat penopang untuk memenuhi kebutuhan saat ini. Panggilan ke metode render akan mengembalikan komponen anak hingga mengembalikan elemen DOM asli. Ini buruk karena komponen anak seperti itu tidak akan berpartisipasi dalam siklus hidup. Misalnya, dalam hal ini, kami tidak akan dapat mengimplementasikan panggilan componentWillMount. Kami akan memperbaikinya nanti.

Dan lagi kita memperbaiki Feact.render ()


Versi pertama dari Feact.render () hanya dapat memproses elemen DOM asli. Sekarang hanya komponen yang ditentukan pengguna yang diproses dengan benar tanpa dukungan asli. Penting untuk menangani kedua kasus. Anda dapat menulis pabrik yang akan membuat komponen tergantung pada jenis elemen yang dilewati, tetapi Bereaksi memilih cara yang berbeda: cukup bungkus komponen yang masuk di komponen lain:

 const TopLevelWrapper = function(props) { this.props = props; }; TopLevelWrapper.prototype.render = function() { return this.props; }; const Feact = { render(element, container) { const wrapperElement = this.createElement(TopLevelWrapper, element); const componentInstance = new FeactCompositeComponentWrapper(wrapperElement); //   } }; 

TopLevelWrapper pada dasarnya adalah komponen khusus. Itu juga dapat didefinisikan dengan memanggil Feact.createClass (). Metode render-nya hanya mengembalikan elemen yang diteruskan ke sana. Sekarang setiap elemen dibungkus dalam TopLevelWrapper, dan FeactCompositeComponentWrapper akan selalu menerima komponen kustom sebagai input.

Kesimpulan bagian pertama


Kami telah menerapkan Feact, yang dapat membuat komponen. Kode yang dihasilkan menunjukkan konsep dasar rendering. Render nyata dalam Bereaksi jauh lebih rumit, dan mencakup peristiwa, fokus, pengguliran jendela, kinerja, dll.

Jsfiddle terakhir dari bagian pertama.

Source: https://habr.com/ru/post/id458916/


All Articles