Reaktivitas Sesuai Permintaan di Vue 3
Diterbitkan: 2022-03-11Selain peningkatan kinerja yang mengagumkan, Vue 3 yang baru saja dirilis juga membawa beberapa fitur baru. Bisa dibilang pengantar yang paling penting adalah Composition API . Di bagian pertama artikel ini, kami merangkum motivasi standar untuk API baru: pengorganisasian dan penggunaan kembali kode yang lebih baik. Di bagian kedua, kami akan fokus pada aspek yang kurang dibahas dalam menggunakan API baru, seperti mengimplementasikan fitur berbasis reaktivitas yang tidak dapat diungkapkan dalam sistem reaktivitas Vue 2.
Kami akan menyebutnya sebagai reaktivitas sesuai permintaan . Setelah memperkenalkan fitur baru yang relevan, kami akan membangun aplikasi spreadsheet sederhana untuk mendemonstrasikan ekspresi baru dari sistem reaktivitas Vue. Di bagian paling akhir, kita akan membahas penggunaan dunia nyata apa yang mungkin dimiliki oleh peningkatan reaktivitas sesuai permintaan ini.
Apa yang Baru di Vue 3 dan Mengapa Itu Penting
Vue 3 adalah penulisan ulang utama Vue 2, memperkenalkan sejumlah besar peningkatan sambil mempertahankan kompatibilitas mundur dengan API lama hampir secara keseluruhan.
Salah satu fitur baru yang paling signifikan di Vue 3 adalah Composition API . Pengenalannya memicu banyak kontroversi ketika pertama kali dibahas secara publik. Jika Anda belum terbiasa dengan API baru, pertama-tama kami akan menjelaskan motivasi di baliknya.
Unit organisasi kode yang biasa adalah objek JavaScript yang kuncinya mewakili berbagai kemungkinan jenis bagian dari suatu komponen. Dengan demikian objek mungkin memiliki satu bagian untuk data reaktif ( data
), bagian lain untuk properti yang dihitung ( computed
), satu lagi untuk metode komponen ( methods
), dll.
Di bawah paradigma ini, sebuah komponen dapat memiliki beberapa fungsi yang tidak terkait atau terkait secara longgar yang cara kerjanya didistribusikan di antara bagian-bagian komponen yang disebutkan di atas. Misalnya, kami mungkin memiliki komponen untuk mengunggah file yang mengimplementasikan dua fungsi yang pada dasarnya terpisah: manajemen file dan sistem yang mengontrol animasi status unggahan.
Bagian <script>
mungkin berisi sesuatu seperti berikut:
export default { data () { return { animation_state: 'playing', animation_duration: 10, upload_filenames: [], upload_params: { target_directory: 'media', visibility: 'private', } } }, computed: { long_animation () { return this.animation_duration > 5; }, upload_requested () { return this.upload_filenames.length > 0; }, }, ... }
Ada manfaat dari pendekatan tradisional untuk organisasi kode ini, terutama bagi pengembang yang tidak perlu khawatir tentang di mana harus menulis potongan kode baru. Jika kita menambahkan variabel reaktif, kita masukkan di bagian data
. Jika kita mencari variabel yang sudah ada, kita tahu itu pasti ada di bagian data
.
Pendekatan tradisional untuk membagi implementasi fungsionalitas menjadi beberapa bagian ( data
, computed
, dll.) tidak cocok di semua situasi.
Pengecualian berikut sering dikutip:
- Berurusan dengan komponen dengan sejumlah besar fungsi. Jika kita ingin meningkatkan kode animasi kita dengan kemampuan untuk menunda dimulainya animasi, misalnya, kita harus menggulir/melompat di antara semua bagian komponen yang relevan dalam editor kode. Dalam hal komponen pengunggahan file kami, komponen itu sendiri kecil dan jumlah fungsi yang diimplementasikannya juga kecil. Jadi, dalam hal ini, melompat antar bagian sebenarnya bukan masalah. Masalah fragmentasi kode ini menjadi relevan ketika kita berurusan dengan komponen besar.
- Situasi lain di mana pendekatan tradisional kurang adalah penggunaan kembali kode. Seringkali kita perlu membuat kombinasi spesifik dari data reaktif, properti yang dihitung, metode, dll., tersedia di lebih dari satu komponen.
Vue 2 (dan Vue 3) yang kompatibel ke belakang menawarkan solusi untuk sebagian besar organisasi kode dan masalah penggunaan kembali: mixins .
Pro dan Kontra Mixin di Vue 3
Mixin memungkinkan fungsionalitas komponen diekstraksi dalam unit kode yang terpisah. Setiap fungsi ditempatkan dalam mixin terpisah dan setiap komponen dapat menggunakan satu atau lebih mixin. Potongan yang didefinisikan dalam mixin dapat digunakan dalam komponen seolah-olah mereka didefinisikan dalam komponen itu sendiri. Mixin sedikit mirip dengan kelas dalam bahasa berorientasi objek karena mereka mengumpulkan kode yang terkait dengan fungsi yang diberikan. Seperti kelas, mixin dapat diwarisi (digunakan) di unit kode lainnya.
Namun, penalaran dengan mixin lebih sulit karena, tidak seperti kelas, mixin tidak perlu dirancang dengan mempertimbangkan enkapsulasi. Mixin diperbolehkan menjadi kumpulan potongan kode yang terikat secara longgar tanpa antarmuka yang terdefinisi dengan baik ke dunia luar. Menggunakan lebih dari satu mixin sekaligus dalam komponen yang sama dapat mengakibatkan komponen sulit dipahami dan digunakan.
Kebanyakan bahasa berorientasi objek (misalnya, C# dan Java) tidak mendukung atau bahkan melarang pewarisan berganda meskipun fakta bahwa paradigma pemrograman berorientasi objek memiliki alat untuk menangani kompleksitas tersebut. (Beberapa bahasa memang mengizinkan pewarisan berganda, seperti C++, tetapi komposisi masih lebih disukai daripada pewarisan.)
Masalah yang lebih praktis yang mungkin terjadi saat menggunakan mixin di Vue adalah tabrakan nama, yang terjadi saat menggunakan dua atau lebih mixin yang mendeklarasikan nama umum. Perlu dicatat di sini bahwa jika strategi default Vue untuk menangani tabrakan nama tidak ideal dalam situasi tertentu, strategi tersebut dapat disesuaikan oleh pengembang. Hal ini menyebabkan kerumitan yang lebih besar.
Masalah lainnya adalah bahwa mixin tidak menawarkan sesuatu yang mirip dengan konstruktor kelas. Ini menjadi masalah karena seringkali kita membutuhkan fungsionalitas yang sangat mirip, tetapi tidak persis sama, untuk hadir dalam komponen yang berbeda. Ini dapat dielakkan dalam beberapa kasus sederhana dengan penggunaan pabrik mixin.
Oleh karena itu, mixin bukanlah solusi ideal untuk pengorganisasian dan penggunaan kembali kode, dan semakin besar proyek, semakin serius masalah mereka. Vue 3 memperkenalkan cara baru untuk memecahkan masalah yang sama mengenai organisasi kode dan penggunaan kembali.
API Komposisi: Jawaban Vue 3 untuk Organisasi dan Penggunaan Kembali Kode
Composition API memungkinkan kita (tetapi tidak mengharuskan kita) untuk sepenuhnya memisahkan bagian-bagian dari suatu komponen. Setiap bagian kode—variabel, properti yang dihitung, jam tangan, dll.—dapat didefinisikan secara independen.
Misalnya, alih-alih memiliki objek yang berisi bagian data
yang berisi animation_state
kunci dengan nilai (default) "bermain", sekarang kita dapat menulis (di mana saja dalam kode JavaScript kita):
const animation_state = ref('playing');
Efeknya hampir sama dengan mendeklarasikan variabel ini di bagian data
beberapa komponen. Satu-satunya perbedaan penting adalah bahwa kita perlu membuat ref
yang didefinisikan di luar komponen tersedia di komponen tempat kita ingin menggunakannya. Kami melakukan ini dengan mengimpor modulnya ke tempat komponen didefinisikan dan mengembalikan ref
dari bagian setup
komponen. Kami akan melewatkan prosedur ini untuk saat ini dan hanya fokus pada API baru sejenak. Reaktivitas di Vue 3 tidak memerlukan komponen; itu sebenarnya sistem mandiri.
Kita dapat menggunakan variabel animation_state
dalam lingkup apa pun yang kita impor variabel ini. Setelah membangun ref
, kami mendapatkan dan menetapkan nilai aktualnya menggunakan ref.value
, misalnya:
animation_state.value = 'paused'; console.log(animation_state.value);
Kita memerlukan akhiran '.value' karena operator penugasan akan menetapkan nilai (non-reaktif) "dijeda" ke variabel animation_state
. Reaktivitas dalam JavaScript (keduanya saat diimplementasikan melalui properti defineProperty
seperti pada Vue 2, dan saat didasarkan pada Proxy
seperti pada Vue 3) memerlukan objek yang kuncinya dapat kita gunakan secara reaktif.
Perhatikan bahwa ini juga terjadi di Vue 2; di sana, kami memiliki komponen sebagai awalan untuk setiap anggota data reaktif ( component.data_member
). Kecuali dan sampai standar bahasa JavaScript memperkenalkan kemampuan untuk membebani operator penugasan, ekspresi reaktif akan memerlukan objek dan kunci (mis., animation_state
dan value
seperti di atas) untuk muncul di sisi kiri dari setiap operasi penugasan di mana kita ingin mempertahankan reaktivitas.
Dalam template, kita dapat menghilangkan .value
karena Vue harus memproses kode template terlebih dahulu dan dapat secara otomatis mendeteksi referensi:
<animation :state='animation_state' />
Secara teori, kompiler Vue dapat melakukan praproses bagian <script>
dari Komponen File Tunggal (SFC) dengan cara yang sama, juga, memasukkan .value
jika diperlukan. Namun, penggunaan refs
kemudian akan berbeda berdasarkan apakah kita menggunakan SFC atau tidak, jadi mungkin fitur seperti itu bahkan tidak diinginkan.
Terkadang, kita memiliki entitas (misalnya, menjadi objek Javascript atau array) yang tidak pernah ingin kita ganti dengan instance yang sama sekali berbeda. Sebagai gantinya, kami mungkin hanya tertarik untuk memodifikasi bidang yang dikunci. Ada singkatan dalam hal ini: menggunakan reactive
alih-alih ref
memungkinkan kita untuk membuang .value
:
const upload_params = reactive({ target_directory: 'media', visibility: 'private', }); upload_params.visibility = 'public'; // no `.value` needed here // if we did not make `upload_params` constant, the following code would compile but we would lose reactivity after the assignment; it is thus a good idea to make reactive variables ```const``` explicitly: upload_params = { target_directory: 'static', visibility: 'public', };
Reaktivitas yang dipisahkan dengan ref
dan reactive
bukanlah fitur yang sepenuhnya baru dari Vue 3. Itu sebagian diperkenalkan di Vue 2.6, di mana contoh data reaktif yang dipisahkan seperti itu disebut "dapat diamati." Untuk sebagian besar, seseorang dapat mengganti Vue.observable
dengan reactive
. Salah satu perbedaannya adalah bahwa mengakses dan mengubah objek yang diteruskan ke Vue.observable
secara langsung bersifat reaktif, sedangkan API baru mengembalikan objek proxy, jadi mengubah objek asli tidak akan memiliki efek reaktif.

Apa yang benar-benar baru di Vue 3 adalah bahwa komponen reaktif lainnya sekarang dapat didefinisikan secara independen juga, selain data reaktif. Properti yang dihitung diimplementasikan dengan cara yang diharapkan:
const x = ref(5); const x_squared = computed(() => x.value * x.value); console.log(x_squared.value); // outputs 25
Demikian pula seseorang dapat menerapkan berbagai jenis jam tangan, metode siklus hidup, dan injeksi ketergantungan. Demi singkatnya, kami tidak akan membahasnya di sini.
Misalkan kita menggunakan pendekatan SFC standar untuk pengembangan Vue. Kita bahkan mungkin menggunakan API tradisional, dengan bagian terpisah untuk data, properti yang dihitung, dll. Bagaimana kita mengintegrasikan reaktivitas kecil Composition API dengan SFC? Vue 3 memperkenalkan bagian lain hanya untuk ini: setup
. Bagian baru dapat dianggap sebagai metode siklus hidup baru (yang dijalankan sebelum kait lain—khususnya, sebelum created
).
Berikut adalah contoh komponen lengkap yang mengintegrasikan pendekatan tradisional dengan Composition API:
<template> <input v-model="x" /> <div>Squared: {{ x_squared }}, negative: {{ x_negative }}</div> </template> <script> import { ref, computed } from 'vue'; export default { name: "Demo", computed: { x_negative() { return -this.x; } }, setup() { const x = ref(0); const x_squared = computed(() => x.value * x.value); return {x, x_squared}; } } </script>
Hal-hal yang perlu diambil dari contoh ini:
- Semua kode API Komposisi sekarang dalam
setup
. Anda mungkin ingin membuat file terpisah untuk setiap fungsi, mengimpor file ini dalam SFC, dan mengembalikan bit reaktivitas yang diinginkan darisetup
(agar tersedia untuk komponen lainnya). - Anda dapat mencampur pendekatan baru dan tradisional dalam file yang sama. Perhatikan bahwa
x
, meskipun itu referensi, tidak memerlukan.value
ketika dirujuk dalam kode template atau di bagian tradisional komponen seperticomputed
. - Last but not least, perhatikan kami memiliki dua node root DOM di template kami; kemampuan untuk memiliki banyak node root adalah fitur baru lainnya dari Vue 3.
Reaktivitas Lebih Ekspresif di Vue 3
Di bagian pertama artikel ini, kami menyentuh motivasi standar untuk Composition API, yang meningkatkan organisasi kode dan penggunaan kembali. Memang, nilai jual utama API baru bukanlah kekuatannya, tetapi kenyamanan organisasi yang dibawanya: kemampuan untuk menyusun kode dengan lebih jelas. Sepertinya hanya itu— bahwa Composition API memungkinkan cara mengimplementasikan komponen yang menghindari batasan dari solusi yang sudah ada, seperti mixin.
Namun, ada lebih banyak API baru. Composition API sebenarnya memungkinkan tidak hanya sistem reaktif yang lebih terorganisir tetapi juga lebih kuat. Bahan utamanya adalah kemampuan untuk menambahkan reaktivitas secara dinamis ke aplikasi. Sebelumnya, seseorang harus mendefinisikan semua data, semua properti yang dihitung, dll. sebelum memuat komponen. Mengapa menambahkan objek reaktif pada tahap selanjutnya berguna? Dalam apa yang tersisa kita lihat contoh yang lebih kompleks: spreadsheet.
Membuat Spreadsheet di Vue 2
Alat spreadsheet seperti Microsoft Excel, LibreOffice Calc, dan Google Sheets semuanya memiliki semacam sistem reaktivitas. Alat-alat ini memberi pengguna sebuah tabel, dengan kolom yang diindeks oleh A–Z, AA–ZZ, AAA–ZZZ, dll., dan baris diindeks secara numerik.
Setiap sel dapat berisi nilai biasa atau rumus. Sel dengan rumus pada dasarnya adalah properti yang dihitung, yang mungkin bergantung pada nilai atau properti yang dihitung lainnya. Dengan spreadsheet standar (dan tidak seperti sistem reaktivitas di Vue), properti yang dihitung ini bahkan diizinkan untuk bergantung pada dirinya sendiri! Referensi diri tersebut berguna dalam beberapa skenario di mana nilai yang diinginkan diperoleh dengan pendekatan berulang.

Setelah konten sel berubah, semua sel yang bergantung pada sel tersebut akan memicu pembaruan. Jika terjadi perubahan lebih lanjut, pembaruan lebih lanjut mungkin dijadwalkan.
Jika kita membangun aplikasi spreadsheet dengan Vue, wajar jika kita bertanya apakah kita dapat menggunakan sistem reaktivitas Vue sendiri dan menjadikan Vue mesin aplikasi spreadsheet. Untuk setiap sel, kita dapat mengingat nilai mentahnya yang dapat diedit, serta nilai komputasi yang sesuai. Nilai yang dihitung akan mencerminkan nilai mentah jika itu adalah nilai biasa, dan sebaliknya, nilai yang dihitung adalah hasil dari ekspresi (rumus) yang ditulis, bukan nilai biasa.
Dengan Vue 2, cara untuk mengimplementasikan spreadsheet adalah dengan memiliki raw_values
array string dua dimensi, dan computed_values
array nilai sel dua dimensi (dihitung).
Jika jumlah sel kecil dan tetap sebelum komponen Vue yang sesuai dimuat, kita dapat memiliki satu nilai mentah dan satu nilai yang dihitung untuk setiap sel tabel dalam definisi komponen kita. Selain dari keindahan estetika yang akan ditimbulkan oleh implementasi seperti itu, tabel dengan jumlah sel yang tetap pada waktu kompilasi mungkin tidak dihitung sebagai spreadsheet.
Ada masalah dengan array dua dimensi computed_values
, juga. Properti yang dihitung selalu merupakan fungsi yang evaluasinya, dalam hal ini, bergantung pada dirinya sendiri (menghitung nilai sel akan, secara umum, memerlukan beberapa nilai lain yang sudah dihitung). Bahkan jika Vue mengizinkan properti komputasi referensi sendiri, memperbarui satu sel akan menyebabkan semua sel dikomputasi ulang (terlepas dari apakah ada dependensi atau tidak). Ini akan sangat tidak efisien. Jadi, kita mungkin akhirnya menggunakan reaktivitas untuk mendeteksi perubahan pada data mentah dengan Vue 2, tetapi segala sesuatu yang lain dari segi reaktivitas harus diimplementasikan dari awal.
Pemodelan Nilai yang Dikomputasi di Vue 3
Dengan Vue 3, kami dapat memperkenalkan properti komputasi baru untuk setiap sel. Jika tabel bertambah, properti baru yang dihitung akan diperkenalkan.
Misalkan kita memiliki sel A1
dan A2
, dan kita ingin A2
menampilkan kuadrat A1
yang nilainya adalah angka 5. Sketsa situasi ini:
let A1 = computed(() => 5); let A2 = computed(() => A1.value * A1.value); console.log(A2.value); // outputs 25
Misalkan kita tinggal dalam skenario sederhana ini sejenak. Ada masalah di sini; bagaimana jika kita ingin mengubah A1
sehingga berisi angka 6? Misalkan kita menulis ini:
A1 = computed(() => 6); console.log(A2.value); // outputs 25 if we already ran the code above
Ini tidak hanya mengubah nilai 5 menjadi 6 di A1
. Variabel A1
memiliki identitas yang sama sekali berbeda sekarang: properti yang dihitung yang menghasilkan angka 6. Namun, variabel A2
masih bereaksi terhadap perubahan identitas lama variabel A1
. Jadi, A2
seharusnya tidak merujuk ke A1
secara langsung, melainkan ke beberapa objek khusus yang akan selalu tersedia dalam konteksnya, dan akan memberi tahu kita apa itu A1
saat ini. Dengan kata lain, kita membutuhkan tingkat tipuan sebelum mengakses A1
, sesuatu seperti pointer. Tidak ada pointer sebagai entitas kelas satu dalam Javascript, tetapi mudah untuk mensimulasikannya. Jika kita ingin memiliki pointer
yang menunjuk ke suatu value
, kita dapat membuat objek pointer = {points_to: value}
. Mengarahkan pointer sama dengan menugaskan ke pointer.points_to
, dan dereferencing (mengakses nilai yang ditunjuk) sama dengan mengambil nilai pointer.points_to
. Dalam kasus kami, kami melanjutkan sebagai berikut:
let A1 = reactive({points_to: computed(() => 5)}); let A2 = reactive({points_to: computed(() => A1.points_to * A1.points_to)}); console.log(A2.points_to); // outputs 25
Sekarang kita bisa mengganti 5 dengan 6.
A1.points_to = computed(() => 6); console.log(A2.points_to); // outputs 36
Di server Discord Vue, pengguna redblobgames menyarankan pendekatan lain yang menarik: alih-alih menggunakan nilai yang dihitung, gunakan referensi yang membungkus fungsi reguler. Dengan cara ini, seseorang juga dapat menukar fungsi tanpa mengubah identitas referensi itu sendiri.
Implementasi spreadsheet kami akan memiliki sel yang dirujuk oleh kunci dari beberapa array dua dimensi. Array ini dapat memberikan tingkat tipuan yang kita butuhkan. Jadi dalam kasus kami, kami tidak memerlukan simulasi pointer tambahan. Kita bahkan dapat memiliki satu larik yang tidak membedakan antara nilai mentah dan yang dihitung. Semuanya bisa menjadi nilai yang dihitung:
const cells = reactive([ computed(() => 5), computed(() => cells[0].value * cells[0].value) ]); cells[0] = computed(() => 6); console.log(cells[1].value); // outputs 36
Namun, kami benar-benar ingin membedakan nilai mentah dan yang dihitung karena kami ingin dapat mengikat nilai mentah ke elemen input HTML. Selanjutnya, jika kita memiliki larik terpisah untuk nilai mentah, kita tidak perlu mengubah definisi properti yang dihitung; mereka akan memperbarui secara otomatis berdasarkan data mentah.
Menerapkan Spreadsheet
Mari kita mulai dengan beberapa definisi dasar, yang sebagian besar sudah cukup jelas.
const rows = ref(30), cols = ref(26); /* if a string codes a number, return the number, else return a string */ const as_number = raw_cell => /^[0-9]+(\.[0-9]+)?$/.test(raw_cell) ? Number.parseFloat(raw_cell) : raw_cell; const make_table = (val = '', _rows = rows.value, _cols = cols.value) => Array(_rows).fill(null).map(() => Array(_cols).fill(val)); const raw_values = reactive(make_table('', rows.value, cols.value)); const computed_values = reactive(make_table(undefined, rows.value, cols.value)); /* a useful metric for debugging: how many times did cell (re)computations occur? */ const calculations = ref(0);
Rencananya adalah setiap computed_values[row][column]
dihitung sebagai berikut. Jika raw_values[row][column]
tidak dimulai dengan =
, kembalikan raw_values[row][column]
. Jika tidak, parsing rumus, kompilasi ke JavaScript, evaluasi kode yang dikompilasi, dan kembalikan nilainya. Untuk mempersingkatnya, kami akan sedikit menipu dengan rumus penguraian dan kami tidak akan melakukan beberapa pengoptimalan yang jelas di sini, seperti cache kompilasi.
Kami akan berasumsi bahwa pengguna dapat memasukkan ekspresi JavaScript yang valid sebagai formula. Kami dapat mengganti referensi ke nama sel yang muncul dalam ekspresi pengguna, seperti A1, B5, dll., dengan referensi ke nilai sel aktual (dihitung). Fungsi berikut melakukan pekerjaan ini, dengan asumsi bahwa string yang menyerupai nama sel benar-benar selalu mengidentifikasi sel (dan bukan merupakan bagian dari beberapa ekspresi JavaScript yang tidak terkait). Untuk mempermudah, kita akan menganggap indeks kolom terdiri dari satu huruf.
const letters = Array(26).fill(0) .map((_, i) => String.fromCharCode("A".charCodeAt(0) + i)); const transpile = str => { let cell_replacer = (match, prepend, col, row) => { col = letters.indexOf(col); row = Number.parseInt(row) - 1; return prepend + ` computed_values[${row}][${col}].value `; }; return str.replace(/(^|[^AZ])([AZ])([0-9]+)/g, cell_replacer); };
Menggunakan fungsi transpile
, kita bisa mendapatkan ekspresi JavaScript murni dari ekspresi yang ditulis dalam "ekstensi" kecil JavaScript kita dengan referensi sel.
Langkah selanjutnya adalah menghasilkan properti yang dihitung untuk setiap sel. Prosedur ini akan terjadi sekali seumur hidup setiap sel. Kami dapat membuat pabrik yang akan mengembalikan properti komputasi yang diinginkan:
const computed_cell_generator = (i, j) => { const computed_cell = computed(() => { // we don't want Vue to think that the value of a computed_cell depends on the value of `calculations` nextTick(() => ++calculations.value); let raw_cell = raw_values[i][j].trim(); if (!raw_cell || raw_cell[0] != '=') return as_number(raw_cell); let user_code = raw_cell.substring(1); let code = transpile(user_code); try { // the constructor of a Function receives the body of a function as a string let fn = new Function(['computed_values'], `return ${code};`); return fn(computed_values); } catch (e) { return "ERROR"; } }); return computed_cell; }; for (let i = 0; i < rows.value; ++i) for (let j = 0; j < cols.value; ++j) computed_values[i][j] = computed_cell_generator(i, j);
Jika kita meletakkan semua kode di atas dalam metode setup
, kita perlu mengembalikan {raw_values, computed_values, rows, cols, letters, calculations}
.
Di bawah ini, kami menyajikan komponen lengkap, bersama dengan antarmuka pengguna dasar.
Kode tersedia di GitHub, dan Anda juga dapat melihat demo langsung.
<template> <div> <div>Calculations: {{ calculations }}</div> <table class="table" border="0"> <tr class="row"> <td></td> <td class="column" v-for="(_, j) in cols" :key="'header' + j" > {{ letters[j] }} </td> </tr> <tr class="row" v-for="(_, i) in rows" :key="i" > <td class="column"> {{ i + 1 }} </td> <td class="column" v-for="(__, j) in cols" :key="i + '-' + j" :class="{ column_selected: active(i, j), column_inactive: !active(i, j), }" @click="activate(i, j)" > <div v-if="active(i, j)"> <input :ref="'input' + i + '-' + j" v-model="raw_values[i][j]" @keydown.enter.prevent="ui_enter()" @keydown.esc="ui_esc()" /> </div> <div v-else v-html="computed_value_formatter(computed_values[i][j].value)"/> </td> </tr> </table> </div> </template> <script> import {ref, reactive, computed, watchEffect, toRefs, nextTick, onUpdated} from "vue"; export default { name: 'App', components: {}, data() { return { ui_editing_i: null, ui_editing_j: null, } }, methods: { get_dom_input(i, j) { return this.$refs['input' + i + '-' + j]; }, activate(i, j) { this.ui_editing_i = i; this.ui_editing_j = j; nextTick(() => this.get_dom_input(i, j).focus()); }, active(i, j) { return this.ui_editing_i === i && this.ui_editing_j === j; }, unselect() { this.ui_editing_i = null; this.ui_editing_j = null; }, computed_value_formatter(str) { if (str === undefined || str === null) return 'none'; return str; }, ui_enter() { if (this.ui_editing_i < this.rows - 1) this.activate(this.ui_editing_i + 1, this.ui_editing_j); else this.unselect(); }, ui_esc() { this.unselect(); }, }, setup() { /*** All the code we wrote above goes here. ***/ return {raw_values, computed_values, rows, cols, letters, calculations}; }, } </script> <style> .table { margin-left: auto; margin-right: auto; margin-top: 1ex; border-collapse: collapse; } .column { box-sizing: border-box; border: 1px lightgray solid; } .column:first-child { background: #f6f6f6; min-width: 3em; } .column:not(:first-child) { min-width: 4em; } .row:first-child { background: #f6f6f6; } #empty_first_cell { background: white; } .column_selected { border: 2px cornflowerblue solid !important; padding: 0px; } .column_selected input, .column_selected input:active, .column_selected input:focus { outline: none; border: none; } </style>
Bagaimana Dengan Penggunaan di Dunia Nyata?
Kami melihat bagaimana sistem reaktivitas yang dipisahkan dari Vue 3 memungkinkan tidak hanya kode yang lebih bersih tetapi juga memungkinkan sistem reaktif yang lebih kompleks berdasarkan mekanisme reaktivitas baru Vue. Kira-kira tujuh tahun telah berlalu sejak Vue diperkenalkan, dan peningkatan ekspresifitas jelas tidak terlalu dicari.
Contoh spreadsheet adalah demonstrasi langsung tentang kemampuan Vue sekarang, dan Anda juga dapat melihat demo langsung.
Tetapi sebagai contoh kata nyata, ini agak khusus. Dalam situasi seperti apa sistem baru dapat berguna? Kasus penggunaan yang paling jelas untuk reaktivitas sesuai permintaan mungkin dalam peningkatan kinerja untuk aplikasi yang kompleks.

Dalam aplikasi front-end yang bekerja dengan data dalam jumlah besar, penggunaan reaktivitas yang tidak dipikirkan dengan matang mungkin berdampak negatif pada kinerja. Misalkan kita memiliki aplikasi dashboard bisnis yang menghasilkan laporan interaktif dari aktivitas bisnis perusahaan. Pengguna dapat memilih rentang waktu dan menambah atau menghapus indikator kinerja dalam laporan. Beberapa indikator mungkin menampilkan nilai yang bergantung pada indikator lain.
Salah satu cara untuk mengimplementasikan pembuatan laporan adalah melalui struktur monolitik. Saat pengguna mengubah parameter input di antarmuka, satu properti yang dihitung, misalnya, report_data
, akan diperbarui. Perhitungan properti yang dihitung ini terjadi sesuai dengan rencana yang dikodekan: pertama, hitung semua indikator kinerja independen, lalu indikator yang hanya bergantung pada indikator independen ini, dll.
Implementasi yang lebih baik akan memisahkan bit laporan dan menghitungnya secara independen. Ada beberapa manfaat untuk ini:
- Pengembang tidak perlu membuat hardcode rencana eksekusi, yang membosankan dan rawan kesalahan. Sistem reaktivitas Vue akan secara otomatis mendeteksi dependensi.
- Bergantung pada jumlah data yang terlibat, kami mungkin mendapatkan peningkatan kinerja yang substansial karena kami hanya memperbarui data laporan yang secara logis bergantung pada parameter input yang dimodifikasi.
Jika semua indikator kinerja yang mungkin menjadi bagian dari laporan akhir diketahui sebelum komponen Vue dimuat, kami mungkin dapat mengimplementasikan decoupling yang diusulkan bahkan dengan Vue 2. Jika tidak, jika backend adalah satu-satunya sumber kebenaran (yaitu biasanya terjadi pada aplikasi berbasis data), atau jika ada penyedia data eksternal, kami dapat membuat properti terkomputasi sesuai permintaan untuk setiap bagian laporan.
Berkat Vue 3, ini sekarang tidak hanya mungkin tetapi juga mudah dilakukan.