Terbitan Boleh Ubah dalam Kereaktifan

Linda Hamilton
Lepaskan: 2024-10-24 08:22:02
asal
814 orang telah melayarinya

Semua penerokaan ke dalam penjadualan dan async ini menyedarkan saya betapa kita masih tidak faham tentang kereaktifan. Banyak penyelidikan berasal daripada pemodelan litar dan sistem masa nyata yang lain. Terdapat sejumlah besar penerokaan dalam paradigma pengaturcaraan berfungsi juga. Saya rasa ini sebahagian besarnya telah membentuk perspektif moden yang kita ada tentang kereaktifan.

Apabila saya mula-mula melihat Svelte 3, dan kemudian pengkompil React, orang ramai mencabar bahawa rangka kerja ini mempunyai butiran halus dalam pemaparannya. Dan sejujurnya, mereka berkongsi banyak ciri yang sama. Jika kami menamatkan cerita dengan Isyarat dan primitif terbitan yang telah kami lihat setakat ini, anda boleh mempertikaikan kesetaraan, kecuali sistem ini tidak membenarkan kereaktifan mereka hidup di luar komponen UI mereka.

Tetapi terdapat sebab bahawa Solid tidak pernah memerlukan pengkompil untuk mencapai ini. Dan kenapa sehingga hari ini ia masih lebih optimum. Ia bukan perincian pelaksanaan. Ia adalah seni bina. Ia berkaitan dengan kebebasan reaktif daripada komponen UI tetapi ia lebih daripada itu.


Boleh Berubah vs Tidak Boleh Berubah

Dalam definisi, keupayaan untuk berubah berbanding tidak. Tetapi bukan itu yang kami maksudkan. Kami akan mempunyai perisian yang agak membosankan jika tiada apa-apa yang berubah. Dalam pengaturcaraan, ia adalah sama ada nilai boleh diubah. Jika sesuatu nilai tidak boleh diubah maka satu-satunya cara untuk menukar nilai pembolehubah adalah dengan menetapkannya semula.

Dari perspektif itu, Isyarat Kekal mengikut reka bentuk. Satu-satunya cara mereka mengetahui jika sesuatu telah berubah adalah dengan memintas apabila nilai baharu diberikan. Jika seseorang mengubah nilainya secara bebas, tiada reaktif akan berlaku.

const [signal, setSignal] = createSignal({ a: 1 });

createEffect(() => console.log(signal().a)); // logs "1"

// does not trigger the effect
signal().a = 2; 

setSignal({ a: 3 }); // the effect logs "3"
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Sistem reaktif kami ialah graf bersambung bagi nod tidak berubah. Apabila kami memperoleh data kami mengembalikan nilai seterusnya. Malah mungkin dikira menggunakan nilai sebelumnya.

const [log, setLog] = createSignal("start");

const allLogs = createMemo(prev => prev + log()); // derived value
createEffect(() => console.log(allLogs())); // logs "start"

setLog("-end"); // effect logs "start-end"
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Tetapi sesuatu yang menarik berlaku apabila kami meletakkan Isyarat dalam Isyarat dan Kesan dalam Kesan.

function User(user) {
  // make "name" a Signal
  const [name, setName] = createSignal(user.name);
  return { ...user, name, setName };
}

const [user, setUser] = createSignal(
  new User({ id: 1, name: "John" })
);

createEffect(() => {
  const u = user();
  console.log("User", u.id);
  createEffect(() => {
     console.log("Name", u.name());
  })
}); // logs "User 1", "Name John"

// effect logs "User 2", "Name Jack"
setUser(new User({ id: 2, name: "Jack" })); 

// effect logs "Name Janet"
user().setName("Janet");
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Kini kita tidak boleh hanya menukar pengguna tetapi juga menukar nama pengguna. Lebih penting lagi, kita boleh melangkau melakukan kerja yang tidak perlu apabila hanya nama yang bertukar. Kami tidak menjalankan semula Kesan luar. Tingkah laku ini bukan akibat di mana keadaan diisytiharkan tetapi di mana ia digunakan.

Ini sangat berkuasa, tetapi sukar untuk mengatakan sistem kami Kekal. Ya atom individu adalah, tetapi dengan menyarangkannya, kami telah mencipta struktur yang dioptimumkan untuk mutasi. Hanya bahagian tepat kod yang perlu dilaksanakan dijalankan apabila kami menukar apa-apa.

Kami boleh mendapatkan hasil yang sama tanpa bersarang, tetapi kami akan melakukannya dengan menjalankan kod tambahan kepada diff:

const [signal, setSignal] = createSignal({ a: 1 });

createEffect(() => console.log(signal().a)); // logs "1"

// does not trigger the effect
signal().a = 2; 

setSignal({ a: 3 }); // the effect logs "3"
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Terdapat pertukaran yang agak jelas di sini. Versi bersarang kami perlu memetakan data yang masuk untuk menjana Isyarat bersarang, dan kemudian pada dasarnya memetakannya untuk kali kedua untuk memisahkan cara data itu diakses dalam kesan bebas. Versi perbezaan kami boleh menggunakan data biasa tetapi perlu menjalankan semula semua kod pada sebarang perubahan dan membezakan semua nilai untuk menentukan perkara yang telah berubah.

Memandangkan pembezaan adalah agak berprestasi, dan pemetaan ke atas data terutamanya bersarang dalam adalah menyusahkan orang ramai umumnya memilih yang kedua. React pada dasarnya ini. Walau bagaimanapun, pembezaan hanya akan menjadi lebih mahal apabila data dan kerja kami yang berkaitan dengan data itu meningkat.

Setelah meletakkan jawatan untuk diffing ia tidak dapat dielakkan. Kami kehilangan maklumat. Anda boleh melihatnya dalam contoh di atas. Apabila kami menetapkan nama kepada "Janet" dalam contoh pertama kami memberitahu program untuk mengemas kini nama pengguna().setName("Janet"). Dalam kemas kini kedua, kami menetapkan pengguna baharu dan program perlu memikirkan perkara yang telah berubah di mana-mana sahaja pengguna digunakan.

Walaupun bersarang lebih rumit, ia tidak akan menjalankan kod yang tidak perlu. Perkara yang memberi inspirasi kepada saya untuk mencipta Solid ialah menyedari bahawa masalah terbesar dengan memetakan kereaktifan bersarang boleh diselesaikan dengan Proksi. Dan Kedai reaktif dilahirkan:

const [log, setLog] = createSignal("start");

const allLogs = createMemo(prev => prev + log()); // derived value
createEffect(() => console.log(allLogs())); // logs "start"

setLog("-end"); // effect logs "start-end"
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Lebih baik.

Sebab ini berfungsi kerana kami masih tahu bahawa nama itu dikemas kini apabila kami setUser(user => user.name = "Janet"). Penetap untuk harta nama dipukul. Kami mencapai kemas kini berbutir ini, tanpa memetakan data kami atau berbeza.

Mengapa ini penting? Gambar jika anda mempunyai senarai pengguna sebaliknya. Pertimbangkan perubahan yang tidak berubah:

function User(user) {
  // make "name" a Signal
  const [name, setName] = createSignal(user.name);
  return { ...user, name, setName };
}

const [user, setUser] = createSignal(
  new User({ id: 1, name: "John" })
);

createEffect(() => {
  const u = user();
  console.log("User", u.id);
  createEffect(() => {
     console.log("Name", u.name());
  })
}); // logs "User 1", "Name John"

// effect logs "User 2", "Name Jack"
setUser(new User({ id: 2, name: "Jack" })); 

// effect logs "Name Janet"
user().setName("Janet");
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Kami mendapat tatasusunan baharu dengan semua objek pengguna sedia ada kecuali objek baharu dengan nama yang dikemas kini. Semua rangka kerja tahu pada ketika ini ialah senarai telah berubah. Ia perlu mengulangi keseluruhan senarai untuk menentukan sama ada mana-mana baris perlu dialihkan, ditambah atau dialih keluar atau jika mana-mana baris telah berubah. Jika ia telah berubah, ia akan menjalankan semula fungsi peta dan menjana output yang akan diganti/dibezakan dengan apa yang ada dalam DOM pada masa ini.

Pertimbangkan perubahan boleh ubah:

const [user, setUser] = createSignal({ id: 1, name: "John" });

let prev;
createEffect(() => {
  const u = user();
  // diff values
  if (u.id !== prev?.id) console.log("User", u.id);
  if (u.name !== prev?.name) console.log("Name", u.name);

  // set previous
  prev = u;
}); // logs "User 1", "Name John"

// effect logs "User 2", "Name Jack"
setUser({ id: 2, name: "Jack" }); 

// effect logs "Name Janet"
setUser({ id: 2, name: "Janet" });
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Kami tidak memulangkan apa-apa. Sebaliknya, satu isyarat iaitu nama untuk pengguna itu mengemas kini dan menjalankan kesan khusus yang mengemas kini tempat yang kami tunjukkan nama itu. Tiada senarai rekreasi. Tiada perbezaan senarai. Tiada rekreasi baris. Tiada perbezaan DOM.

Dengan menganggap kereaktifan boleh ubah sebagai warganegara kelas pertama, kami mendapat pengalaman pengarangan yang serupa dengan keadaan tidak berubah tetapi dengan keupayaan yang tidak dapat dicapai oleh pengkompil yang paling bijak. Tetapi kami tidak berada di sini hari ini untuk bercakap tentang Kedai reaktif dengan tepat. Apakah kaitannya dengan derivasi?


Meninjau Semula Derivasi

Nilai terbitan seperti yang kita tahu ia tidak boleh berubah. Kami mempunyai fungsi yang apabila ia dijalankan ia mengembalikan keadaan seterusnya.

keadaan = fn(keadaan)

Apabila input berubah, ia akan dijalankan semula dan anda mendapat nilai seterusnya. Mereka memainkan beberapa peranan penting dalam graf reaktif kami.

Pertama, ia berfungsi sebagai titik hafalan. Kami boleh menjimatkan kerja pada pengiraan mahal atau async jika kami menyedari bahawa input tidak berubah. Kita boleh menggunakan nilai beberapa kali tanpa mengiranya semula.

Kedua, ia bertindak sebagai nod penumpuan. Mereka ialah "cantuman" dalam graf kami. Mereka mengikat pelbagai sumber yang berbeza bersama-sama menentukan hubungan mereka. Ini adalah kunci kepada perkara yang dikemas kini bersama-sama, tetapi ia juga beralasan bahawa dengan bilangan sumber yang terhad dan jumlah kebergantungan yang semakin meningkat antara mereka, semuanya akhirnya akan terjerat.

Ia sangat masuk akal. Dengan struktur data tidak berubah yang diperolehi anda hanya mempunyai "cantum" bukan "garpu". Apabila skala kerumitan anda ditakdirkan untuk bergabung. Menariknya, "Kedai" yang reaktif tidak mempunyai sifat ini. Bahagian individu dikemas kini secara bebas. Jadi bagaimana kita menggunakan pemikiran ini pada derivasi?

Mengikuti Bentuk

Mutable Derivations in Reactivity

Andre Staltz menerbitkan artikel yang menakjubkan beberapa tahun lalu di mana dia menghubungkan semua jenis primitif reaktif/boleh lelar ke dalam satu kontinum. Tolak/tarik semua bersatu di bawah satu model.

Saya telah lama diilhamkan oleh pemikiran sistematik yang diterapkan Andre dalam artikel ini. Dan saya telah lama bergelut dengan topik yang telah saya kupas dalam siri ini. Kadangkala memahami bahawa ruang reka bentuk wujud sudah cukup untuk membuka penerokaan yang betul. Kadangkala hanya bentuk penyelesaian yang anda perlu fahami pada mulanya.


Sebagai contoh, saya sedar lama dahulu bahawa jika kita ingin mengelakkan penyegerakan untuk kemas kini keadaan tertentu, kita memerlukan cara untuk mendapatkan keadaan boleh tulis. Ia berlegar dalam fikiran saya selama beberapa tahun tetapi akhirnya saya mencadangkan terbitan yang boleh ditulis.

const [signal, setSignal] = createSignal({ a: 1 });

createEffect(() => console.log(signal().a)); // logs "1"

// does not trigger the effect
signal().a = 2; 

setSignal({ a: 3 }); // the effect logs "3"
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk


Ideanya ialah ia sentiasa boleh ditetapkan semula daripada sumbernya, tetapi seseorang boleh menggunakan kemas kini jangka pendek di atas sehingga kali seterusnya sumber itu berubah. Mengapa menggunakan ini berbanding Kesan?

const [log, setLog] = createSignal("start");

const allLogs = createMemo(prev => prev + log()); // derived value
createEffect(() => console.log(allLogs())); // logs "start"

setLog("-end"); // effect logs "start-end"
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk


Oleh kerana, seperti yang dibincangkan secara mendalam dalam bahagian satu siri ini, Isyarat di sini tidak dapat mengetahui bahawa ia bergantung pada props.field. Ia memecahkan ketekalan graf kerana kita tidak dapat menjejak kembali kebergantungannya. Secara intuitif saya tahu meletakkan bacaan di dalam primitif yang sama membuka kunci keupayaan itu. Malah createWritable boleh dilaksanakan sepenuhnya di userland hari ini.

<script> // Detect dark theme var iframe = document.getElementById('tweet-1252839841630314497-983'); if (document.body.className.includes('dark-theme')) { iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1252839841630314497&theme=dark" } </script>
const [signal, setSignal] = createSignal({ a: 1 });

createEffect(() => console.log(signal().a)); // logs "1"

// does not trigger the effect
signal().a = 2; 

setSignal({ a: 3 }); // the effect logs "3"
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Ia hanyalah Isyarat peringkat lebih tinggi. Isyarat Isyarat atau seperti yang dipanggil Andre sebagai gabungan "Getter-getter" dan "Getter-setter". Apabila lulus dalam fn melaksanakan terbitan luar (createMemo) menjejakinya dan mencipta Isyarat. Setiap kali kebergantungan tersebut menukar Isyarat baharu dicipta. Walau bagaimanapun, sehingga diganti, Isyarat itu aktif dan apa-apa sahaja yang mendengar fungsi getter yang dikembalikan melanggan kedua-dua terbitan dan Isyarat yang mengekalkan rantaian pergantungan.

Kami mendarat di sini kerana mengikut bentuk penyelesaiannya. Dan dari masa ke masa mengikut bentuk itu, saya kini percaya seperti yang dinyatakan pada akhir artikel terakhir primitif terbitan boleh ubah ini kurang sebagai Terbitan Boleh Tulis tetapi Isyarat Terbitan.

const [log, setLog] = createSignal("start");

const allLogs = createMemo(prev => prev + log()); // derived value
createEffect(() => console.log(allLogs())); // logs "start"

setLog("-end"); // effect logs "start-end"
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Tetapi kami masih melihat kepada primitif yang tidak berubah. Ya, ini ialah terbitan boleh tulis tetapi perubahan nilai dan pemberitahuan masih berlaku secara borong.


Masalah dengan Perbezaan

Mutable Derivations in Reactivity

Secara intuitif kita dapat melihat terdapat jurang. Saya boleh mencari contoh perkara yang saya ingin selesaikan tetapi tidak pernah dapat mencapai satu primitif untuk mengendalikan ruang itu. Saya menyedari sebahagian daripada masalahnya ialah bentuknya.

Di satu pihak, kami boleh meletakkan nilai terbitan ke dalam Kedai:

function User(user) {
  // make "name" a Signal
  const [name, setName] = createSignal(user.name);
  return { ...user, name, setName };
}

const [user, setUser] = createSignal(
  new User({ id: 1, name: "John" })
);

createEffect(() => {
  const u = user();
  console.log("User", u.id);
  createEffect(() => {
     console.log("Name", u.name());
  })
}); // logs "User 1", "Name John"

// effect logs "User 2", "Name Jack"
setUser(new User({ id: 2, name: "Jack" })); 

// effect logs "Name Janet"
user().setName("Janet");
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Tetapi bagaimanakah ini boleh berubah bentuk secara dinamik, iaitu menjana getter yang berbeza tanpa menulis ke Kedai?

Sebaliknya, kita boleh memperoleh bentuk dinamik daripada Kedai tetapi outputnya bukan Kedai.

const [user, setUser] = createSignal({ id: 1, name: "John" });

let prev;
createEffect(() => {
  const u = user();
  // diff values
  if (u.id !== prev?.id) console.log("User", u.id);
  if (u.name !== prev?.name) console.log("Name", u.name);

  // set previous
  prev = u;
}); // logs "User 1", "Name John"

// effect logs "User 2", "Name Jack"
setUser({ id: 2, name: "Jack" }); 

// effect logs "Name Janet"
setUser({ id: 2, name: "Janet" });
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Jika semua nilai terbitan dibuat daripada menghantar dalam fungsi pembalut yang mengembalikan nilai seterusnya, bagaimanakah kita boleh mengasingkan perubahan? Sebaik-baiknya kami boleh membezakan hasil baharu dengan sebelumnya dan menggunakan kemas kini berbutir ke luar. Tetapi itu mengandaikan kami sentiasa mahu berbeza.

Saya membaca artikel daripada pasukan Signia tentang pengiraan tambahan yang melaksanakan sesuatu seperti komponen Solid's For secara generik. Walau bagaimanapun, di luar logiknya saya perhatikan:

  • Ia adalah satu Isyarat tidak berubah. Perubahan bersarang tidak boleh dicetuskan secara bebas.

  • Setiap nod dalam rantaian perlu mengambil bahagian. Setiap satu perlu menggunakan perbezaan sumbernya untuk merealisasikan nilai yang dikemas kini dan, dengan pengecualian nod akhir, menghasilkan perbezaannya untuk diturunkan.

Apabila berurusan dengan data tidak berubah. Rujukan akan hilang. Diffs membantu mendapatkan maklumat ini kembali tetapi kemudian anda membayar kos ke atas keseluruhan rangkaian. Dan dalam beberapa kes seperti dengan data baharu daripada pelayan, tiada rujukan yang stabil. Sesuatu perlu "mengunci" model dan itu tidak terdapat dalam Immer yang digunakan dalam contoh. React mempunyai keupayaan ini.

Ketika itulah saya terfikir perpustakaan ini dibina untuk React. Andaian telah pun dibuat kerana akan ada lebih banyak perbezaan. Sebaik sahaja anda meletakkan diri anda untuk diffing, diffing menghasilkan lebih diffing. Itulah kebenaran yang tidak dapat dielakkan. Mereka telah mencipta sistem untuk mengelakkan beban berat dengan menolak kos tambahan di seluruh sistem.

Mutable Derivations in Reactivity

Saya rasa saya cuba menjadi terlalu pandai. Pendekatan "buruk" walaupun tidak mampan tidak dapat dinafikan lebih berprestasi.


Teori Penyatuan Agung Kereaktifan (Berbutir Halus).

Mutable Derivations in Reactivity

Tidak salah untuk memodelkan barangan secara tidak berubah. Tetapi ada jurang.

Jadi jom ikut bentuk:

const [signal, setSignal] = createSignal({ a: 1 });

createEffect(() => console.log(signal().a)); // logs "1"

// does not trigger the effect
signal().a = 2; 

setSignal({ a: 3 }); // the effect logs "3"
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Apa yang menjadi jelas ialah fungsi terbitan adalah bentuk yang sama dengan fungsi penetap Isyarat. Dalam kedua-dua kes, anda memasukkan nilai sebelumnya dan mengembalikan nilai baharu.

Mengapa kita tidak melakukan ini dengan Kedai?

const [log, setLog] = createSignal("start");

const allLogs = createMemo(prev => prev + log()); // derived value
createEffect(() => console.log(allLogs())); // logs "start"

setLog("-end"); // effect logs "start-end"
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Kami juga boleh membawa masuk sumber yang diperolehi:

function User(user) {
  // make "name" a Signal
  const [name, setName] = createSignal(user.name);
  return { ...user, name, setName };
}

const [user, setUser] = createSignal(
  new User({ id: 1, name: "John" })
);

createEffect(() => {
  const u = user();
  console.log("User", u.id);
  createEffect(() => {
     console.log("Name", u.name());
  })
}); // logs "User 1", "Name John"

// effect logs "User 2", "Name Jack"
setUser(new User({ id: 2, name: "Jack" })); 

// effect logs "Name Janet"
user().setName("Janet");
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Terdapat simetri di sini. Perubahan tidak berubah sentiasa membina keadaan seterusnya, dan perubahan boleh berubah mengubah keadaan semasa ke keadaan seterusnya. Tidak juga melakukan diffing. Jika keutamaan berubah pada terbitan tidak berubah (Memo) maka keseluruhan rujukan diganti dan semua kesan sampingan berjalan. Jika keutamaan berubah pada terbitan boleh ubah (Unjuran) hanya perkara yang mendengar keutamaan secara khusus dikemas kini.


Meneroka Unjuran

Perubahan tidak berubah adalah konsisten dalam operasinya, kerana ia hanya perlu membina keadaan seterusnya tanpa mengira apa perubahan. Boleh ubah mungkin mempunyai operasi yang berbeza bergantung pada perubahan. Perubahan tidak berubah sentiasa mempunyai keadaan sebelumnya yang tidak diubah suai untuk berfungsi manakala perubahan boleh berubah tidak. Ini memberi kesan kepada jangkaan.

Kita boleh lihat ini dalam bahagian sebelumnya dengan keperluan untuk menggunakan reconcile dalam contoh. Apabila objek baharu sepenuhnya dihantar dengan Unjuran, anda tidak berpuas hati dengan menggantikan semuanya. Anda perlu menggunakan perubahan secara berperingkat. Bergantung pada kemas kini yang mungkin diperlukan untuk bermutasi dengan cara yang berbeza. Anda boleh menggunakan semua perubahan setiap kali dan memanfaatkan semakan kesaksamaan dalaman Kedai:

const [user, setUser] = createSignal({ id: 1, name: "John" });

let prev;
createEffect(() => {
  const u = user();
  // diff values
  if (u.id !== prev?.id) console.log("User", u.id);
  if (u.name !== prev?.name) console.log("Name", u.name);

  // set previous
  prev = u;
}); // logs "User 1", "Name John"

// effect logs "User 2", "Name Jack"
setUser({ id: 2, name: "Jack" }); 

// effect logs "Name Janet"
setUser({ id: 2, name: "Janet" });
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Tetapi ini menjadi cepat terlarang kerana ia hanya berfungsi secara cetek. Mendamaikan (berbeza) sentiasa menjadi pilihan. Tetapi selalunya apa yang kita mahu lakukan hanya menggunakan apa yang kita perlu. Ini membawa kepada kod yang lebih rumit tetapi boleh menjadi lebih cekap.

Mengubah suai petikan daripada klon Trello Solid, kami boleh menggunakan Unjuran untuk sama ada menggunakan setiap kemas kini optimistik secara individu atau menyelaraskan papan dengan kemas kini terkini daripada pelayan.

const [signal, setSignal] = createSignal({ a: 1 });

createEffect(() => console.log(signal().a)); // logs "1"

// does not trigger the effect
signal().a = 2; 

setSignal({ a: 3 }); // the effect logs "3"
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Ini hebat kerana ia bukan sahaja mengekalkan rujukan dalam UI jadi hanya kemas kini berbutir berlaku tetapi ia menggunakan mutasi (kemas kini optimistik) secara berperingkat tanpa pengklonan dan pembezaan. Jadi bukan sahaja Komponen tidak perlu dijalankan semula, tetapi semasa anda membuat setiap perubahan, ia tidak perlu membina semula keseluruhan keadaan papan berulang kali untuk menyedari sekali lagi hanya sedikit yang berubah. Dan akhirnya, apabila ia perlu berbeza, apabila pelayan akhirnya mengembalikan data baharu kami, ia berbeza dengan unjuran yang dikemas kini itu. Rujukan disimpan dan tiada apa yang perlu dipaparkan semula.

Walaupun saya percaya pendekatan ini akan menjadi kemenangan besar untuk sistem masa nyata dan diutamakan tempatan pada masa hadapan, kami sudah menggunakan Unjuran hari ini mungkin tanpa disedari. Pertimbangkan fungsi peta reaktif yang termasuk isyarat untuk indeks:

const [log, setLog] = createSignal("start");

const allLogs = createMemo(prev => prev + log()); // derived value
createEffect(() => console.log(allLogs())); // logs "start"

setLog("-end"); // effect logs "start-end"
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Indeks ditayangkan pada senarai baris anda yang tidak mengandungi indeks sebagai sifat reaktif. Kini primitif ini sangat asas, saya mungkin tidak akan melaksanakannya dengan createProjection tetapi adalah penting untuk memahami bahawa ia adalah satu kategori.

Contoh lain ialah API createSelector Solid yang tidak jelas. Ia membolehkan anda menayangkan keadaan pemilihan pada senarai baris dengan cara yang berprestasi supaya menukar perkara yang dipilih tidak mengemas kini setiap baris. Terima kasih kepada primitif Unjuran rasmi, kami tidak memerlukan primitif khas lagi:

function User(user) {
  // make "name" a Signal
  const [name, setName] = createSignal(user.name);
  return { ...user, name, setName };
}

const [user, setUser] = createSignal(
  new User({ id: 1, name: "John" })
);

createEffect(() => {
  const u = user();
  console.log("User", u.id);
  createEffect(() => {
     console.log("Name", u.name());
  })
}); // logs "User 1", "Name John"

// effect logs "User 2", "Name Jack"
setUser(new User({ id: 2, name: "Jack" })); 

// effect logs "Name Janet"
user().setName("Janet");
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Ini mencipta Peta tempat anda mencari mengikut id tetapi hanya baris yang dipilih wujud. Memandangkan ia adalah proksi untuk hayat langganan, kami boleh menjejaki sifat yang tidak wujud dan masih memberitahu mereka apabila ia dikemas kini. Menukar selectionId akan mengemas kini paling banyak 2 baris: yang telah dipilih dan yang baharu sedang dipilih. Kami menukar operasi O(n) kepada O(2).

Semasa saya bermain dengan primitif ini lebih banyak, saya menyedari bahawa ia bukan sahaja mencapai terbitan boleh ubah langsung tetapi boleh digunakan untuk melalui kereaktifan secara dinamik.

const [user, setUser] = createSignal({ id: 1, name: "John" });

let prev;
createEffect(() => {
  const u = user();
  // diff values
  if (u.id !== prev?.id) console.log("User", u.id);
  if (u.name !== prev?.name) console.log("Name", u.name);

  // set previous
  prev = u;
}); // logs "User 1", "Name John"

// effect logs "User 2", "Name Jack"
setUser({ id: 2, name: "Jack" }); 

// effect logs "Name Janet"
setUser({ id: 2, name: "Janet" });
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Unjuran ini hanya mendedahkan id dan nama pengguna tetapi memastikan privateValue tidak boleh diakses. Ia melakukan sesuatu yang menarik kerana ia menggunakan pengambil pada nama. Jadi semasa unjuran dijalankan semula apabila kami menggantikan keseluruhan pengguna, hanya mengemas kini nama pengguna boleh menjalankan kesan tanpa unjuran berjalan semula.

Kes penggunaan ini hanya sedikit dan saya akui ia memerlukan sedikit masa lagi untuk membungkus kepala anda. Tetapi saya merasakan bahawa Unjuran adalah pautan yang hilang dalam cerita Isyarat.


Kesimpulan

Mutable Derivations in Reactivity

Sepanjang penerokaan Derivasi dalam Kereaktifan ini sejak beberapa tahun lalu, saya telah belajar banyak. Seluruh perspektif saya tentang Reaktiviti telah berubah. Kebolehubahan daripada dilihat sebagai kejahatan yang perlu telah berkembang pada saya untuk menjadi tonggak kereaktifan yang berbeza. Sesuatu yang tidak dicontohi oleh pendekatan berbutir kursus atau penyusun.

Ini ialah pernyataan kuat yang mencadangkan perbezaan asas antara kereaktifan tidak berubah dan boleh berubah. Isyarat dan Stor bukanlah perkara yang sama. Begitu juga Memo dan Unjuran. Walaupun kita mungkin dapat menyatukan API mereka, mungkin kita tidak sepatutnya.

Saya mencapai kesedaran ini dengan mengikut bentuk API Solid, tetapi penyelesaian lain mempunyai API yang berbeza untuk Isyarat mereka. Jadi saya tertanya-tanya sama ada mereka akan membuat kesimpulan yang sama. Untuk bersikap adil, terdapat cabaran untuk melaksanakan Unjuran dan cerita di sini belum berakhir. Tetapi saya fikir ini menamatkan penerokaan pemikiran kami buat masa ini. Saya mempunyai banyak kerja di hadapan saya.

Terima kasih kerana menyertai saya dalam perjalanan ini.

Atas ialah kandungan terperinci Terbitan Boleh Ubah dalam Kereaktifan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
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
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!