Jadual Kandungan
Responsif
总结
Rumah hujung hadapan web View.js Bagaimana untuk menulis tangan sistem responsif Vue3

Bagaimana untuk menulis tangan sistem responsif Vue3

May 14, 2023 am 09:40 AM
vue3

Responsif

Pertama sekali, apakah responsif?

Responsif adalah untuk melaksanakan satu siri pemprosesan pautan apabila data yang diperhatikan berubah. Sama seperti acara sosial yang hangat, apabila ada kemas kini, semua media akan membuat susulan dan membuat laporan yang berkaitan. Di sini acara hangat sosial menjadi sasaran yang perlu diperhatikan. Jadi dalam rangka kerja hadapan, apakah sasaran yang diperhatikan? Jelas sekali, ia adalah negeri. Secara amnya terdapat beberapa keadaan, yang disusun mengikut objek. Oleh itu, kita boleh melihat perubahan setiap kunci objek keadaan dan melakukan satu siri proses bersama.

Kami perlu mengekalkan struktur data sedemikian:

Bagaimana untuk menulis tangan sistem responsif Vue3

Setiap kunci objek keadaan mempunyai siri kesan sampingan yang berkaitan functions , iaitu logik pelaksanaan pautan apabila perubahan diatur melalui Set.

Setiap kekunci dikaitkan dengan satu siri fungsi kesan dan berbilang kunci boleh dikekalkan dalam Peta.

Peta ini wujud apabila objek wujud, dan ia akan dimusnahkan apabila objek dimusnahkan. (Oleh kerana objek hilang, tidak perlu mengekalkan kesan yang dikaitkan dengan setiap kekunci)

Dan WeakMap hanya mempunyai ciri sedemikian, kekunci WeakMap mestilah objek, dan nilainya boleh berupa sebarang data . Kunci Apabila objek dimusnahkan, nilainya juga akan dimusnahkan.

Jadi, Peta responsif akan disimpan menggunakan WeakMap, dan kuncinya ialah objek asal.

Struktur data ini ialah struktur data teras tindak balas.

Contohnya, objek status seperti ini:

const obj = {
    a: 1,
    b: 2
}
Salin selepas log masuk

Struktur data responsifnya adalah seperti ini:

rreee

Struktur data yang dibuat adalah seperti dalam gambar:

Bagaimana untuk menulis tangan sistem responsif Vue3

Bagaimana untuk menulis tangan sistem responsif Vue3

Kemudian tambahkan kebergantungan deps, sebagai contoh, fungsi bergantung pada a , maka ia perlu ditambah ke koleksi deps a:

const depsMap = new Map();
const aDeps = new Set();
depsMap.set('a', aDeps);
const bDeps = new Set();
depsMap.set('b', bDeps);
const reactiveMap = new WeakMap()
reactiveMap.set(obj, depsMap);
Salin selepas log masuk

Iaitu, ia seperti ini:

effect(() => {
    console.log(obj.a);
});
Salin selepas log masuk

Tiada masalah dalam mengekalkan fungsi deps dengan cara ini, tetapi adakah kita mahu pengguna untuk Adakah anda perlu menambah deps secara manual? Ini bukan sahaja akan menyerang kod perniagaan, tetapi ia juga mudah terlepas.

Jadi pengguna pastinya tidak akan diminta untuk menyelenggara deps secara manual, tetapi perlu mengumpul tanggungan secara automatik. Jadi bagaimana untuk mengumpul kebergantungan secara automatik? Apabila membaca nilai status, hubungan pergantungan dengan status diwujudkan, jadi mudah untuk memikirkan kaedah get yang boleh digunakan untuk proksi status. Anda boleh menggunakan Object.defineProperty atau Proxy:

const depsMap = reactiveMap.get(obj);
const aDeps = depsMap.get('a');
aDeps.add(该函数);
Salin selepas log masuk

effect akan melaksanakan fungsi panggil balik yang diluluskan fn Apabila anda membaca obj.a dalam fn, get akan dicetuskan dan anda akan mendapat respons objek , ambil set deps yang sepadan dengan a daripadanya, dan tambahkan fungsi kesan semasa padanya.

Ini melengkapkan koleksi pergantungan.

Apabila anda mengubah suai obj.a, anda perlu memaklumkan semua deps, jadi anda juga perlu menetapkan set proksi:

const data = {
    a: 1,
    b: 2
}
let activeEffect
function effect(fn) {
  activeEffect = fn
  fn()
}
const reactiveMap = new WeakMap()
const obj = new Proxy(data, {
    get(targetObj, key) {
        let depsMap = reactiveMap.get(targetObj);
        
        if (!depsMap) {
          reactiveMap.set(targetObj, (depsMap = new Map()))
        }
        
        let deps = depsMap.get(key)
        
        if (!deps) {
          depsMap.set(key, (deps = new Set()))
        }
        deps.add(activeEffect)

        return targetObj[key]
   }
})
Salin selepas log masuk

Responsif asas selesai. Mari kita uji:

Bagaimana untuk menulis tangan sistem responsif Vue3

dicetak dua kali, kali pertama ialah 1 dan kali kedua ialah 3. Kesannya mula-mula akan melaksanakan fungsi panggil balik masuk dan pencetus untuk mengumpul dependensi Pada masa ini, obj.a yang dicetak ialah 1. Kemudian apabila obj.a diberikan nilai 3, set akan dicetuskan untuk melaksanakan tanggungan yang dikumpul. . Pada masa ini, obj. a ialah 3

Kebergantungan juga dikumpul dengan betul:

Bagaimana untuk menulis tangan sistem responsif Vue3

Hasilnya betul, kami telah menyelesaikan tindak balas asas! Sudah tentu, responsif bukan sahaja mempunyai kod kecil ini Pelaksanaan semasa kami tidak sempurna dan masih terdapat beberapa masalah. Contohnya, Jika terdapat suis cawangan dalam kod, pelaksanaan terakhir akan bergantung pada obj.b, tetapi pelaksanaan seterusnya tidak akan bergantung padanya Adakah terdapat pergantungan yang tidak sah pada masa ini?

Sekeping kod:

set(targetObj, key, newVal) {
    targetObj[key] = newVal
    const depsMap = reactiveMap.get(targetObj)
    if (!depsMap) return
    const effects = depsMap.get(key)
    effects && effects.forEach(fn => fn())
}
Salin selepas log masuk

Kali pertama fungsi kesan dilaksanakan, obj.a ialah 1. Pada masa ini, ia akan pergi ke cawangan pertama, dan kemudian Bergantung pada obj.b. Ubah suai obj.a kepada undefined, set pencetus dan laksanakan semua fungsi bergantung Pada masa ini, kita pergi ke cawangan dua dan tidak lagi bergantung pada obj.b.

Tukar obj.b kepada 3. Semestinya tiada fungsi yang bergantung kepada b pada masa ini. Mari cuba:

Bagaimana untuk menulis tangan sistem responsif Vue3

Adalah betul untuk mencetak 2 untuk kali pertama, iaitu, ia telah mencapai cawangan pertama, dan mencetak obj.b

Ia juga betul untuk mencetak apa-apa untuk kali kedua, dan ia telah sampai ke cawangan kedua pada masa ini. Tetapi mencetak apa-apa untuk kali ketiga adalah salah, kerana pada masa ini obj.b tidak lagi mempunyai fungsi bergantung, tetapi ia masih dicetak.

Cetak dan lihat deps, anda akan dapati deps obj.b tidak dibersihkan

Bagaimana untuk menulis tangan sistem responsif Vue3

所以解决方案就是每次添加依赖前清空下上次的 deps。怎么清空某个函数关联的所有 deps 呢?记录下就好了。

我们改造下现有的 effect 函数:

let activeEffect
function effect(fn) {
  activeEffect = fn
  fn()
}
Salin selepas log masuk

记录下这个 effect 函数被放到了哪些 deps 集合里。也就是:

let activeEffect
function effect(fn) {
  const effectFn = () => {
      activeEffect = effectFn
      fn()
  }
  effectFn.deps = []
  effectFn()
}
Salin selepas log masuk

对之前的 fn 包一层,在函数上添加个 deps 数组来记录被添加到哪些依赖集合里。

get 收集依赖的时候,也记录一份到这里:

Bagaimana untuk menulis tangan sistem responsif Vue3

这样下次再执行这个 effect 函数的时候,就可以把这个 effect 函数从上次添加到的依赖集合里删掉:

Bagaimana untuk menulis tangan sistem responsif Vue3

cleanup 实现如下:

function cleanup(effectFn) {
    for (let i = 0; i < effectFn.deps.length; i++) {
        const deps = effectFn.deps[i]
        deps.delete(effectFn)
    }
    effectFn.deps.length = 0
}
Salin selepas log masuk

effectFn.deps 数组记录了被添加到的 deps 集合,从中删掉自己。全删完之后就把上次记录的 deps 数组置空。

我们再来测试下:

Bagaimana untuk menulis tangan sistem responsif Vue3

无限循环打印了,什么鬼?

问题出现在这里:

Bagaimana untuk menulis tangan sistem responsif Vue3

set 的时候会执行所有的当前 key 的 deps 集合里的 effect 函数。

而我们执行 effect 函数之前会把它从之前的 deps 集合中清掉:

Bagaimana untuk menulis tangan sistem responsif Vue3

执行的时候又被添加到了 deps 集合。这样 delete 又 add,delete 又 add,所以就无限循环了。

解决的方式就是创建第二个 Set,只用于遍历:

Bagaimana untuk menulis tangan sistem responsif Vue3

这样就不会无限循环了。

再测试一次:

Bagaimana untuk menulis tangan sistem responsif Vue3

现在当 obj.a 赋值为 undefined 之后,再次执行 effect 函数,obj.b 的 deps 集合就被清空了,所以需改 obj.b 也不会打印啥。

看下现在的响应式数据结构:

Bagaimana untuk menulis tangan sistem responsif Vue3

确实,b 的 deps 集合被清空了。那现在的响应式实现是完善的了么?也不是,还有一个问题:

如果 effect 嵌套了,那依赖还能正确的收集么?

首先讲下为什么要支持 effect 嵌套,因为组件是可以嵌套的,而且组件里会写 effect,那也就是 effect 嵌套了,所以必须支持嵌套。

我们嵌套下试试:

effect(() => {
    console.log(&#39;effect1&#39;);
    effect(() => {
        console.log(&#39;effect2&#39;);
        obj.b;
    });
    obj.a;
});
obj.a = 3;
Salin selepas log masuk

按理说会打印一次 effect1、一次 effect2,这是最开始的那次执行。然后 obj.a 修改为 3 后,会触发一次 effect1 的打印,执行内层 effect,又触发一次 effect2 的打印。也就是会打印 effect1、effect2、effect1、effect2。

我们测试下:

Bagaimana untuk menulis tangan sistem responsif Vue3

打印了 effect1、effet2 这是对的,但第三次打印的是 effect2,这说明 obj.a 修改后并没有执行外层函数,而是执行的内层函数。为什么呢?

看下这段代码:

Bagaimana untuk menulis tangan sistem responsif Vue3

我们执行 effect 的时候,会把它赋值给一个全局变量 activeEffect,然后后面收集依赖就用的这个。

当嵌套 effect 的时候,内层函数执行后会修改 activeEffect 这样收集到的依赖就不对了。

怎么办呢?嵌套的话加一个栈来记录 effect 不就行了?

也就是这样:

Bagaimana untuk menulis tangan sistem responsif Vue3

执行 effect 函数前把当前 effectFn 入栈,执行完以后出栈,修改 activeEffect 为栈顶的 effectFn。

这样就保证了收集到的依赖是正确的。

这种思想的应用还是很多的,需要保存和恢复上下文的时候,都是这样加一个栈。

我们再测试一下:

Bagaimana untuk menulis tangan sistem responsif Vue3

现在的打印就对了。至此,我们的响应式系统就算比较完善了。

全部代码如下:

const data = {
    a: 1,
    b: 2
}
let activeEffect
const effectStack = [];
function effect(fn) {
  const effectFn = () => {
      cleanup(effectFn)
      activeEffect = effectFn
      effectStack.push(effectFn);
      fn()
      effectStack.pop();
      activeEffect = effectStack[effectStack.length - 1];
  }
  effectFn.deps = []
  effectFn()
}
function cleanup(effectFn) {
    for (let i = 0; i < effectFn.deps.length; i++) {
        const deps = effectFn.deps[i]
        deps.delete(effectFn)
    }
    effectFn.deps.length = 0
}
const reactiveMap = new WeakMap()
const obj = new Proxy(data, {
    get(targetObj, key) {
        let depsMap = reactiveMap.get(targetObj)
        
        if (!depsMap) {
          reactiveMap.set(targetObj, (depsMap = new Map()))
        }
        let deps = depsMap.get(key)
        if (!deps) {
          depsMap.set(key, (deps = new Set()))
        }
        deps.add(activeEffect)
        activeEffect.deps.push(deps);
        return targetObj[key]
   },
   set(targetObj, key, newVal) {
        targetObj[key] = newVal
        const depsMap = reactiveMap.get(targetObj)
        if (!depsMap) return
        const effects = depsMap.get(key)
        // effects && effects.forEach(fn => fn())
        const effectsToRun = new Set(effects);
        effectsToRun.forEach(effectFn => effectFn());
    }
})
Salin selepas log masuk

总结

响应式就是数据变化的时候做一系列联动的处理。

核心是这样一个数据结构:

Bagaimana untuk menulis tangan sistem responsif Vue3

最外层是 WeakMap,key 为对象,value 为响应式的 Map。这样当对象销毁时,Map 也会销毁。Map 里保存了每个 key 的依赖集合,用 Set 组织。

我们通过 Proxy 来完成自动的依赖收集,也就是添加 effect 到对应 key 的 deps 的集合里。 set 的时候触发所有的 effect 函数执行。

这就是基本的响应式系统。

但是还不够完善,每次执行 effect 前要从上次添加到的 deps 集合中删掉它,然后重新收集依赖。这样可以避免因为分支切换产生的无效依赖。并且执行 deps 中的 effect 前要创建一个新的 Set 来执行,避免 add、delete 循环起来。此外,为了支持嵌套 effect,需要在执行 effect 之前把它推到栈里,然后执行完出栈。解决了这几个问题之后,就是一个完善的 Vue 响应式系统了。当然,现在虽然功能是完善的,但是没有实现 computed、watch 等功能,之后再实现。

最后,再来看一下这个数据结构,理解了它就理解了 vue 响应式的核心:

Bagaimana untuk menulis tangan sistem responsif Vue3

Atas ialah kandungan terperinci Bagaimana untuk menulis tangan sistem responsif Vue3. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

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

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Cara Memperbaiki Audio Jika anda tidak dapat mendengar sesiapa
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Arahan sembang dan cara menggunakannya
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

vue3+vite: Bagaimana untuk menyelesaikan ralat apabila menggunakan memerlukan untuk mengimport imej secara dinamik dalam src vue3+vite: Bagaimana untuk menyelesaikan ralat apabila menggunakan memerlukan untuk mengimport imej secara dinamik dalam src May 21, 2023 pm 03:16 PM

Penggunaan vue3+vite:src memerlukan pengimportan imej secara dinamik dan laporan ralat dan penyelesaian vue3+vite secara dinamik Jika vue3 dibangunkan menggunakan skrip taip, akan terdapat mesej ralat untuk keperluan untuk memperkenalkan imej tidak boleh digunakan :require(' .../assets/test.png') diimport kerana typescript tidak menyokong require, jadi import digunakan Berikut ialah cara menyelesaikannya: gunakan awaitimport

Cara menggunakan tinymce dalam projek vue3 Cara menggunakan tinymce dalam projek vue3 May 19, 2023 pm 08:40 PM

tinymce ialah pemalam editor teks kaya yang berfungsi sepenuhnya, tetapi memperkenalkan tinymce ke dalam vue tidak selancar seperti pemalam teks kaya Vue yang lain tidak sesuai untuk Vue dan @tinymce/tinymce-vue perlu diperkenalkan. dan Ia adalah pemalam teks kaya asing dan belum melepasi versi Cina Anda perlu memuat turun pakej terjemahan dari tapak web rasminya (anda mungkin perlu memintas tembok api). 1. Pasang kebergantungan yang berkaitan npminstalltinymce-Snpminstall@tinymce/tinymce-vue-S2 Muat turun pakej Cina 3. Perkenalkan pakej kulit dan Cina Buat folder tinymce baharu dalam folder awam projek dan muat turun

Cara memuat semula sebahagian kandungan halaman dalam Vue3 Cara memuat semula sebahagian kandungan halaman dalam Vue3 May 26, 2023 pm 05:31 PM

Untuk mencapai muat semula separa halaman, kami hanya perlu melaksanakan pemaparan semula komponen setempat (dom). Dalam Vue, cara paling mudah untuk mencapai kesan ini ialah menggunakan arahan v-if. Dalam Vue2, selain menggunakan arahan v-if untuk memaparkan semula dom setempat, kami juga boleh mencipta komponen kosong baharu Apabila kami perlu memuat semula halaman setempat, lompat ke halaman komponen kosong ini dan kemudian masuk semula pengawal beforeRouteEnter dalam komponen kosong. Seperti yang ditunjukkan dalam rajah di bawah, cara mengklik butang muat semula dalam Vue3.X untuk memuatkan semula DOM dalam kotak merah dan memaparkan status pemuatan yang sepadan. Memandangkan pengawal dalam komponen dalam sintaks persediaan skrip dalam Vue3.X hanya mempunyai o

Cara Vue3 menghuraikan penurunan harga dan melaksanakan penyerlahan kod Cara Vue3 menghuraikan penurunan harga dan melaksanakan penyerlahan kod May 20, 2023 pm 04:16 PM

Vue melaksanakan bahagian hadapan blog dan perlu melaksanakan penghuraian markdown Jika terdapat kod, ia perlu melaksanakan penyerlahan kod. Terdapat banyak pustaka parsing markdown untuk Vue, seperti markdown-it, vue-markdown-loader, marked, vue-markdown, dsb. Perpustakaan ini semuanya sangat serupa. Ditanda digunakan di sini, dan highlight.js digunakan sebagai pustaka penonjolan kod. Langkah-langkah pelaksanaan khusus adalah seperti berikut: 1. Pasang perpustakaan bergantung Buka tetingkap arahan di bawah projek vue dan masukkan arahan berikut npminstallmarked-save//marked untuk menukar markdown ke htmlnpmins.

Bagaimana untuk menyelesaikan masalah bahawa selepas projek vue3 dibungkus dan diterbitkan ke pelayan, halaman akses dipaparkan kosong Bagaimana untuk menyelesaikan masalah bahawa selepas projek vue3 dibungkus dan diterbitkan ke pelayan, halaman akses dipaparkan kosong May 17, 2023 am 08:19 AM

Selepas projek vue3 dibungkus dan diterbitkan ke pelayan, halaman akses memaparkan kosong 1. PublicPath dalam fail vue.config.js diproses seperti berikut: const{defineConfig}=require('@vue/cli-service') module.exports=defineConfig({publicPath :process.env.NODE_ENV==='pengeluaran'?'./':'/&

Bagaimana untuk memilih avatar dan memangkasnya dalam Vue3 Bagaimana untuk memilih avatar dan memangkasnya dalam Vue3 May 29, 2023 am 10:22 AM

Kesan terakhir ialah memasang komponen VueCropper yarnaddvue-cropper@next Nilai pemasangan di atas adalah untuk Vue3 Jika ia adalah Vue2 atau anda ingin menggunakan kaedah lain untuk merujuk, sila lawati alamat npm rasminya. Ia juga sangat mudah untuk merujuk dan menggunakannya dalam komponen Anda hanya perlu memperkenalkan komponen yang sepadan dan fail gayanya. Saya tidak merujuknya secara global di sini, tetapi hanya memperkenalkan import{userInfoByRequest}from'../js/api. ' dalam fail komponen saya import{VueCropper}dari'vue-cropper&

Cara menggunakan komponen boleh guna semula Vue3 Cara menggunakan komponen boleh guna semula Vue3 May 20, 2023 pm 07:25 PM

Prakata Sama ada ia adalah vue atau react, apabila kita menghadapi berbilang kod berulang, kita akan memikirkan cara untuk menggunakan semula kod ini, dan bukannya mengisi fail dengan sekumpulan kod berlebihan. Malah, kedua-dua vue dan react boleh mencapai penggunaan semula dengan mengekstrak komponen, tetapi jika anda menemui beberapa serpihan kod kecil dan anda tidak mahu mengekstrak fail lain, sebagai perbandingan, react boleh digunakan dalam yang sama Isytiharkan widget yang sepadan dalam fail , atau laksanakannya melalui fungsi render, seperti: constDemo:FC=({msg})=>{returndemomsgis{msg}}constApp:FC=()=>{return(

Cara menggunakan vue3+ts+axios+pinia untuk mencapai penyegaran yang tidak masuk akal Cara menggunakan vue3+ts+axios+pinia untuk mencapai penyegaran yang tidak masuk akal May 25, 2023 pm 03:37 PM

vue3+ts+axios+pinia menyedari penyegaran yang tidak masuk akal 1. Mula-mula muat turun aiXos dan pinianpmipinia dalam projek--savenpminstallaxios--save2. AxiosResponse}daripada"axios";importaxiosfrom'axios';import{ElMess

See all articles