Hallo DEV.to-Community!
Dieser Artikel behandelt mehrere Aspekte von Vue 3, die am häufigsten verwendet werden oder eher auf der dunklen Seite liegen und nicht so beachtet werden, wie sie sollten.
Da ich Vue 3 beschreibe, werde ich die Kompositions-API und nicht die altmodische Options-API verwenden. Das Konzept beider Methoden ist dasselbe, sodass Sie sich ziemlich schnell an die Kompositions-API anpassen können. Ich bin nicht in der Lage, irgendetwas vorzuschreiben, und jeder Programmierer kann frei wählen, wie er sein Programm schreiben möchte, aber meiner persönlichen Meinung nach bevorzuge ich die Composition API wegen ihrer prägnanten Syntax und besseren Codeverwaltung. Wenn Sie also immer noch Angst haben, auf die Kompositions-API umzusteigen, empfehle ich Ihnen, es auszuprobieren, denn es wird sich lohnen.
Reaktion ist nicht das Einzige, was einem in den Sinn kommen sollte, wenn wir über Reaktivität sprechen. Unter Reaktivität versteht man die Fähigkeit einer Entität (bei einer gegebenen Webseite), basierend auf Datenänderungen zu reagieren. Möglicherweise kennen Sie dieses Konzept als MVVM. MVVM ist die Kurzform von Model-View-View-Model. Wie der Name schon sagt, ändert sich die Ansicht, wenn sich Daten ändern, und umgekehrt.
Um die Reaktionsfähigkeit von Vue zu nutzen, werden wir einige Optionen behandeln.
Sie können sich einen Verweis als eine besondere Art von Variable vorstellen, die Sie in Ihrer Vue-Anwendung verwenden können. Diese Beschreibung trifft nur zu, wenn Sie zum ersten Mal mit Vue arbeiten, da es danach etwas komplexer wird.
Das Definieren einer Referenz ist so einfach:
const message = ref("Hello World")
Verwenden Sie es in Ihrer Vorlage mit der Interpolationssyntax:
<span>{{ message }}</span>
Wenn Sie sich fragen, warum ich es eine Variable genannt habe, die Nachricht aber mit einem const-Schlüsselwort deklariert habe, haben Sie jedes Recht dazu.
Wie Sie wissen, können Sie den Wert von konstant nicht ändern, da dies der Zweck des Schlüsselworts const ist. Aber es gibt eine subtile Kleinigkeit, über die Sie Bescheid wissen sollten. Obwohl das Schlüsselwort const nicht zulässt, dass die Daten einer Variablen geändert werden, kümmert es sich nicht um die verschachtelten Daten! Dies ist auch bei ref der Fall. Um diese Situation besser zu verstehen, versuchen Sie den folgenden Code:
const o = { a: 1, b: 2, c: 3 } console.log(o.a) // 1 o.a = 4 console.log(o.a) // 4
Wie Sie sehen, kann ich den Wert von o.a ändern, da Objekte nur Referenzen und kein ganzer Wert für sich sind. Wenn ich also den Wert von a innerhalb des Objekts o ändere, wird die Konstantenbeschränkung nicht angewendet. Wenn Sie o sich selbst einen Wert zuweisen möchten, wird natürlich ein Fehler ausgegeben und Sie können dies nicht tun. Beispielsweise ist der folgende Code falsch:
const o = { a: 1, b: 2, c: 3 } o = "hello"
Dies ist der gleiche Fall, wenn ref verwendet wird (und andere Dinge, die Sie später hier sehen werden). Wenn Sie eine Ref-Funktion aufrufen, wandelt sie alles, was sie empfängt, in ein Objekt um. Dies wird als Umwickeln bezeichnet. Probieren Sie diesen Code aus:
const message = ref("Hello World") console.log(message)
Sie sollten etwa das Bild unten sehen:
Wie Sie beim Protokollieren der Nachrichtenvariablen sehen können, erhalten Sie Hello World nicht direkt, sondern innerhalb eines Objekts und Sie können über den Wertschlüssel des oben genannten Objekts auf Ihren tatsächlichen Wert zugreifen. Dadurch kann Vue auf Änderungen achten und die MVVM-Sache ausführen! :)
Wenn Sie in einer Vue-Vorlage auf Ihre Referenz zugreifen, ist kein Zugriff wie auf message.value erforderlich. Vue ist intelligent genug, um den tatsächlichen Wert innerhalb der Vorlage und nicht im Objekt darzustellen. Wenn Sie jedoch auf den Wert einer Referenz in Ihrem Skript zugreifen oder diesen ändern möchten, sollten Sie dies mit .value:
tun
message.value = "Adnan!" console.log(message.value) // Adnan!
Wie Sie bei der Verwendung einer Referenz gesehen haben, verpackt Vue Ihre Daten in ein Objekt und ermöglicht Ihnen den Zugriff darauf über .value. Dies ist normalerweise der am häufigsten verwendete Fall. Sie können fast alles mit einem Ref umschließen und reaktiv machen.
Falls Sie sich fragen, wie Vue Wertänderungen überwacht und die Ansicht immer wieder rendert, sollten Sie sich JavaScript-Proxys ansehen.
Wenn Ihr Wert selbst ein Objekt ist, können Sie reaktiv anstelle von ref verwenden. Die reaktive Funktion umschließt Ihren Wert nicht und macht stattdessen das Objekt selbst reaktiv und beobachtbar.
const o = reactive({count: 0})
Wenn Sie versuchen, die o-Konstante auszudrucken, werden Sie feststellen, dass es sich tatsächlich um Ihr Objekt ohne größere Änderungen handelt:
Now you may manipulate the key count as you would normally do in JavaScript and Vue will render the changes as soon as possible.
Here is an example:
const increase = () => { o.count++ }
If you had ref instead of reactive it would have looked like this:
const o = ref({count: 0}) const increase = () => { o.value.count++ }
If you are still unsure which one to use, keep in mind that ref is a safe option to use.
Give that you have a ref like below:
const state = ref({ names: { adnan: 'babakan', arian: 'amini', ata: 'parvin', mahdi: 'salehi' } })
And printed my last name in your template as below:
<span>{{ state.names.adnan }}</span>
If you every changed my last name like this:
state.value.names.adnan = 'masruri'
Your template will be updated to show masruri instead of babakan. This is due to the fact that ref makes a deeply watched object and the changes to the view (template) are triggered for nested data as well.
There is an option to prevent such behaviour if that's what you want. To do so you may use shallowRef. A shallowRef acts exactly like ref does, with an exception of not watching for deep changes.
const state = shallowRef({ names: { adnan: 'babakan', arian: 'amini', ata: 'parvin', mahdi: 'salehi' } }) onMounted(() => { state.value.names.adnan = 'masruri' })
The code above will result in your template showing babakan as it is not watched. But changing the .value entirely will trigger changes. So the code below will result in your template getting updated as well:
const state = shallowRef({ names: { adnan: 'babakan', arian: 'amini', ata: 'parvin', mahdi: 'salehi' } }) onMounted(() => { state.value = { names: { adnan: 'masruri', arian: 'amini', ata: 'parvin', mahdi: 'salehi' } } })
This is a great option for performance-related concerns.
So far we've known that ref wraps the data and watches it deeply and shallowRef wraps the data and watches it shallowly. Now tell me this, if reactive makes an object reactive, what does shallowReactive do?
const state = shallowReactive({ names: { adnan: 'babakan', arian: 'amini', ata: 'parvin', mahdi: 'salehi', }, }) onMounted(() => { state.names.adnan = 'masruri' })
As you might have guessed the template won't be updated.
Given that you are using a shallowRef and changed a value and now want your template to be updated according to the new data as well, you may use the triggerRef function:
const state = shallowRef({ names: { adnan: 'babakan', arian: 'amini', ata: 'parvin', mahdi: 'salehi' } }) onMounted(() => { state.value.names.adnan = 'masruri' triggerRef(state) })
Now the template will also show masruri. This is more like changing from an automatic gear to a manual gear if you will.
This is usable for both shallowRef and shallowReactive.
The readonly function receives a ref or a reactive as an argument and returns an exact copy that is only possible to be read from. This is used when you want to make sure your data is safe and is not possible to change when watching for it.
Example:
<template> <div> {{ infoReadOnly }} </div> </template> <script setup> const info = ref({ first: 'Adnan', last: 'Babakan' }) const infoReadOnly = readonly(info) onMounted(() => { info.value.first = 'Arian' }) </script>
Though I've changed the value of info, since infoReadOnly is actually a live copy of info my changes are reflected in infoReadOnly as well. Yet you may not change the values using infoReadOnly directly:
const info = ref({ first: 'Adnan', last: 'Babakan' }) const infoReadOnly = readonly(info) onMounted(() => { infoReadOnly.value.first = 'Arian' })
This won't change the data and will warn you in the console as below:
If we have ref and shallowRef, reactive and shallowReactive, why not have a shallowReadonly?
A shallowReadonly only makes the root level elements readonly whilst you can change the nested data:
const stateShallowReadonly = shallowReadonly({ name: 'Adnan', friends: [ { name: 'Arian' }, { name: 'Ata' }, { name: 'Mahdi' } ] }) onMounted(() => { stateShallowReadonly.name = 'Brad' })
The code above will warn you and won't change the value of name since it is a direct property.
But you can freely change anything inside friends since it is nested:
const stateShallowReadonly = shallowReadonly({ name: 'Adnan', friends: [ { name: 'Arian' }, { name: 'Ata' }, { name: 'Mahdi' } ] }) onMounted(() => { stateShallowReadonly.friends[0].name = 'Brad' })
Man, I love computed in Vue! You can imagine it as a glass in which you can mix your potions and still have your potions standing there intact!
A computed is like a ref or reactive that can be accessed and watched but not changed. Then what's the difference between a computed and a readonly you might ask. A computed can be a mixture of stuff. For example:
const state = ref({ first: 'Adnan', last: 'Babakan' }) const fullName = computed(() => state.value.first + ' ' + state.value.last)
Now you have a fullName which you may access its value inside a template with {{ fullName }} or inside your script using fullName.value.
The value of fullName will always depend on the state.value.first and state.value.last and will change if those guys change.
A computed receives a function that returns a value and can depend on multiple reactive data.
Though a computed is mostly used to read a combination of data, the possibility to make a computed writable is also there.
Instead of passing a function to computed you may pass an object including two properties called get and set that both are functions.
For instance:
const state = ref({ first: 'Adnan', last: 'Babakan' }) const fullName = computed({ get: () => state.value.first + ' ' + state.value.last, set: (value) => { const [first, last] = value.split(' ') state.value.first = first state.value.last = last } })
Now if you try to write the value of fullName like below:
fullName.value = 'Ata Parvin'
It will split your string into 2 parts using a space and assign the first part to state.value.first and the second to state.value.last.
This is not a good way to determine which part of a name is a first name and which is a last name, but for the sake of demonstration, this is the only thing that came to my mind. :D
Watching is something that you will probably need a lot. Watching is referred to the act in which you want to run something in case a reactive data changes. In different systems there are various naming for this act, sometimes they are called hooks as well. But in Vue, we will call them watches.
The first thing you will encounter. A watch function receives two arguments. The reactive data to watch and the function to be invoked when the data changes respectively.
Here is a simple watch:
const count = ref(0) const increase = () => { count.value++ } watch(count, () => { console.log('Count changed to ' + count.value) })
Now whenever the value of count is changed, you will see the log Count changed to ((count)) in your console.
The callback function also receives two arguments which are passed to it when the watch is triggered. The first argument holds the new value and the second one holds the old value. Here is an example:
const count = ref(0) const increase = () => { count.value++ } watch(count, (newValue, oldValue) => { console.log('Counter changed from ' + oldValue + ' to ' + newValue) })
Note: Be careful when using the newValue and oldValue with objects as objects are passed by reference.
To be more accurate, a watch function receives a third argument as well. This third argument is an object that holds some options which can change the behaviour of the watching action.
An immediate watch function is triggered at the instance it's created as well as when a change happens. You can think of it as the difference between a while loop and a do...while loop if you know what I mean. In other words, even if there is never a change, your callback function will run at least once:
watch(count, () => { console.log('Count changed to ' + count.value) }, { immediate: true, })
The value for immediate can be true or false. And the default value is false.
If you want your watcher to run only once, you may define the once option and set its value to true. The default value is false.
watch(count, () => { console.log('Count changed to ' + count.value) }, { once: true, })
This will only trigger once when the value of count changes.
Previously we've mentioned that watchers accept a reactive data as the first argument. While this is true, this is not the whole case.
A watch function can receive a getter function or an array of reactive objects and getter functions. This is used for when we need to watch for multiple data changes, and/or when we need to watch the result of two or more things when affecting each other. Let's have some examples.
Take the code below as an example:
<template> <div> <div> <div>Timer one: {{ timerOne }}</div> <div>Timer two: {{ timerTwo }}</div> </div> <button @click="timerOne++">Accelerate timer one</button> <button @click="timerTwo++">Accelerate timer two</button> </div> </template> <script setup> const timerOne = ref(0) const timerTwo = ref(0) onMounted(() => { setInterval(() => { timerOne.value++ timerTwo.value++ }, 1000) }) watch(() => timerOne.value - timerTwo.value, () => { console.log('There was a change in the gap between timerOne and timerTwo. Gap is ' + (Math.abs(timerOne.value - timerTwo.value)) + ' seconds.') }) </script>
It's a simple code that makes 2 refs holding a number and increasing both of them 1 by 1 each second. Logically the difference of these two refs are always equal to zero unless one gets changes out of its turn. As both increase the difference stays 0 so the watched won't get triggered as it only watches for the changes to the result of timerOne.value - timerTwo.value.
Yet there are two buttons that each adds 1 to timerOne and timerTwo respectively. When you click on any of those buttons the difference will be more or less than 0 thus the watch being triggered and logging the gap between these two timers.
Here is an example of an array of reactive data being passed to the first argument of the watch function:
<template> <div> <div> <div>Counter one: {{ counterOne }}</div> <div>Counter two: {{ counterTwo }}</div> </div> <button @click="counterOne++">Increase counter one</button> <button @click="counterTwo++">Increase counter two</button> </div> </template> <script setup> const counterOne = ref(0) const counterTwo = ref(0) watch([ counterOne, counterTwo, ], () => { console.log('One of the counters changes') }) </script>
No matter which ref changes, the watcher will be triggered.
A watchEffect function acts almost exactly like a watch function with a main difference. In a watchEffect you don't need to define what to watch and any reactive data that is used inside the callback you provide your watchEffect is watched automatically. For example:
const count = ref(0) watchEffect(() => { console.log(count.value) })
In case our count is changed the watchEffect will trigger its callback function since count.value is used inside it. This is good if you have complex logic to watch for.
Hope this was useful and you've enjoyed it. In case you spot any mistakes or feel like there should be an improvement, kindly let me know.
BTW! Check out my free Node.js Essentials E-book here:
Feel free to contact me if you have any questions or suggestions.
위 내용은 다크 사이드의 Vue 치트 시트 | 부품 반응성의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!