Tindakan Pelayan muncul sebagai idea untuk mengurangkan kod klien dan memudahkan interaksi yang memerlukan komunikasi dengan pelayan. Ia adalah penyelesaian terbaik yang membolehkan pembangun menulis kod yang lebih sedikit. Walau bagaimanapun, terdapat beberapa cabaran yang berkaitan dengan pelaksanaannya dalam rangka kerja lain, yang tidak boleh diabaikan.
Dalam artikel ini, kita akan bercakap tentang masalah ini dan bagaimana dalam Brisa kami telah menemui penyelesaian.
Untuk memahami apa yang disediakan oleh Tindakan Pelayan, adalah berguna untuk menyemak cara komunikasi dengan pelayan dahulu. Anda mungkin biasa melakukan tindakan berikut untuk setiap interaksi dengan pelayan:
Tujuh tindakan ini diulang untuk setiap interaksi. Contohnya, jika anda mempunyai halaman dengan 10 interaksi yang berbeza, anda akan mengulangi kod yang hampir sama sebanyak 10 kali, menukar hanya butiran seperti jenis permintaan, URL, data yang dihantar dan status pelanggan.
Contoh biasa ialah
a:
<input onInput={(e) => { // debounce if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { fetch("/api/search", { method: "POST", body: JSON.stringify({ query: e.target.value }), }) .then((res) => res.json()) .then((data) => { setState({ data }); }); }, 300); }} />
Dan dalam pelayan:
app.post("/api/search", async (req, res) => { const { query } = req.body; const data = await search(query); res.json(data); });
Meningkatkan saiz bundle pelanggan... dan kekecewaan pembangun.
Tindakan Pelayan merangkum tindakan ini dalam Panggilan Prosedur Jauh (RPC), yang menguruskan komunikasi pelanggan-pelayan, mengurangkan kod pada klien dan memusatkan logik pada pelayan :
Di sini segala-galanya dilakukan untuk anda oleh Brisa RPC.
Ini ialah kod daripada komponen pelayan:
<input onInput={(e) => { // debounce if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { fetch("/api/search", { method: "POST", body: JSON.stringify({ query: e.target.value }), }) .then((res) => res.json()) .then((data) => { setState({ data }); }); }, 300); }} />
Di sini, pembangun tidak menulis kod klien, kerana ia adalah komponen pelayan. Acara onInput diterima selepas nyahpantun, dikendalikan oleh RPC Pelanggan, manakala RPC Pelayan menggunakan "Isyarat Tindakan" untuk mencetuskan Komponen Web yang mempunyai isyarat yang didaftarkan dengan harta kedai tersebut.
Seperti yang anda lihat, ini mengurangkan kod pelayan dengan ketara dan, yang paling penting, saiz kod pada klien tidak meningkat dengan setiap interaksi. Kod Klien RPC menduduki 2 KB tetap, sama ada anda mempunyai 10 atau 1000 interaksi sedemikian. Ini bermakna meningkatkan 0 bait dalam saiz berkas pelanggan, dengan kata lain, tidak meningkat.
Selain itu, dalam hal memerlukan penyampaian semula, ini dilakukan pada pelayan dan dikembalikan dalam penstriman HTML, menjadikan pengguna melihat perubahan lebih awal daripada cara tradisional di mana anda perlu melakukan kerja ini pada klien selepas respons pelayan.
Dengan cara ini:
Dalam rangka kerja lain seperti React, mereka telah menumpukan pada tindakan hanya menjadi sebahagian daripada borang onSubmit, bukannya sebarang acara.
Ini adalah masalah, kerana terdapat banyak acara bukan bentuk yang juga harus dikendalikan daripada komponen pelayan tanpa menambah kod klien. Contohnya, onInput input untuk melakukan cadangan automatik, onScroll untuk memuatkan skrol tak terhingga, onMouseOver untuk melakukan tuding, dsb.
Banyak rangka kerja juga telah melihat perpustakaan HTMX sebagai alternatif yang sangat berbeza kepada tindakan pelayan, malah ia telah membawa idea yang sangat baik yang boleh digabungkan dengan Tindakan Pelayan untuk mempunyai lebih potensi dengan hanya menambah atribut tambahan dalam HTML yang Pelanggan RPC boleh mengambil kira, seperti debounceInput yang telah kami lihat sebelum ini. Juga idea HTMX lain seperti penunjuk untuk menunjukkan pemutar semasa membuat permintaan, atau dapat menangani ralat dalam Klien RPC.
Apabila Tindakan Pelayan diperkenalkan dalam React, terdapat anjakan paradigma baharu yang mana ramai pembangun terpaksa menukar cip mental apabila bekerja dengan mereka.
Kami mahu menjadikannya sebagai biasa mungkin dengan Platform Web, dengan cara ini, anda boleh menangkap acara bersiri daripada pelayan dan menggunakan sifatnya. Satu-satunya acara yang sedikit berbeza ialah onSubmit yang telah memindahkan FormData dan mempunyai sifat e.formData, namun, selebihnya sifat acara boleh berinteraksi. Ini ialah contoh menetapkan semula borang:
<input onInput={(e) => { // debounce if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { fetch("/api/search", { method: "POST", body: JSON.stringify({ query: e.target.value }), }) .then((res) => res.json()) .then((data) => { setState({ data }); }); }, 300); }} />
Dalam contoh ini, tiada kod pelanggan sama sekali dan semasa tindakan pelayan anda boleh melumpuhkan butang hantar dengan penunjuk, menggunakan CSS, supaya borang tidak boleh diserahkan dua kali, dan pada masa yang sama selepas melakukan tindakan pada pelayan dan akses data borang dengan e.formData dan kemudian menetapkan semula borang menggunakan API acara yang sama.
Secara mental, ia sangat sama dengan bekerja dengan Platform Web. Satu-satunya perbezaan ialah semua peristiwa semua komponen pelayan adalah tindakan pelayan.
Dengan cara ini, terdapat pemisahan kebimbangan yang nyata, di mana TIDAK perlu untuk meletakkan "pelayan pengguna" atau "menggunakan klien" dalam anda komponen lagi.
Hanya perlu diingat bahawa semuanya berjalan hanya pada pelayan. Satu-satunya pengecualian adalah untuk folder src/web-components yang dijalankan pada klien dan di sana acara adalah perkara biasa.
Di Brisa, Tindakan Pelayan disebarkan antara Komponen Pelayan seolah-olah ia adalah acara DOM. Maksudnya, daripada Tindakan Pelayan anda boleh memanggil acara prop Komponen Pelayan dan kemudian Tindakan Pelayan bagi Komponen Pelayan induk dilaksanakan, dsb.
<input onInput={(e) => { // debounce if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { fetch("/api/search", { method: "POST", body: JSON.stringify({ query: e.target.value }), }) .then((res) => res.json()) .then((data) => { setState({ data }); }); }, 300); }} />
Dalam kes ini, acara onAfterMyAction dilaksanakan pada komponen induk dan tindakan boleh dilakukan pada pelayan. Ini sangat berguna untuk membuat tindakan pada pelayan yang mempengaruhi beberapa komponen pelayan.
Terutama selepas beberapa minggu kebelakangan ini Komponen Web agak tidak disukai selepas beberapa perbincangan di X (sebelumnya Twitter). Walau bagaimanapun, sebagai sebahagian daripada HTML, ia adalah cara terbaik untuk berinteraksi dengan Tindakan Pelayan atas beberapa sebab:
Menggunakan atribut dalam Komponen Web memerlukan penyirian dengan cara yang sama seperti menghantar data dari pelayan ke klien tanpa menggunakan Komponen Web, oleh itu, menggunakan kedua-duanya, terdapat tiada siri tambahan untuk diuruskan.
Nota: Menstrim HTML dan memprosesnya dengan algoritma pembezaan adalah sesuatu yang saya jelaskan dalam artikel lain ini jika anda berminat.
Di Brisa, kami telah menambah konsep baharu untuk memberikan lebih kuasa kepada Tindakan Pelayan, konsep ini dipanggil "Isyarat Tindakan". Idea "Isyarat Tindakan" ialah anda mempunyai 2 kedai, satu pada pelayan dan satu pada klien.
Mengapa 2 kedai?
kedai pelayan lalai hidup hanya pada tahap permintaan. Dan anda boleh berkongsi data yang akan tidak kelihatan kepada klien. Sebagai contoh, anda boleh menetapkan perisian tengah kepada pengguna dan mempunyai akses kepada data pengguna sensitif dalam mana-mana Komponen Pelayan. Dengan tinggal pada tahap permintaan adalah mustahil untuk mempunyai konflik antara permintaan yang berbeza, kerana setiap permintaan mempunyai kedai sendiri dan TIDAK disimpan dalam mana-mana pangkalan data, apabila permintaan telah selesai, ia mati secara lalai.
Sebaliknya, di kedai pelanggan, ia adalah kedai yang setiap harta apabila digunakan adalah isyarat, iaitu jika ia dikemas kini, Komponen Web yang mendengar isyarat itu bertindak balas.
Walau bagaimanapun, konsep baharu "Isyarat Tindakan" ialah kita boleh melanjutkan hayat stor pelayan melebihi permintaan. Untuk melakukan ini, anda perlu menggunakan kod ini:
<input onInput={(e) => { // debounce if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { fetch("/api/search", { method: "POST", body: JSON.stringify({ query: e.target.value }), }) .then((res) => res.json()) .then((data) => { setState({ data }); }); }, 300); }} />
Kaedah transferToClient ini, kongsi data pelayan ke kedai pelanggan dan ditukar kepada isyarat. Dengan cara ini, banyak kali tidak diperlukan untuk membuat sebarang pemaparan semula daripada pelayan, anda hanya boleh daripada Tindakan Pelayan membuat bertindak balas terhadap isyarat Komponen Web yang mendengar isyarat itu.
Pemindahan kedai ini menjadikan kehidupan kedai pelayan sekarang:
Render awal Komponen Pelayan → Klien → Tindakan Pelayan → Klien → Tindakan Pelayan...
Jadi ia beralih daripada hidup dari hanya pada tahap permintaan kepada hidup secara kekal, serasi dengan navigasi antara halaman.
Contoh:
app.post("/api/search", async (req, res) => { const { query } = req.body; const data = await search(query); res.json(data); });
Dalam contoh ini, kami memanjangkan hayat harta simpan ralat, bukan untuk digunakan pada klien, tetapi untuk digunakan semula dalam Tindakan Pelayan dan akhirnya dalam penyampaian semula Tindakan Pelayan. Dalam kes ini, sebagai data tidak sensitif, ia tidak perlu untuk menyulitkannya. Kod contoh ini semua berlaku pada pelayan, malah perenderan semula dan pengguna akan melihat ralat selepas pemaparan ini pada pelayan di mana RPC Pelayan akan menghantar potongan HTML dalam penstriman dan RPC Pelanggan akan memprosesnya untuk membuat perbezaan dan menunjukkan ralat untuk memberi maklum balas kepada pengguna.
Jika dalam tindakan pelayan beberapa pembolehubah digunakan yang wujud pada tahap pemaparan, pada tahap keselamatan banyak rangka kerja seperti Next.js 14 perkara yang mereka lakukan ialah menyulitkan data ini untuk mencipta petikan data yang digunakan di masa rendering. Ini lebih kurang baik, tetapi menyulitkan data sentiasa mempunyai kos pengiraan yang berkaitan dan ia tidak selalunya data sensitif.
Di Brisa, untuk menyelesaikannya, terdapat permintaan yang berbeza, di mana pada pemaparan awal ia mempunyai nilai, dan dalam tindakan pelayan anda boleh menangkap nilai yang terdapat dalam permintaan ini.
<input debounceInput={300} onInput={async (e) => { // All this code only runs on the server const data = await search(e.target.value); store.set("query", data); store.transferToClient(["query"]); }} />
Ini berguna dalam sesetengah kes tetapi tidak selalu, contohnya jika anda melakukan Math.random ia akan berbeza antara pemaparan awal dan pelaksanaan Tindakan Pelayan pastinya.
<input onInput={(e) => { // debounce if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { fetch("/api/search", { method: "POST", body: JSON.stringify({ query: e.target.value }), }) .then((res) => res.json()) .then((data) => { setState({ data }); }); }, 300); }} />
Inilah sebabnya kami mencipta konsep "Isyarat Tindakan", untuk memindahkan data daripada kedai pelayan ke kedai pelanggan, dan pembangun boleh memutuskan sama ada hendak menyulitkannya atau tidak sesuka hati.
Kadangkala, bukannya menanyakan pangkalan data daripada Tindakan Pelayan, anda mungkin mahu memindahkan data yang sudah wujud dalam pemaparan awal walaupun ia memerlukan penyulitan yang berkaitan. Untuk melakukan ini, anda hanya menggunakan:
app.post("/api/search", async (req, res) => { const { query } = req.body; const data = await search(query); res.json(data); });
Apabila anda melakukannya:
<input debounceInput={300} onInput={async (e) => { // All this code only runs on the server const data = await search(e.target.value); store.set("query", data); store.transferToClient(["query"]); }} />
Di dalam Komponen Web (pelanggan) akan sentiasa disulitkan, tetapi pada pelayan ia akan sentiasa dinyahsulit.
Nota: Brisa menggunakan aes-256-cbc untuk penyulitan, gabungan algoritma kriptografi yang digunakan untuk menyulitkan maklumat yang disyorkan oleh OpenSSL dengan selamat. Kunci penyulitan dijana semasa pembinaan projek anda.
Di Brisa, walaupun kami suka menyokong penulisan Komponen Web dengan mudah, matlamatnya adalah untuk dapat membuat SPA tanpa kod pelanggan dan hanya menggunakan Komponen Web apabila ia adalah interaksi pelanggan semata-mata atau API Web perlu disentuh. Itulah sebabnya Tindakan Pelayan sangat penting, kerana ia membenarkan interaksi dengan pelayan tanpa perlu menulis kod klien.
Kami menggalakkan anda mencuba Brisa, anda hanya perlu menjalankan arahan ini dalam terminal: bun create brisa, atau cuba beberapa contoh untuk melihat cara ia berfungsi.
Atas ialah kandungan terperinci Tindakan Pelayan telah dibetulkan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!