Bahan, terjemahan yang kami terbitkan hari ini, mengungkapkan pendekatan yang digunakan oleh penulisnya ketika menyusun aplikasi Bereaksi. Secara khusus, kita akan membahas di sini struktur folder yang digunakan, penamaan entitas, tempat di mana file tes berada, dan hal-hal serupa lainnya.
Salah satu fitur React yang paling menyenangkan adalah bahwa perpustakaan ini tidak memaksa pengembang untuk secara ketat mematuhi konvensi tertentu mengenai struktur proyek. Banyak dari ini tetap pada kebijaksanaan programmer. Pendekatan ini berbeda dari itu, katakanlah, diadopsi dalam kerangka Ember.js atau Angular. Mereka memberi pengembang lebih banyak fitur standar. Kerangka kerja ini menyediakan konvensi mengenai struktur proyek dan aturan untuk penamaan file dan komponen.

Secara pribadi, saya suka pendekatan yang diadopsi oleh React. Faktanya adalah bahwa saya lebih suka mengendalikan sesuatu sendiri, tanpa bergantung pada "perjanjian" tertentu. Namun, ada banyak keuntungan dari pendekatan penataan proyek yang ditawarkan Angular. Pilihan antara kebebasan dan aturan yang kurang lebih kaku bermuara pada apa yang lebih dekat dengan Anda dan tim Anda.
Selama bertahun-tahun bekerja dengan React, saya telah mencoba berbagai cara untuk menyusun aplikasi. Beberapa ide yang saya terapkan ternyata lebih sukses daripada yang lain. Oleh karena itu, di sini saya akan berbicara tentang segala sesuatu yang telah menunjukkan dirinya dengan baik dalam praktik. Saya harap Anda menemukan sesuatu di sini yang bermanfaat bagi Anda.
Saya tidak mencoba menunjukkan di sini cara penataan aplikasi yang "hanya benar". Anda dapat mengambil beberapa ide saya dan mengubahnya agar sesuai dengan kebutuhan Anda. Anda mungkin tidak setuju dengan saya dengan terus bekerja seperti sebelumnya. Tim yang berbeda membuat aplikasi yang berbeda dan menggunakan cara yang berbeda untuk mencapai tujuan mereka.
Penting untuk dicatat bahwa jika Anda melihat situs web
Thread , yang saya ikuti dalam pengembangan, dan melihat perangkat antarmuka-nya, Anda akan menemukan tempat-tempat di mana aturan-aturan yang akan saya bicarakan tidak dihormati. Faktanya adalah bahwa "aturan" dalam pemrograman harus diambil hanya sebagai rekomendasi, dan bukan sebagai standar komprehensif yang berlaku dalam situasi apa pun. Dan jika Anda berpikir bahwa semacam "aturan" tidak cocok untuk Anda - Anda, demi meningkatkan kualitas apa yang sedang Anda kerjakan, harus menemukan kekuatan untuk menyimpang dari "aturan" ini.
Sebenarnya, sekarang, tanpa basa-basi lagi, saya menawarkan cerita saya tentang penataan aplikasi Bereaksi.
Jangan terlalu khawatir tentang aturannya.
Mungkin Anda memutuskan bahwa rekomendasi yang Anda tidak terlalu khawatirkan tentang aturan terlihat aneh di awal percakapan kami. Tapi inilah yang saya maksud ketika saya mengatakan bahwa kesalahan utama yang dimiliki programmer dalam mengamati peraturan adalah bahwa programmer terlalu mementingkan aturan. Ini terutama benar pada awal pengerjaan proyek baru. Pada saat pembuatan
index.jsx
pertama
index.jsx
tidak mungkin untuk mengetahui apa yang terbaik untuk proyek ini. Ketika proyek berkembang, Anda secara alami akan menemukan beberapa jenis struktur file dan folder, yang mungkin cukup baik untuk proyek ini. Jika selama kelanjutan pekerjaan ternyata struktur yang ada agak tidak berhasil, maka bisa diperbaiki.
Jika Anda membaca ini dan mendapati diri Anda berpikir bahwa tidak ada dalam aplikasi Anda yang sedang dibahas, maka ini bukan masalah. Setiap aplikasi unik, tidak ada dua tim pengembangan yang benar-benar identik. Oleh karena itu, masing-masing tim, yang mengerjakan suatu proyek, datang ke beberapa perjanjian mengenai struktur dan metode mengerjakannya. Ini membantu anggota tim bekerja secara produktif. Jangan berusaha keras, setelah mengetahui tentang bagaimana seseorang melakukan sesuatu, segera perkenalkan hal ini kepada diri Anda sendiri. Jangan mencoba memperkenalkan ke dalam pekerjaan Anda apa yang disebut bahan-bahan tertentu, dan bahkan dalam hal ini, "cara paling efektif" untuk menyelesaikan masalah. Saya selalu mematuhi dan mematuhi strategi berikut mengenai rekomendasi tersebut. Saya memiliki seperangkat aturan sendiri, tetapi membaca tentang bagaimana orang lain bertindak dalam situasi tertentu, saya memilih apa yang menurut saya berhasil dan cocok untuk saya. Ini mengarah pada fakta bahwa seiring waktu, metode kerja saya meningkat. Pada saat yang sama, saya tidak memiliki kejutan dan tidak ada keinginan untuk menulis ulang semuanya dari awal.
Komponen penting terletak di folder terpisah
Pendekatan untuk menempatkan file komponen dalam folder yang saya datangi adalah bahwa komponen yang dapat dianggap "penting", "dasar", "dasar" dalam konteks aplikasi ditempatkan di folder yang terpisah. Folder-folder ini, pada gilirannya, terletak di folder
components
. Misalnya, jika kita berbicara tentang aplikasi untuk toko elektronik, maka komponen
<Product>
digunakan untuk menggambarkan produk dapat dikenali sebagai komponen yang serupa. Inilah yang saya maksud:
- src/ - components/ - product/ - product.jsx - product-price.jsx - navigation/ - navigation.jsx - checkout-flow/ - checkout-flow.jsx
Dalam hal ini, komponen "sekunder" yang hanya digunakan oleh komponen "utama" tertentu berada di folder yang sama dengan komponen "utama" ini. Pendekatan ini telah terbukti dalam praktiknya. Faktanya adalah karena penerapannya, struktur tertentu muncul dalam proyek, tetapi tingkat folder bersarang tidak terlalu besar. Aplikasinya tidak mengarah pada tampilan seperti
../../../
dalam perintah impor komponen, tidak membuatnya sulit untuk bergerak di sekitar proyek. Pendekatan ini memungkinkan Anda untuk membangun hierarki komponen yang jelas. Komponen itu, yang namanya sesuai dengan nama folder, dianggap "dasar". Komponen lain yang terletak di folder yang sama berfungsi untuk membagi komponen "pangkalan" menjadi beberapa bagian, yang menyederhanakan bekerja dengan kode komponen ini dan dukungannya.
Meskipun saya seorang pendukung kehadiran struktur folder tertentu dalam proyek, saya percaya bahwa hal yang paling penting adalah pemilihan nama file yang baik. Folder itu sendiri kurang penting.
Menggunakan subfolder untuk subkomponen
Salah satu kelemahan dari pendekatan di atas adalah penggunaannya dapat menyebabkan munculnya folder komponen "dasar" yang mengandung banyak file. Pertimbangkan, misalnya, komponen
<Product>
. File CSS akan dilampirkan padanya (kami akan membicarakannya nanti), menguji file, banyak subkomponen, dan, mungkin, sumber daya lain - seperti gambar dan ikon SVG. Daftar "tambahan" ini tidak terbatas. Semua ini akan jatuh ke folder yang sama dengan komponen "base".
Saya benar-benar tidak peduli tentang itu. Ini cocok untuk saya jika file memiliki nama yang dipikirkan dengan matang, dan jika mereka dapat dengan mudah ditemukan (menggunakan alat pencarian file di editor). Jika demikian, maka struktur folder memudar ke latar belakang.
Berikut ini tweet tentang topik ini.
Namun, jika Anda lebih suka proyek Anda memiliki struktur yang lebih luas, tidak ada yang sulit dalam memindahkan subkomponen ke folder mereka sendiri:
- src/ - components/ - product/ - product.jsx - ... - product-price/ - product-price.jsx
File uji terletak di tempat yang sama dengan file komponen yang diuji.
Kami memulai bagian ini dengan rekomendasi sederhana, yaitu file uji harus ditempatkan di tempat yang sama dengan file dengan kode yang diperiksa dengan bantuan mereka. Saya juga akan berbicara tentang bagaimana saya lebih suka menyusun komponen, berusaha memastikan bahwa mereka akan saling berdekatan. Tetapi sekarang saya dapat mengatakan bahwa saya merasa nyaman untuk menempatkan file uji di folder yang sama dengan file komponen. Dalam hal ini, nama-nama file dengan tes identik dengan nama-nama file dengan kode. Untuk nama pengujian, sebelum ekstensi nama file, akhiran
.test
hanya ditambahkan:
- Nama file komponen:
auth.js
- Uji nama file:
auth.test.js
Pendekatan ini memiliki beberapa kekuatan:
- Itu membuatnya mudah untuk menemukan file uji. Sekilas, Anda bisa mengerti jika ada tes untuk komponen yang saya kerjakan.
- Semua perintah impor yang diperlukan sangat sederhana. Dalam pengujian, untuk mengimpor kode yang diuji, Anda tidak perlu membuat struktur yang menggambarkan, katakanlah, jalan keluar dari folder
__tests__
. Tim seperti itu terlihat sangat sederhana. Misalnya, seperti ini: import Auth from './auth'
.
Jika kami memiliki beberapa data yang digunakan selama pengujian, misalnya, sesuatu seperti tiruan permintaan API, kami menempatkannya di folder yang sama di mana komponen dan pengujiannya berada. Ketika segala sesuatu yang mungkin diperlukan terletak di satu folder, ini berkontribusi pada pertumbuhan produktivitas. Misalnya, jika Anda menggunakan struktur folder bercabang dan pemrogram yakin bahwa ada file tertentu, tetapi tidak dapat mengingat namanya, pemrogram harus mencari file ini di banyak subdirektori. Dengan pendekatan yang diusulkan, lihat saja isi dari satu folder dan semuanya akan menjadi jelas.
Modul CSS
Saya penggemar berat
modul CSS . Kami menemukan bahwa mereka sangat bagus untuk membuat aturan CSS modular untuk komponen.
Selain itu, saya sangat suka teknologi
komponen-gaya . Namun, dalam proses pengerjaan proyek di mana banyak pengembang berpartisipasi, ternyata kehadiran file CSS nyata dalam proyek meningkatkan kegunaan.
Seperti yang mungkin sudah Anda duga, file CSS kami berada, seperti file lainnya, di sebelah file komponen, di folder yang sama. Ini sangat menyederhanakan perpindahan antar file ketika Anda perlu dengan cepat memahami makna suatu kelas.
Rekomendasi yang lebih umum, esensi yang menembus semua materi ini, adalah bahwa semua kode yang terkait dengan komponen tertentu harus disimpan dalam folder yang sama di mana komponen ini berada. Lewat sudah hari-hari ketika folder terpisah digunakan untuk menyimpan CSS dan kode JS, kode uji dan sumber daya lainnya. Penggunaan struktur folder yang rumit mempersulit perpindahan antar file dan tidak memiliki manfaat yang jelas, kecuali bahwa hal itu membantu "mengatur kode." Simpan file yang saling terhubung dalam folder yang sama - ini berarti menghabiskan lebih sedikit waktu untuk berpindah antar folder selama bekerja.
Kami bahkan membuat loader Webpack untuk CSS, kemampuan yang sesuai dengan fitur pekerjaan kami. Ia memeriksa nama kelas yang dideklarasikan dan melempar kesalahan di konsol jika kita merujuk ke kelas yang tidak ada.
Hampir selalu, hanya satu kode komponen yang ditempatkan dalam satu file
Pengalaman saya menunjukkan bahwa programmer biasanya terlalu ketat mematuhi aturan bahwa kode untuk satu dan hanya satu komponen Bereaksi harus dalam satu file. Pada saat yang sama, saya sepenuhnya mendukung gagasan bahwa tidak layak menempatkan terlalu banyak komponen dalam satu file (bayangkan kesulitan menamai file seperti itu!). Tetapi saya percaya bahwa tidak ada yang salah dengan meletakkan file yang sama di mana kode komponen "besar" berada, dan kode komponen "kecil" yang terkait dengannya. Jika langkah seperti itu membantu menjaga kemurnian kode, jika komponen "kecil" tidak terlalu besar untuk meletakkannya di file terpisah, maka ini tidak akan membahayakan siapa pun.
Misalnya, jika saya membuat komponen
<Product>
, dan saya perlu sepotong kecil kode untuk menampilkan harganya, maka saya bisa melakukan ini:
const Price = ({ price, currency }) => ( <span> {currency} {formatPrice(price)} </span> ) const Product = props => {
Hal yang baik tentang pendekatan ini adalah saya tidak perlu membuat file terpisah untuk komponen
<Price>
, dan bahwa komponen ini tersedia secara eksklusif untuk komponen
<Product>
. Kami tidak mengekspor komponen ini, sehingga tidak dapat diimpor di tempat lain dalam aplikasi. Ini berarti bahwa ketika ditanya apakah akan meletakkan
<Price>
dalam file terpisah, Anda dapat memberikan jawaban yang jelas dan positif jika Anda perlu mengimpornya di tempat lain. Jika tidak, Anda dapat melakukannya tanpa memasukkan kode
<Price>
ke file terpisah.
Pisahkan folder untuk komponen universal
Kami baru-baru ini menggunakan komponen universal. Mereka, pada kenyataannya, membentuk sistem desain kami (yang kami rencanakan untuk diterbitkan suatu hari nanti), tetapi sejauh ini kami telah memulai dari yang kecil - dengan komponen seperti
<Button>
dan
<Logo>
. Suatu komponen dianggap “universal” jika tidak terikat pada bagian tertentu dari situs, tetapi merupakan salah satu blok bangunan dari antarmuka pengguna.
Komponen serupa terletak di folder Anda sendiri (
src/components/generic
). Ini sangat menyederhanakan pekerjaan dengan semua komponen universal. Mereka berada di satu tempat - sangat nyaman. Seiring waktu, seiring pertumbuhan proyek, kami berencana untuk mengembangkan panduan gaya (kami adalah penggemar besar
styleguidist reaksi ) untuk lebih menyederhanakan pekerjaan dengan komponen universal.
Menggunakan Alias untuk Mengimpor Entitas
Struktur folder yang relatif datar dalam proyek kami memastikan bahwa perintah impor tidak memiliki struktur terlalu panjang seperti
../../
. Tetapi sulit dilakukan tanpa mereka. Oleh karena itu, kami menggunakan
babel-plugin-module-resolver untuk mengkonfigurasi alias yang menyederhanakan perintah impor.
Anda dapat melakukan hal yang sama dengan Webpack, tetapi berkat plugin Babel, perintah impor yang sama dapat berfungsi dalam pengujian.
Kami mengonfigurasikan ini dengan sepasang alias:
{ components: './src/components', '^generic/([\\w_]+)': './src/components/generic/\\1/\\1', }
Yang pertama sangat sederhana. Ini memungkinkan Anda untuk mengimpor komponen apa pun, memulai perintah dengan
components
kata. Dalam pendekatan normal, perintah impor terlihat seperti ini:
import Product from '../../components/product/product'
Sebagai gantinya, kita dapat menulisnya seperti ini:
import Product from 'components/product/product'
Kedua perintah mengimpor file yang sama. Ini sangat nyaman, karena memungkinkan Anda untuk tidak memikirkan struktur folder.
Alias kedua sedikit lebih rumit:
'^generic/([\\w_]+)': './src/components/generic/\\1/\\1',
Kami menggunakan regex di sini. Ia menemukan perintah impor yang dimulai dengan
generic
(tanda
^
di awal ekspresi memungkinkan Anda untuk hanya memilih perintah yang dimulai dengan
generic
), dan menangkap apa yang setelah
generic/
ke dalam grup. Setelah itu, kami menggunakan fragmen yang ditangkap (
\\1
) dalam konstruksi
./src/components/generic/\\1/\\1
.
Akibatnya, kita dapat menggunakan perintah impor untuk komponen universal semacam ini:
import Button from 'generic/button'
Mereka dikonversi ke perintah berikut:
import Button from 'src/components/generic/button/button'
Perintah ini, misalnya, berfungsi untuk mengimpor file JSX yang menggambarkan tombol universal. Kami melakukan semua ini karena pendekatan ini sangat menyederhanakan impor komponen universal. Selain itu, ini akan bermanfaat bagi kita jika kita memutuskan untuk mengubah struktur file proyek (ini, karena sistem desain kita berkembang, sangat mungkin).
Di sini saya ingin mencatat bahwa Anda harus berhati-hati ketika bekerja dengan nama samaran. Jika Anda hanya memiliki sedikit dari mereka, dan mereka dirancang untuk menyelesaikan masalah impor standar, maka semuanya baik-baik saja. Tetapi jika Anda memiliki banyak dari mereka, mereka dapat membawa lebih banyak kebingungan daripada kebaikan.
Folder lib universal untuk utilitas
Saya ingin mendapatkan kembali semua waktu yang saya habiskan untuk mencari tempat yang sempurna untuk kode yang bukan kode komponen. Saya membagikan ini semua sesuai dengan prinsip yang berbeda, menyoroti kode utilitas, layanan, fungsi tambahan. Semua ini memiliki banyak nama sehingga saya tidak akan menyebut semuanya. Sekarang saya tidak mencoba mencari tahu perbedaan antara "utilitas" dan "fungsi bantu" untuk menemukan tempat yang tepat untuk file tertentu. Sekarang saya menggunakan pendekatan yang jauh lebih sederhana dan lebih mudah dipahami: semua ini jatuh ke dalam folder
lib
tunggal.
Dalam jangka panjang, ukuran folder ini mungkin berubah menjadi sangat besar sehingga Anda harus menyusunnya entah bagaimana, tetapi ini benar-benar normal. Selalu lebih mudah untuk melengkapi sesuatu dengan struktur tertentu daripada menyingkirkan kesalahan penataan yang berlebihan.
Dalam proyek Thread kami, folder
lib
berisi sekitar 100 file. Mereka dibagi kira-kira sama menjadi file yang berisi implementasi fitur-fitur tertentu, dan menjadi file uji. Itu tidak menyebabkan kesulitan dalam menemukan file yang diperlukan. Berkat
lib/name_of_thing
pencari cerdas yang dibangun di sebagian besar editor, saya hampir selalu harus memasukkan sesuatu seperti
lib/name_of_thing
, dan apa yang saya butuhkan dapat ditemukan.
Selain itu, kami memiliki alias yang menyederhanakan pengimporan dari folder
lib
, memungkinkan Anda menggunakan perintah seperti ini:
import formatPrice from 'lib/format_price'
Jangan khawatir dengan struktur folder datar yang dapat menyebabkan banyak file disimpan dalam satu folder. Biasanya struktur seperti itulah yang diperlukan untuk proyek tertentu.
Menyembunyikan perpustakaan pihak ketiga di belakang API asli
Saya sangat suka sistem pemantauan bug
Sentry . Saya sering menggunakannya ketika mengembangkan bagian server dan aplikasi klien. Dengan bantuannya, Anda dapat menangkap pengecualian dan menerima pemberitahuan tentang terjadinya. Ini adalah alat yang hebat yang memungkinkan kami mengikuti masalah yang dihadapi di situs.
Setiap kali saya menggunakan perpustakaan pihak ketiga dalam proyek saya, saya berpikir tentang cara membuatnya sehingga, jika perlu, bisa diganti semudah mungkin dengan yang lain. Seringkali, seperti halnya dengan sistem Sentry yang sama yang kita sukai, ini tidak perlu. Tetapi, untuk berjaga-jaga, tidak ada salahnya untuk memikirkan cara untuk menghindari menggunakan layanan tertentu atau cara mengubahnya ke sesuatu yang lain.
Solusi terbaik untuk masalah ini adalah mengembangkan API Anda sendiri yang menyembunyikan alat orang lain. Ini adalah sesuatu seperti membuat modul
lib/error-reporting.js
yang mengekspor fungsi
reportError()
. Inti dari modul ini menggunakan Sentry. Tetapi Sentry hanya diimpor langsung dalam modul ini dan tidak di tempat lain. Ini berarti bahwa mengganti Sentry dengan alat lain akan terlihat sangat sederhana. Untuk melakukan ini, cukup mengubah satu file di satu tempat. Selama API publik dari file ini tetap tidak berubah, sisa proyek bahkan tidak akan tahu bahwa ketika memanggil
reportError()
, itu bukan Sentry yang digunakan, tetapi sesuatu yang lain.
Harap perhatikan bahwa API publik modul disebut fungsi yang diekspor dan argumennya. Mereka juga disebut antarmuka publik dari modul.
Menggunakan PropTypes (atau alat seperti TypeScript atau Flow)
Ketika saya melakukan pemrograman, saya memikirkan tiga versi diri saya:
- Jack dari masa lalu dan kode yang dia tulis (terkadang kode meragukan).
- Jack hari ini, dan kode yang dia tulis sekarang.
- Jack dari masa depan. Ketika saya memikirkan masa depan ini sendiri, saya bertanya pada diri saya saat ini tentang bagaimana saya bisa menulis kode yang akan membuat hidup saya lebih mudah di masa depan.
Ini mungkin terdengar aneh, tetapi saya merasa berguna, berpikir tentang cara menulis kode, tanyakan pertanyaan berikut: "Bagaimana itu akan dirasakan dalam enam bulan?".
Salah satu cara sederhana untuk membuat diri Anda hadir dan diri Anda lebih produktif adalah dengan menentukan jenis properti (
PropTypes
) yang digunakan oleh komponen. Ini akan menghemat waktu mencari kemungkinan kesalahan ketik. Ini akan melindungi Anda dari situasi di mana, menggunakan komponen, properti dari tipe yang salah diterapkan, atau mereka benar-benar lupa tentang transfer properti. Dalam kasus kami, aturan
eslint-react / prop-types adalah pengingat yang baik tentang perlunya menggunakan
PropTypes
.
Jika Anda melangkah lebih jauh, disarankan untuk menggambarkan properti seakurat mungkin. Misalnya, Anda dapat melakukan ini:
blogPost: PropTypes.object.isRequired
Tetapi akan jauh lebih baik untuk melakukan ini:
blogPost: PropTypes.shape({ id: PropTypes.number.isRequired, title: PropTypes.string.isRequired,
Pada contoh pertama, pemeriksaan minimum yang diperlukan dilakukan. Pada tahap kedua, pengembang diberikan informasi yang jauh lebih bermanfaat. Mereka akan sangat berguna, misalnya, jika seseorang lupa tentang bidang tertentu yang digunakan dalam objek.
Perpustakaan pihak ketiga hanya digunakan ketika mereka benar-benar dibutuhkan.
Tip ini lebih relevan dari sebelumnya dengan munculnya
React hooks . Sebagai contoh, saya terlibat dalam perubahan besar pada salah satu bagian situs Thread dan memutuskan untuk memberi perhatian khusus pada penggunaan perpustakaan pihak ketiga. , , . ( ), .
, React-. — , , React API Context, .
, , Redux, . , ( , ). , , , .
— , , . .
, . , . , « ». , , , . . «» - , . , , .
Redux. . , . , ,
user_add_to_cart
, . . , , Redux, . , Redux , .
, , , , :
- , , . .
- , , . , .
- , - , . , , .
- , . , , . , , , «» .
, , API Context
- .
, (, ,
). : , .
, , , . :
const wrapper = mount( <UserAuth.Provider value=> <ComponentUnderTest /> </UserAuth.Provider> )
:
const wrapper = mountWithAuth(ComponentUnderTest, { name: 'Jack', userId: 1, })
:
- . — , , , .
- —
mountWithAuth
. , .
,
test-utils.js
. , — . .
Ringkasan
. , , , , . , , . , : . . - , .
Pembaca yang budiman! React-?
