Terdahulu kita bercakap tentang sifat yang dikira, yang secara automatik boleh mengira dan cache nilai data responsif. Dan jika kita hanya perlu melakukan beberapa operasi pratetap apabila data responsif berubah, kita boleh menggunakan pendengar watch
. Mari kita laksanakan contoh paling mudah dahulu, dan kemudian kembangkannya sedikit demi sedikit.
const data = {foo: 1} const obj = reactive(data) watch(obj, () => { console.log('obj已改变') })
Dalam contoh ini, kami menggunakan watch
pendengar, dan apabila sifat obj
ditukar, konsol harus mencetak obj已改变
. Berdasarkan pelaksanaan hartanah terkira kami sebelum ini, kami sudah mempunyai idea umum di sini. Fikirkan watch
sebagai fungsi kesan sampingan objek responsif Apabila objek responsif berubah, pelaksanaan fungsi kesan sampingan dicetuskan.
Jika anda ingin mencetuskan fungsi kesan sampingan, anda mesti mengumpulnya dahulu Adakah anda masih ingat bagaimana fungsi kesan sampingan dikumpul? Ya, mengumpul fungsi kesan sampingan get
apabila data reaktif diluluskan. Jadi pertama, kita perlu membuat watch
dikumpul oleh objek reaktif.
function watch(getter, cb) { effect( () => getter.foo ) }
Seterusnya, kami juga perlu membiarkan kaedah lalai kami dilaksanakan. Apabila data reaktif adalah set
, fungsi kesan sampingan dicetuskan. Apa yang ingin kita cetuskan di sini ialah fungsi panggil balik yang dihantar dalam cb
Di sini kita boleh menggunakan penjadual apabila melaksanakan sifat yang dikira Apabila penjadual wujud, set
yang dicetuskan oleh trigger
akan melaksanakan fungsi itu terlebih dahulu pemproses.
function watch(getter, cb) { effect( () => getter.foo, { scheduler() { cb() } } ) }
Pendengar mudah sudah lengkap! Di sini, demi kesederhanaan, kami telah menulis fungsi itu hingga mati dan hanya menyokong mendengar untuk obj.foo
. Seterusnya, kita perlu memikirkan bagaimana untuk mendengar mana-mana sifat objek responsif?
Mengikut idea sebelumnya, jika kita ingin mendengar sebarang atribut objek responsif, kita perlu get
mengakses setiap atribut objek, yang memerlukan kita melakukan rekursi pada objek responsif. merentasi.
function traverse(value, seen = new Set()) { // (1) if(typeof value !== 'object' || value === null || seen.has(value)) return seen.add(value) for(const key in value) { traverse(value[key], seen) } return value }
Untuk mengelakkan gelung tak terhingga yang disebabkan oleh rujukan bulat apabila melintasi objek secara rekursif, kami mencipta (1)
pada Set
dan kembali terus apabila objek yang sama muncul berulang kali.
Dalam Vue3, kita tidak boleh mendengar secara langsung nilai sifat objek reaktif. Jika anda perlu mendengar nilai sifat objek reaktif, anda memerlukan fungsi getter
supaya pendengar boleh dikumpulkan oleh objek reaktif.
const data = { foo: 1 } const obj = reactive(data) watch( () => obj.foo, () => { console.log('obj.foo已改变') })
Menentukan atribut bermakna pendengar semasa hanya akan dicetuskan oleh atribut yang ditentukan dan tidak perlu melintasi keseluruhan objek responsif secara rekursif.
function watch(getter, cb) { if(typeof getter !== 'function') getter = traverse(getter) // (2) effect( () => getter(), { scheduler() { cb() } } ) }
Pada (2), kami menambah penghakiman Jika yang diluluskan sudah menjadi fungsi getter
, kami menggunakannya secara langsung Jika ia bukan getter
fungsi, Jika ia dianggap sebagai objek responsif, ia perlu dilalui secara rekursif.
Dalam Vue kita juga perlu mendapat nilai baharu dan lama sebelum dan selepas kemas kini data responsif dalam fungsi panggil balik cb()
.
const data = { foo: 1 } const obj = reactive(data) watch( () => obj.foo, (newValue, oldValue) => { console.log(newValue, oldValue) })
Soalan seterusnya ialah, bagaimana untuk mendapatkan newValue
dan oldValue
. newValue
mudah diselesaikan Selepas melaksanakan fungsi panggil balik cb()
, anda akan mendapat newValue
, tetapi bagaimana untuk mendapatkan nilai oldValue
di sini? Untuk mendapatkan nilai lama daripada watch
, fungsi kesan sampingan tidak boleh dilaksanakan serta-merta. Apa yang terlintas di fikiran di sini? Ya, apabila melaksanakan sifat yang dikira, kami telah menggunakan lazy
, yang boleh melarang pelaksanaan automatik fungsi kesan sampingan.
function watch(getter, cb) { if(typeof getter !== 'function') getter = traverse(getter) let oldValue const effectFn = effect( () => getter(), { lazy: true, // (3) scheduler() { cb(oldValue) } } ) oldValue = effectFn() // (4) }
Pada (3) kami menetapkan suis lazy
Selepas menetapkan lazy
, hak pelaksanaan fungsi kesan sampingan diserahkan kepada diri kami sendiri. Pada (4), kami melaksanakan fungsi kesan sampingan secara manual. Kita perlu melihat kembali di sini. getter
yang kita lalui tadi ialah fungsi () => obj.foo
, dan parameter pertama fungsi effect
ialah fungsi kesan sampingan yang sebenarnya dilaksanakan, jadi apa yang kita laksanakan secara manual sebenarnya adalah Fungsi. () => obj.foo
, jadi kita dapat nilai lama.
Bagaimana untuk mendapatkan nilai baharu? Selepas nilai data responsif dikemas kini, fungsi kesan sampingan effect
akan dicetuskan untuk dilaksanakan Apabila atribut penjadual wujud, penjadual akan dilaksanakan. Dalam penjadual, kita boleh melaksanakan fungsi kesan sampingan sekali lagi dan mendapatkan nilai tukar baharu melalui () => obj.foo
.
function watch(getter, cb) { if(typeof getter !== 'function') getter = traverse(getter) let oldValue, newValue const effectFn = effect( () => getter(), { lazy: true, scheduler() { newValue = effectFn() cb(newValue, oldValue) oldValue = newValue // (5) } } ) oldValue = effectFn() }
Pada (5), selepas melaksanakan fungsi panggil balik cb()
, kami melakukan beberapa kerja selepas itu dan mengemas kini nilai oldValue
untuk menyediakan panggilan balik seterusnya.
Kadangkala, kami juga mahu pendengar melaksanakan fungsi panggil balik serta-merta apabila ia dicipta.
const data = { foo: 1 } const obj = reactive(data) watch( () => obj.foo, (newValue, oldValue) => { console.log('newValue:', newValue,', oldValue:', oldValue) }, { immediate: true } )
Apabila nilai immediate
ialah true
, ia perlu dilaksanakan segera. Selepas menjelaskan keperluan, mari kita perbaiki watch
pendengar.
function watch(getter, cb, options = {}) { if(typeof getter !== 'function') getter = traverse(getter) let oldValue, newValue function job() { // (6) newValue = effectFn() cb(newValue, oldValue) oldValue = newValue } const effectFn = effect( () => getter(), { lazy: true, scheduler: job, } ) if(options.immediate) { // (7) job() } else { oldValue = effectFn() } }
在(6)处,我们抽离了回调函数的执行逻辑,当options.immediate
存在时,直接触发执行。
Atas ialah kandungan terperinci Apakah prinsip pelaksanaan pendengar Vue3. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!