Rumah >hujung hadapan web >tutorial js >WTF Adakah Kereaktifan !?

WTF Adakah Kereaktifan !?

DDD
DDDasal
2024-12-22 05:44:10374semak imbas

Model Kereaktifan Diterangkan

kata pengantar

Sudah (sudah) 10 tahun saya mula membangunkan aplikasi dan tapak web, tetapi ekosistem JavaScript tidak pernah lebih menarik daripada hari ini!

Pada tahun 2022, komuniti terpikat dengan konsep "Isyarat" sehingga kebanyakan rangka kerja JavaScript menyepadukannya ke dalam enjin mereka sendiri. Saya sedang memikirkan tentang Preact, yang telah menawarkan pembolehubah reaktif yang diasingkan daripada kitaran hayat komponen sejak September 2022; atau lebih baru Angular, yang melaksanakan Isyarat secara eksperimen pada Mei 2023, kemudian secara rasmi bermula dari versi 18. Pustaka JavaScript lain juga telah memilih untuk memikirkan semula pendekatan mereka...

Antara 2023 dan sehingga kini, saya telah menggunakan Signals secara konsisten merentas pelbagai projek. Kesederhanaan pelaksanaan dan penggunaannya telah meyakinkan saya sepenuhnya, sehinggakan saya telah berkongsi manfaatnya dengan rangkaian profesional saya semasa bengkel teknikal, sesi latihan dan persidangan.

Tetapi baru-baru ini, saya mula bertanya pada diri sendiri sama ada konsep ini benar-benar "revolusioner" / adakah terdapat alternatif kepada Isyarat? Jadi, saya mendalami refleksi ini dan menemui pendekatan yang berbeza untuk sistem reaktif.

Siaran ini ialah gambaran keseluruhan model kereaktifan yang berbeza, bersama-sama dengan pemahaman saya tentang cara ia berfungsi.

NB: Pada ketika ini, anda mungkin sudah meneka, saya tidak akan membincangkan tentang "Arus Reaktif" Java; jika tidak, saya akan menamakan siaran ini "WTF Is Backpressure!?" ?

Teori

Apabila kita bercakap tentang model kereaktifan, kita (pertama sekali) bercakap tentang "pengaturcaraan reaktif", tetapi terutamanya tentang "reaktiviti".

pengaturcaraan reaktif ialah paradigma pembangunan yang membolehkan secara automatik menyebarkan perubahan sumber data kepada pengguna.

Jadi, kita boleh mentakrifkan reaktiviti sebagai keupayaan untuk mengemas kini kebergantungan dalam masa nyata, bergantung pada perubahan data.

NB: Ringkasnya, apabila pengguna mengisi dan/atau menyerahkan borang, kami mesti bertindak balas terhadap perubahan ini, memaparkan komponen pemuatan atau apa-apa lagi yang menyatakan sesuatu sedang berlaku. .. Contoh lain, apabila menerima data secara tak segerak, kita mesti bertindak balas dengan memaparkan semua atau sebahagian daripada data ini, melaksanakan tindakan baharu, dsb.

Dalam konteks ini, perpustakaan reaktif menyediakan pembolehubah yang mengemas kini dan menyebarkan secara automatik dengan cekap, menjadikannya lebih mudah untuk menulis kod yang ringkas dan dioptimumkan.

Untuk menjadi cekap, sistem ini mesti mengira semula/menilai semula pembolehubah ini jika, dan hanya jika, nilainya telah berubah! Dengan cara yang sama, untuk memastikan data yang disiarkan kekal konsisten dan terkini, sistem mesti mengelak daripada memaparkan sebarang keadaan perantaraan (terutamanya semasa pengiraan perubahan keadaan).

NB: Keadaan merujuk kepada data/nilai yang digunakan sepanjang hayat program/aplikasi.

Baiklah, tetapi kemudian… Apakah sebenarnya "model kereaktifan" ini?

PUSH, a.k.a Kereaktifan "Bersemangat".

Model kereaktifan pertama dipanggil "PUSH" (atau kereaktifan "bersemangat"). Sistem ini adalah berdasarkan prinsip berikut:

  • Pemulaan sumber data (seperti yang dikenali sebagai "Boleh Diperhatikan")
  • Komponen/Fungsi melanggan sumber data ini (ini adalah pengguna)
  • Apabila nilai berubah, data disebarkan serta-merta kepada pengguna (seperti yang dikenali sebagai "Pemerhati")

Seperti yang anda duga, model "PUSH" bergantung pada corak reka bentuk "Boleh Diperhatikan/Pemerhati".

Kes Penggunaan Pertama : Keadaan Awal dan Perubahan Keadaan

Mari kita pertimbangkan keadaan awal berikut,

let a = { firstName: "John", lastName: "Doe" };
const b = a.firstName;
const c = a.lastName;
const d = `${b} ${c}`;

WTF Is Reactivity !?

Menggunakan perpustakaan reaktif (seperti RxJS), keadaan awal ini akan kelihatan lebih seperti ini:

let a = observable.of({ firstName: "John", lastName: "Doe" });
const b = a.pipe(map((a) => a.firstName));
const c = a.pipe(map((a) => a.lastName));
const d = merge(b, c).pipe(reduce((b, c) => `${b} ${c}`));

NB: Demi siaran ini, semua coretan kod hendaklah dianggap sebagai "pseudo-code."

Sekarang, mari kita anggap bahawa pengguna (contohnya komponen) mahu merekodkan nilai keadaan D apabila sumber data ini dikemas kini,

d.subscribe((value) => console.log(value));

Komponen kami akan melanggan aliran data; ia masih perlu mencetuskan perubahan,

a.next({ firstName: "Jane", lastName: "Doe" });

Dari situ, sistem "PUSH" mengesan perubahan dan menyiarkannya secara automatik kepada pengguna. Berdasarkan keadaan awal di atas, berikut ialah perihalan operasi yang mungkin berlaku:

  • Perubahan keadaan berlaku dalam sumber data A!
  • Nilai A disebarkan kepada B (pengiraan sumber data B);
  • Kemudian, nilai B disebarkan kepada D (pengiraan sumber data D);
  • Nilai A disebarkan kepada C (pengiraan sumber data C);
  • Akhir sekali, nilai C disebarkan kepada D (pengiraan semula sumber data D);

WTF Is Reactivity !?

Salah satu cabaran sistem ini terletak pada susunan pengiraan. Sesungguhnya, berdasarkan kes penggunaan kami, anda akan mendapati bahawa D mungkin dinilai dua kali: kali pertama dengan nilai C dalam keadaan sebelumnya; dan kali kedua dengan nilai C terkini! Dalam model kereaktifan jenis ini, cabaran ini dipanggil "Masalah Berlian" ♦️.

Kes Penggunaan Kedua : Lelaran Seterusnya

Sekarang, mari kita anggap negeri bergantung pada dua sumber data utama,

let a = { firstName: "John", lastName: "Doe" };
const b = a.firstName;
const c = a.lastName;
const d = `${b} ${c}`;

Apabila mengemas kini E, sistem akan mengira semula keseluruhan keadaan, yang membolehkannya mengekalkan satu sumber kebenaran dengan menimpa keadaan sebelumnya.

  • Perubahan keadaan berlaku dalam sumber data E!
  • Nilai A disebarkan kepada B (pengiraan sumber data B);
  • Kemudian, nilai B disebarkan kepada D (pengiraan sumber data D);
  • Nilai A disebarkan kepada C (pengiraan sumber data C);
  • Nilai E disebarkan kepada C (pengiraan semula sumber data C);.
  • Akhir sekali, nilai C disebarkan kepada D (pengiraan semula sumber data D);

WTF Is Reactivity !?

Sekali lagi, "Masalah Berlian" berlaku... Kali ini pada sumber data C yang berpotensi dinilai 2 kali, dan sentiasa pada D.

Masalah Berlian

"Masalah Berlian" bukanlah satu cabaran baharu dalam model kereaktifan "bersemangat". Sesetengah algoritma pengiraan (terutamanya yang digunakan oleh MobX) boleh menandai "nod pokok kebergantungan reaktif" untuk meratakan pengiraan keadaan. Dengan pendekatan ini, sistem akan terlebih dahulu menilai sumber data "root" (A dan E dalam contoh kami), kemudian B dan C, dan akhirnya D. Menukar susunan pengiraan keadaan membantu menyelesaikan masalah seperti ini.

WTF Is Reactivity !?

TARIK, a.k.a Kereaktifan "Malas".

Model kereaktifan kedua dipanggil "TARIK". Tidak seperti model "PUSH", ia berdasarkan prinsip berikut:

  • Pengisytiharan pembolehubah reaktif
  • Sistem menangguhkan pengiraan keadaan
  • Keadaan terbitan dikira berdasarkan kebergantungannya
  • Sistem mengelakkan kemas kini yang berlebihan

Peraturan terakhir inilah yang paling penting untuk diingat: tidak seperti sistem sebelumnya, yang terakhir ini menangguhkan pengiraan keadaan untuk mengelakkan berbilang penilaian sumber data yang sama.

Kes Penggunaan Pertama : Keadaan Awal dan Perubahan Keadaan

Mari kekalkan keadaan awal sebelumnya...

WTF Is Reactivity !?

Dalam sistem jenis ini, sintaks keadaan awal akan berada dalam bentuk berikut:

let a = observable.of({ firstName: "John", lastName: "Doe" });
const b = a.pipe(map((a) => a.firstName));
const c = a.pipe(map((a) => a.lastName));
const d = merge(b, c).pipe(reduce((b, c) => `${b} ${c}`));

NB: Peminat reaksi berkemungkinan akan mengenali sintaks ini ?

Mengisytiharkan pembolehubah reaktif memberikan "kelahiran" kepada tuple: pembolehubah tidak berubah pada satu bahagian; kemas kini fungsi pembolehubah ini pada yang lain. Pernyataan selebihnya (B, C dan D dalam kes kami) dianggap sebagai keadaan terbitan kerana ia "mendengar" kebergantungan masing-masing.

d.subscribe((value) => console.log(value));

Ciri yang menentukan sistem "malas" ialah ia tidak menyebarkan perubahan serta-merta, tetapi hanya apabila diminta secara eksplisit.

let a = { firstName: "John", lastName: "Doe" };
const b = a.firstName;
const c = a.lastName;
const d = `${b} ${c}`;

Dalam model "PULL", menggunakan effect() (daripada komponen) untuk log nilai pembolehubah reaktif (dinyatakan sebagai kebergantungan) mencetuskan pengiraan perubahan keadaan:

  • D akan menyemak sama ada kebergantungannya (B dan C) telah dikemas kini;
  • B akan menyemak sama ada kebergantungannya (A) telah dikemas kini;
  • A akan menyebarkan nilainya kepada B (mengira nilai B);
  • C akan menyemak sama ada kebergantungannya (A) telah dikemas kini;
  • A akan menyebarkan nilainya kepada C (mengira nilai C)
  • B dan C akan menyebarkan nilai masing-masing kepada D (mengira nilai D);

WTF Is Reactivity !?

Pengoptimuman sistem ini boleh dilakukan apabila menanyakan kebergantungan. Sesungguhnya, dalam senario di atas, A disoal dua kali untuk menentukan sama ada ia telah dikemas kini. Walau bagaimanapun, pertanyaan pertama mungkin cukup untuk menentukan sama ada keadaan telah berubah. C tidak perlu melakukan tindakan ini... Sebaliknya, A hanya boleh menyiarkan nilainya.

Kes Penggunaan Kedua : Lelaran Seterusnya

Mari kita rumitkan keadaan dengan menambah pembolehubah reaktif kedua "root",

let a = observable.of({ firstName: "John", lastName: "Doe" });
const b = a.pipe(map((a) => a.firstName));
const c = a.pipe(map((a) => a.lastName));
const d = merge(b, c).pipe(reduce((b, c) => `${b} ${c}`));

Sekali lagi, sistem menangguhkan pengiraan keadaan sehingga ia diminta secara eksplisit. Menggunakan kesan yang sama seperti sebelumnya, mengemas kini pembolehubah reaktif baharu akan mencetuskan langkah berikut:

  • D akan menyemak sama ada kebergantungannya (B dan C) telah dikemas kini ;
  • B akan menyemak sama ada kebergantungannya (A) telah dikemas kini ;
  • C akan menyemak sama ada kebergantungannya (A dan E) telah dikemas kini ;
  • E akan menyebarkan nilainya kepada C, dan C akan mengambil nilai A melalui memoisasi (mengira nilai C) ;
  • C akan menyebarkan nilainya kepada D, dan D akan mengambil nilai B melalui penghafalan (mengira nilai D) ;

WTF Is Reactivity !?

Memandangkan nilai A tidak berubah, pengiraan semula pembolehubah ini adalah tidak perlu (perkara yang sama berlaku untuk nilai B). Dalam kes sedemikian, penggunaan algoritma memoisasi meningkatkan prestasi semasa pengiraan keadaan.

TOLAK-TARIK, a.k.a Kereaktifan "Berbutir Halus".

Model kereaktifan terakhir ialah sistem "PUSH-PULL". Istilah "PUSH" mencerminkan penyebaran segera pemberitahuan perubahan, manakala "TARIK" merujuk kepada pengambilan nilai keadaan atas permintaan. Pendekatan ini berkait rapat dengan apa yang dipanggil kereaktifan "berbutir halus", yang mematuhi prinsip berikut:

  • Pengisytiharan pembolehubah reaktif (kita bercakap tentang primitif reaktif)
  • Pergantungan dijejaki pada tahap atom
  • Penyebaran perubahan sangat disasarkan

Perhatikan bahawa kereaktifan jenis ini tidak eksklusif untuk model "PUSH-PULL". Kereaktifan berbutir halus merujuk kepada penjejakan tepat kebergantungan sistem. Jadi, terdapat model kereaktifan PUSH dan PULL yang juga berfungsi dengan cara ini (saya sedang memikirkan Jotai atau Recoil.

Kes Penggunaan Pertama : Keadaan Awal dan Perubahan Keadaan

Masih berdasarkan keadaan awal sebelumnya... Pengisytiharan keadaan awal dalam sistem kereaktifan "berbutir halus" akan kelihatan seperti ini:

let a = { firstName: "John", lastName: "Doe" };
const b = a.firstName;
const c = a.lastName;
const d = `${b} ${c}`;

NB: Penggunaan kata kunci isyarat bukan sekadar anekdot di sini ?

Dari segi sintaks, ia sangat serupa dengan model "PUSH", tetapi terdapat satu perbezaan yang ketara dan penting: kebergantungan! Dalam sistem kereaktifan "berbutir halus" , tidak perlu mengisytiharkan kebergantungan yang diperlukan secara eksplisit untuk mengira keadaan terbitan, kerana keadaan ini secara tersirat menjejaki pembolehubah yang mereka gunakan. Dalam kes kami, B dan C akan menjejaki perubahan pada nilai A secara automatik dan D akan menjejaki perubahan kepada kedua-dua B dan C.

let a = observable.of({ firstName: "John", lastName: "Doe" });
const b = a.pipe(map((a) => a.firstName));
const c = a.pipe(map((a) => a.lastName));
const d = merge(b, c).pipe(reduce((b, c) => `${b} ${c}`));

Dalam sistem sedemikian, mengemas kini pembolehubah reaktif adalah lebih cekap berbanding model asas "PUSH" kerana perubahan itu disebarkan secara automatik kepada pembolehubah terbitan yang bergantung padanya (hanya sebagai pemberitahuan, bukan nilai itu sendiri).

d.subscribe((value) => console.log(value));

Kemudian, atas permintaan (mari kita ambil contoh logger), penggunaan D dalam sistem akan mengambil nilai keadaan akar yang berkaitan (dalam kes kami A), hitung nilai ​​daripada keadaan terbitan (B dan C), dan akhirnya menilai D. Bukankah ia mod operasi intuitif?

WTF Is Reactivity !?

Kes Penggunaan Kedua : Lelaran Seterusnya

Mari kita pertimbangkan keadaan berikut,

a.next({ firstName: "Jane", lastName: "Doe" });

Sekali lagi, aspek "berbutir halus" sistem PUSH-PULL membolehkan pengesanan automatik setiap negeri. Jadi, keadaan terbitan C kini menjejaki keadaan akar A dan E. Mengemas kini pembolehubah E akan mencetuskan tindakan berikut:

  • Nyatakan perubahan primitif reaktif E!
  • Pemberitahuan perubahan disasarkan (E hingga D melalui C);
  • E akan menyebarkan nilainya kepada C, dan C akan mendapatkan semula nilai A melalui memoisasi (mengira nilai C);
  • C akan menyebarkan nilainya kepada D, dan D akan mendapatkan semula nilai B melalui memoisasi (mengira nilai D);

WTF Is Reactivity !?

Inilah perkaitan terdahulu bagi kebergantungan reaktif antara satu sama lain yang menjadikan model ini begitu cekap!

Sememangnya, dalam sistem "PULL" klasik (seperti DOM Maya React, contohnya), apabila mengemas kini keadaan reaktif daripada komponen, rangka kerja akan dimaklumkan tentang perubahan (mencetuskan " membezakan" fasa). Kemudian, apabila diminta (dan ditangguhkan), rangka kerja akan mengira perubahan dengan merentasi pokok pergantungan reaktif; setiap kali pembolehubah dikemas kini! "Penemuan" keadaan tanggungan ini mempunyai kos yang ketara...

Dengan sistem kereaktifan "berbutir halus" (seperti Isyarat), kemas kini pembolehubah reaktif/primitif secara automatik memberitahu mana-mana keadaan terbitan yang dipautkan kepada mereka tentang perubahan itu. Oleh itu, tidak perlu (semula) menemui kebergantungan yang berkaitan; penyebaran negeri disasarkan!

Kesimpulan(.value)

Pada tahun 2024, kebanyakan rangka kerja web telah memilih untuk memikirkan semula cara ia berfungsi, terutamanya dari segi model kereaktifan mereka. Peralihan ini telah menjadikan mereka secara amnya lebih cekap dan berdaya saing. Yang lain memilih untuk menjadi (masih) hibrid (saya memikirkan tentang Vue di sini), yang menjadikan mereka lebih fleksibel dalam banyak situasi.

Akhir sekali, apa sahaja model yang dipilih, pada pendapat saya, sistem reaktif (baik) dibina berdasarkan beberapa peraturan utama:

  1. Sistem menghalang keadaan terbitan tidak konsisten;
  2. Penggunaan keadaan dalam sistem menghasilkan keadaan reaktif;
  3. Sistem meminimumkan kerja yang berlebihan ;
  4. Dan, "untuk permulaan keadaan, tidak kira jalan yang ikuti, keputusan akhir sistem akan sentiasa sama! "

Perkara terakhir ini, yang boleh ditafsirkan sebagai prinsip asas pengaturcaraan deklaratif, ialah bagaimana saya melihat sistem reaktif (baik) perlu bersifat deterministik! Inilah "determinisme" yang menjadikan model reaktif boleh dipercayai, boleh diramal dan mudah digunakan dalam projek teknikal pada skala, tanpa mengira kerumitan algoritma.

Atas ialah kandungan terperinci WTF Adakah Kereaktifan !?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn