Apabila Angular bertemu Redux

Mukadimah

Kami mendengar lebih banyak tentang penggunaan konsep Redux yang semakin meningkat dengan Angular (2+). Semasa mencari beberapa artikel untuk membimbing saya mengenai subjek itu, saya menyedari bahawa terlalu sedikit yang boleh diakses dalam bahasa indah Molière kami. Oleh itu, tujuan artikel ini adalah untuk membuat anda memahami Redux, dan penggunaannya dengan rangka kerja yang bukan bidang kegemarannya. Jika anda menggunakan Angular dan tidak biasa dengan konsep ini, anda telah datang ke tempat yang betul.

Pra-syarat

Artikel ini ditujukan untuk pembangun yang biasa menggunakan Angular tanpa mempunyai idea yang jelas tentang Redux itu. Konsep seperti Observable, asas Typescript dan Komponen akan diambil mudah. Pengetahuan tentang pengaturcaraan Fungsian adalah satu kelebihan.

Pengenalan

Redux ialah seni bina yang bertujuan untuk menumpukan keadaan aplikasi anda di satu tempat. Tindakan akan dihantar oleh acara, yang setiap kali akan menyambung semula keadaan sebelumnya, mengubah suai sebahagian daripadanya dan mengembalikan yang baharu yang akan digunakan untuk mengurus data anda.
Ia tidak begitu jelas? Jangan risau, tujuan artikel ini adalah untuk merungkai konsep yang kelihatan rumit yang ternyata mudah dan logik.
Sebelum membincangkan perkara ini, saya ingin menegaskan bahawa Redux, yang sering digunakan dengan React, sama sekali tidak bergantung pada yang terakhir. Sudah tiba masanya kita memahami faedah seni bina yang sangat praktikal untuk digunakan dengan mana-mana rangka kerja/perpustakaan berorientasikan komponen, dan Angular ialah contoh yang sempurna.

Apakah isu dengan Angular yang boleh diselesaikan oleh pengurusan data aplikasi baharu ini?

Pembolehubah dikongsi oleh komponen berbeza dari tahap yang berbeza

Dalam komponen Sudut secara umum, jarang sekali terdapat pembolehubah dalam readonly untuk sesuatu selain daripada pemalar perkhidmatan atau Boleh Diperhatikan. Pembolehubah yang dipaparkan secara langsung dalam komponen juga diubah suai oleh komponen yang sama ini. Tetapi di mana anda boleh menghadapi masalah sebenar ialah apabila pembolehubah ini dihantar sebagai Input kepada sub-komponen, dan ia diubah suai oleh sub-komponen ini. Ia mungkin dan ia berfungsi kerana pengikatan data Angular dibuat untuk itu. Walau bagaimanapun, ia menjadi sangat sukar untuk mengujinya, dan kod itu boleh mempunyai kesan sampingan.
Bayangkan pokok seperti:

Komponen0 ├── Komponen1 └── Komponen2 └── Komponen3

Kami mempunyai pembolehubah yang kami mahukan bersama dengan komponen 1 dan 3. Jadi, kami perlu mengisytiharkan pembolehubah ini sebagai sifat Component0 , hanya untuk mengemas kininya dalam Component2 dan juga dalam Component3. Kita perlu meletakkan output dalam komponen 1 jika kita ingin mengubah suai pembolehubah berikutan peristiwa. Dan lakukan perkara yang sama, bukan sahaja dalam Component3, tetapi juga dalam Component2 untuk naik ke Component0 dengan EventEmitter.

Banyak kod, untuk mengubah suai pembolehubah yang digunakan di 2 tempat pada halaman yang sama.

Jelas sekali, Angular sudah membenamkan penyelesaian yang boleh kami gunakan dalam kes ini. Penggunaan perkhidmatan tertentu, di mana kami akan menyimpan pembolehubah yang dikongsi. Jika kita mahu ia terikat dalam kedua-dua komponen, iaitu perubahan dalam masa nyata dalam Component1 apabila ia ditukar dalam Component3, maka kita perlu menggunakan paradigma pengaturcaraan reaktif, terutamanya Observables dengan RXJS. Ini boleh dilakukan, dan banyak aplikasi berfungsi dengan cara ini, tetapi ia boleh menjadi sukar untuk dikekalkan apabila corak berulang secara kerap, di beberapa tempat dalam aplikasi. Walau bagaimanapun, idea ini tidak boleh diketepikan sepenuhnya, kerana ia akan menyediakan asas untuk penyelesaian kami.

Data terikat sangat tidak menentu dan penyahpepijatan boleh menjadi rumit

Data boleh berubah sepanjang masa, dan melainkan anda sentiasa bekerja dalam ketidakbolehubah, penjejakan perubahan dalam nilai objek boleh menjadi rumit. Sukar juga untuk menyimpan sejarah tindakan dan peristiwa yang dijalankan untuk seketika. Redux juga menyediakan penyelesaian pada tahap ini dengan "penyahpepijatan perjalanan masa". Redux menyimpan setiap kali keadaan baharu dibuat salinan versi sebelumnya, dan semuanya tersedia di dalamnya Alat Pembangun melalui sambungan

Ia boleh menjadi sukar untuk mengatur kod yang mengubah suai pembolehubah dengan cara yang tulen.

Di sini, kita benar-benar pergi ke sebahagian daripada penjelasan pengaturcaraan berfungsi, tetapi penyelesaian yang disediakan oleh paradigma ini ialah konsep "fungsi tulen". Fungsi tulen mempunyai satu atau lebih argumen, tidak mengubah suai sebarang pembolehubah di luar skopnya dan mengembalikan nilai yang akan sentiasa sama dengan argumen yang sama. Ia agak mudah dalam teori, dan tidak selalu dalam amalan.
Jika anda ingin menyelam lebih dalam untuk menerangkan dan memahami paradigma pengaturcaraan berfungsi, saya hanya boleh mengesyorkan artikel oleh Yoan Ribeiro mengenai perkara ini.
Lebih-lebih lagi, penggunaan Angular sering membawa kita melakukan OOP dan bukannya pengaturcaraan berfungsi, dalam erti kata kita mengubah suai data komponen (yang merupakan kelas) yang mengikut definisi di luar kaedah pembuatan.

Sedikit teori sebelum praktik

Sekarang bahawa kami telah membangkitkan beberapa perkara yang wujud untuk pembangunan dengan Angular dan yang boleh dipermudahkan dengan Redux, kami akan cuba memahami corak dengan baik, dengan mereka bentuk aplikasi Redux asas.

Anda kadangkala akan melihat Kedai, kadangkala Negeri. Kedua-dua perkataan itu merangkumi makna yang sama secara global, iaitu keadaan aplikasi dan struktur datanya.

Skim ini sangat mudah. Dari komponen kami, kami pergi menghantar tindakan. Kita boleh menterjemah ini dengan fakta mudah menghantar panggilan, acara yang dengan sendirinya akan memanggil fungsi.
Kemudian tindakan ini akan digunakan untuk a reducer yang akan bertindak ke atas adalah daripada apl itu. Akhirnya keadaan yang sama inilah yang akan dibaca oleh komponen dalam UI.
Dalam dua ayat dan gambar rajah mudah, kita boleh merumuskan konsep dengan mudah. Tetapi jelas, tindakan, pengurang dan negeri semuanya mempunyai peranan. Dan walaupun terdapat banyak kod dan fungsi untuk melakukan perkara-perkara kecil, mereka masing-masing mempunyai kepentingan yang besar.
Seperti yang dapat dilihat, aliran itu sentiasa perlu satu arah, dan ini adalah perkara penting yang akan menjadi asas seni bina kami. Tindakan pengguna akan melalui komponen yang akan menghantar tindakan. Tindakan ini akan dihantar kepada pengurang, fungsi tulen kami yang akan mengembalikan keadaan baharu untuk aplikasi.
Terdahulu, kami bercakap tentang kebimbangan terhadap kesan sampingan apabila mengubah suai pembolehubah yang dikongsi oleh beberapa komponen. Redux menyediakan penyelesaian sebenar pada tahap ini dengan meletakkan dirinya pada konsep pengaturcaraan berfungsi, dan bukan pada berorientasikan objek. Pengurangan kesan kelebihan, setiap komponen, walau apa pun, hanya akan bertindak pada keadaan yang diperlukan untuknya. Kami akan menghantar tindakan untuk mengemas kini harta negeri. Kemudian pada setiap tempat di mana sifat ini akan digunakan dalam bentuk pembolehubah dalam komponen, ia akan terikat dan oleh itu diubah suai, semuanya dalam tindakan khusus yang hanya akan mengubah suai sifat ini.

Contoh pengurang

const reducer: Reducer<AppState> = (state: AppState, action: Action) => {
  switch (action.type) {
    case "IS_LOADING":
        return {
            ...state,
            isLoading: action.payload
        };
    default:
        return state;
  }
};

Tempat untuk berlatih

Berikut ialah gambar rajah yang lebih rumit yang menterjemahkan kes penggunaan sebenar dalam rangka kerja pembangunan aplikasi.

NDD: muatan ialah nama yang diberikan kepada pembolehubah dari sebarang jenis yang akan dihantar ke tindakan dan digunakan oleh pengurang.

Mari kita ambil satu kes di mana kita membina aplikasi yang kelihatan seperti blog, tetapi berurusan dengan manga (untuk menukar artikel).
Kami akan mempunyai komponen yang akan memaparkan data manga melalui IDnya (terdapat dalam URL). Mari kita bayangkan bahawa tajuk manga, pengarangnya dan bilangan jilidnya adalah data yang dimuatkan apabila halaman dipaparkan, tetapi bukan penerangannya. Yang ini sangat panjang, anda perlu mengklik pada butang untuk memaparkannya dan oleh itu memuatkannya.
Apabila butang ini diklik, beberapa perkara akan berlaku. Komponen kami akan menghantar tindakan yang akan memuatkan perihalan manga melalui fungsi tersebut loadMangaDescription(). Fungsi ini akan pertama sekali menghantar tindakan DESCRIPTION_IS_LOADING dengan muatan kepada benar. Kami ingin menunjukkan kepada pengguna bahawa maklumat sedang dimuatkan. Kita boleh sebagai contoh, selagi nilai ini benar, memaparkan pemutar. Oleh itu, komponen akan mendapatkan semula nilai ini daripada Negeri, yang dikemas kini oleh pengurang yang dipanggil oleh tindakan DESCRIPTION_IS_LOADING.
Sekali dalam cara yang benar-benar segerak, kami telah mengemas kini Negeri kami untuk mengatakan bahawa penerangan sedang dimuatkan, kami boleh meneruskan. Komponen bukan sahaja menghantar tindakan ini, tetapi ia juga memanggil perkhidmatan yang akan mengambil penerangan melalui panggilan HTTP. Siapa kata HTTP di sini mengatakan tak segerak, jadi kami tidak tahu bila respons akan tiba. Ini biasanya sebab pemutar dipaparkan, seperti dalam contoh ini.
Apabila permintaan kembali, perkhidmatan akan mengembalikan hasil kepada komponen yang akhirnya akan dapat melancarkan dua tindakan berikut (dengan menyambung kepada hasil melalui Janji atau Langganan). Oleh itu, komponen akan dapat menghantar tindakan itu semula DESCRIPTION_IS_LOADING kali ini dengan muatan di palsu.
Tetapi bukan itu sahaja. Matlamat operasi kami, daripada komponen, adalah untuk mendapatkan semula perihalan manga. Tidak mengapa untuk memasang pemutar semasa memuatkan dan mengalih keluarnya sebaik sahaja data diterima, tetapi masih perlu menggunakan data tersebut. Untuk itu, kami akan menghantar tindakan kedua selepas tindakan pertama yang menetapkan pemuatan kepada palsu, yang akan menjadi LOAD_DESCRIPTION. Tindakan ini akan mengandungi dalam muatan data yang ingin kami hantar kepada pengurang kemudian cari di negeri itu. Data inilah yang kemudiannya akan dipaparkan pada UI, yang diberikan kepada komponen.
Kod produk untuk contoh ini boleh didapati di sini Repositori Github untuk mendapatkan pemahaman yang lebih mendalam tentang menggunakan Redux di luar teori dan skema. Ia agak mudah dan sepadan dengan permulaan NgRedux, dengan di atas semua keperluan untuk seni bina berfungsi.

Epik

Konsep lain yang dibawa oleh Redux ialah Epics. Dalam kes sebelumnya, kami ingin contohnya tidak diwajibkan untuk menyatakan bahawa sebaik sahaja panggilan Perkhidmatan dibuat dan tindakan dihantar, kami juga mahu pemuat hilang dengan menghantar tindakan baharu. Jika kami terpaksa menghantar tindakan yang mengisi data ke beberapa tempat, kami kemudiannya perlu menduplikasi kod untuk membuat pemuat hilang. Di sinilah Epik masuk. Epik ialah fungsi yang akan dicetuskan oleh pengurang dan yang akan mengambil aliran tindakan untuk mengembalikan aliran tindakan yang lain. Dalam kes kami, ia akan mencukupi untuk mengatakan bahawa setiap kali tindakan itu LOAD_DESCRIPTION dihantar, kami juga mahu menghantar tindakan itu DESCRIPTION_IS_LOADING dengan muatan ditetapkan kepada palsu. Mudah dan cekap, tiada pertindihan kod atau kes tambahan untuk diuruskan.
Maklumat lanjut tentang konsep Epik dan kes penggunaannya:
- https://medium.com/kevin-salters-blog/epic-middleware-in-redux-e4385b6ff7c6
- https://redux-observable.js.org/docs/basics/Epics.html

Angular-Redux

Sehingga itu, satu soalan masih kekal. Kami tahu cara mendengar acara pada komponen untuk menghantar tindakan kami. Kami tahu cara memasukkan perkhidmatan ke dalamnya jika kami memerlukan data tak segerak dan cara menghantar tindakan kami dengan cekap kepada pengurang supaya perubahan keadaan data tidak bertembung. Kecuali setelah kami mengemas kini keadaan kami, cara untuk mendapatkan semula elemen masih agak tidak jelas.
Bagaimanakah anda tahu apabila pembolehubah telah berubah nilai? Dan bagaimana untuk menggunakan nilai baharu ini dalam komponen kami?
Ia adalah untuk menjawab soalan seperti ini yang disukai oleh perpustakaan Angular-Redux. Ini ialah perisian tengah yang membolehkan kami bertindak dan mendapatkan semula elemen dari keadaan kami secara statik atau dinamik. Berikut ialah kod contoh untuk mendapatkan semula perihalan manga kami secara dinamik.

import { select } from '@angular-redux/store';
import { Observable } from 'rxjs/Observable';
import { MangaState } from '../store/state';
@Component({
    selector: 'app-manga',
})
export class MangaComponent {
    @select(['manga']) readonly manga: Observable;
    constructor() {}
}

Diambil daripada kod dari repositori di atas dan dipermudahkan, kod ini membolehkan kami mempunyai gambaran keseluruhan untuk mendapatkan semula data kami dari kedai.
Kata kunci pertama dalam baris ialah @pilih yang merupakan penghias yang disediakan oleh angular-redux dan yang membolehkan kami mendapatkan semula elemen manga kami dari kedai dan menjadikannya Boleh Diperhatikan. Jenis pembolehubah kami sepadan dengan, mengambil sebagai input antara muka MangaState yang sepadan dengan jenis objek kami.
Dari sana, ia akan menjadi mudah untuk memaparkan data dalam HTML. Walau bagaimanapun, terdapat sedikit perubahan yang perlu dibuat untuk mengurus Observable.

<div *ngIf="manga | async; let manga">
    Nom du manga : {{ manga.name }}
    Auteur du manga : {{ manga.author }}
    <div>LOADING DESCRIPTION</div>
    <div>{{ manga.description }}</div>
</div>

Seperti yang anda lihat, anda perlu mengurus Observable secara tidak segerak. The Observable ialah aliran yang boleh mempunyai nilai tetap pada masa tertentu, tetapi untuk memaparkan nilai ini dalam HTML, anda perlu meletakkan paip | tak segerak yang akan mendapatkan semula nilai terakhir secara automatik. Catatan kedua, di belakang paip, anda boleh mencipta pembolehubah setempat supaya anda tidak perlu melakukan paip async untuk setiap nilai untuk dipaparkan (yang mungkin). Jadi {{ manga.name }} sebenarnya akan memaparkan sifat nama pembolehubah tempatan manga yang dicipta, bukan milik Observable.

Maklum balas

Setelah mengerjakan aplikasi Angular dari awal dengan dan tanpa Redux, saya akan mengatakan bahawa terdapat kemahiran untuk "berfikir Redux" tetapi itu berlaku dengan cepat. Seperti apabila beralih daripada bahasa objek kepada bahasa berfungsi, anda perlu membiasakan diri dengan pengekodan secara berbeza. Tetapi permainan ini pasti bernilai usaha, dan gabungan Redux + menaip + ujian adalah kombo yang menang untuk aplikasi sangat kukuh, mudah dinyahpepijat dan boleh diselenggara. Jelas sekali, pelaksanaannya mengambil masa yang lebih lama kerana Stor mesti difikirkan dengan baik mengikut keperluan aplikasi. Pemisahan data antara pengurang yang berbeza juga boleh menjadi titik yang akan menentukan kebolehselenggaraan bergantung pada penggunaan semula pengurang ini oleh halaman yang berbeza. Redux ialah seni bina terbuka di mana anda juga boleh menambah corak yang sepadan dengan keperluan anda, seperti menggantikan Epics dengan sistem Dispatcher lain yang akan bertanggungjawab sepenuhnya untuk menghantar tindakan, contohnya.

Kesimpulan

Kami melihat bahawa seni bina seperti Redux boleh antara muka dengan sebarang jenis aplikasi. Anda hanya perlu memahami sepenuhnya kegunaan corak untuk mengurus data dan keadaan global aplikasi anda.
Anda hanya perlu memikirkan tambahan untuk mengintegrasikannya ke dalam Angular, seperti Angular-Redux untuk menguruskan pengikatan antara pembolehubah komponen dan keadaan global.
Penggunaan Typescript mengambil makna penuhnya, khususnya melalui penaipan stor dan data yang terkandung di dalamnya, yang boleh didapati dalam pengurang serta tindakan, dan juga Observables dalam komponen (lihat kod dari repositori). Oleh itu, kami mempunyai semua kod keselamatan yang dibawa oleh menaip untuk elakkan kesilapan seboleh mungkin.
Satu lagi perkara penting: Redux bukan sihir. Sudah tentu, anda perlu sedar bahawa Redux tidak lebih daripada seni bina yang ditambahkan pada aplikasi anda, tetapi ia tidak secara ajaib menghapuskan semua masalah corak yang mungkin ada pada aplikasi anda. Walaupun perpustakaan menyediakan penyelesaian yang kukuh untuk pengurusan data aplikasi, hakikatnya tetap bahawa pelaksanaannya memerlukan masa. Kita mesti berfikir dengan teliti tentang struktur data dan normalisasinya, kerana ini akan memberi kesan yang ketara pada seni bina tindakan, kedai, dan pengurang. Anda perlu meluangkan masa untuk menaip dan menguji data ini serta fungsi yang mengubah suainya untuk memastikan aplikasi sihat, berfungsi dan tanpa kejutan.
Ditulis oleh William Barranco