Nicht zu verwechseln mit Lifecycle Hooks, wurden Hooks in React in v16.7.0-alpha eingeführt, und kurz darauf wurde ein Proof of Concept für Vue veröffentlicht. Auch wenn es von React vorgeschlagen wurde, ist es tatsächlich ein wichtiger Kompositionsmechanismus, der Vorteile für das gesamte Ökosystem von JavaScript-Frameworks bietet, daher werden wir heute etwas Zeit damit verbringen, zu diskutieren, was das bedeutet.
Hauptsächlich bieten Hooks eine explizitere Art, wiederverwendbare Muster zu denken – eine, die Umbauten an den Komponenten selbst vermeidet und es disparaten Teilen von zustandsbehafteter Logik ermöglicht, nahtlos zusammenzuarbeiten.
Das ursprüngliche Problem
In Bezug auf React war das Problem dieses: Klassen waren die häufigste Form von Komponenten, um das Konzept von Zustand auszudrücken. Zustandslose funktionale Komponenten waren ebenfalls recht beliebt, aber da sie nur wirklich rendern konnten, waren sie auf darstellende Aufgaben beschränkt.
Klassen an sich stellen einige Probleme dar. Als React beispielsweise immer allgegenwärtiger wurde, taten es auch Stolpersteine für Neulinge. Um React zu verstehen, musste man auch Klassen verstehen. Binding machte den Code ausführlich und damit weniger lesbar, und ein Verständnis von this in JavaScript war erforderlich. Es gibt auch einige Optimierungs-Stolpersteine, die Klassen mit sich bringen, die hier diskutiert werden.
Was die Wiederverwendung von Logik betrifft, war es üblich, Muster wie Render Props und Higher-Order Components zu verwenden, aber wir fanden uns in ähnlichen „Pyramiden des Verderbens“ – Stil der Implementationshölle wieder, wo die Verschachtelung so stark überstrapaziert wurde, dass Komponenten schwer zu warten waren. Das führte dazu, dass ich betrunken Dan Abramov beschimpfte, und niemand will das.
Hooks adressieren diese Bedenken, indem sie es uns ermöglichen, die zustandsbehaftete Logik einer Komponente nur anhand von Funktionsaufrufen zu definieren. Diese Funktionsaufrufe werden komponierbarer, wiederverwendbarer und ermöglichen es uns, Komposition in Funktionen auszudrücken, während wir dennoch auf Zustände zugreifen und diese verwalten können. Als Hooks in React angekündigt wurden, waren die Leute begeistert – Sie können hier einige der Vorteile sehen, wie sie Code und Wiederholungen reduzieren
Habe den Code von @dan_abramov von der #ReactConf2018 genommen und visualisiert, damit Sie die Vorteile sehen können, die React Hooks uns bringen. pic.twitter.com/dKyOQsG0Gd
— Pavel Prichodko (@prchdk) 29. Oktober 2018
Was die Wartung betrifft, ist Einfachheit der Schlüssel, und Hooks bieten eine einzige, funktionale Methode, um gemeinsam genutzte Logik mit dem Potenzial für weniger Code anzugehen.
Warum Hooks in Vue?
Sie lesen das vielleicht und fragen sich, was Hooks in Vue zu bieten haben. Es scheint ein Problem zu sein, das nicht gelöst werden muss. Schließlich verwendet Vue nicht überwiegend Klassen. Vue bietet zustandslose funktionale Komponenten (falls Sie sie benötigen), aber warum sollten wir einen Zustand in einer funktionalen Komponente tragen müssen? Wir haben Mixins für Komposition, wo wir dieselbe Logik für mehrere Komponenten wiederverwenden können. Problem gelöst.
Ich dachte dasselbe, aber nachdem ich mit Evan You gesprochen hatte, wies er auf einen wichtigen Anwendungsfall hin, den ich übersehen hatte: Mixins können keinen Zustand von einem zum anderen konsumieren und verwenden, aber Hooks können. Das bedeutet, dass, wenn wir verkettete gekapselte Logik benötigen, dies jetzt mit Hooks möglich ist.
Hooks erreichen, was Mixins tun, vermeiden aber zwei Hauptprobleme, die mit Mixins einhergehen
- Sie erlauben uns, Zustand von einem zum anderen zu übergeben.
- Sie machen explizit, woher die Logik kommt.
Wenn wir mehr als ein Mixin verwenden, ist nicht klar, welche Eigenschaft von welchem Mixin bereitgestellt wurde. Bei Hooks dokumentiert der Rückgabewert der Funktion den konsumierten Wert.
Wie funktioniert das also in Vue? Wir haben bereits erwähnt, dass bei der Arbeit mit Hooks Logik in Funktionsaufrufen ausgedrückt wird, die wiederverwendbar werden. In Vue bedeutet dies, dass wir einen Datenaufruf, einen Methodenaufruf oder einen berechneten Aufruf in einer anderen benutzerdefinierten Funktion gruppieren und sie frei komponierbar machen können. Daten, Methoden und berechnete Werte werden nun in funktionalen Komponenten verfügbar.
Beispiel
Lassen Sie uns einen sehr einfachen Hook durchgehen, damit wir die Bausteine verstehen, bevor wir zu einem Beispiel für Komposition in Hooks übergehen.
useWat?
Okay, hier haben wir, was man als Crossover-Event zwischen React und Vue bezeichnen könnte. Das use-Präfix ist eine React-Konvention. Wenn Sie also Hooks in React nachschlagen, finden Sie Dinge wie useState, useEffect usw. Weitere Informationen hier.
In Evans Live-Demo sehen Sie, wo er useState und useEffect für eine Render-Funktion aufruft.
Wenn Sie mit Render-Funktionen in Vue nicht vertraut sind, könnte es hilfreich sein, einen Blick darauf zu werfen.
Aber wenn wir mit Vue-Style-Hooks arbeiten, werden wir haben – Sie ahnen es – Dinge wie: useData, useComputed, etc.
Damit wir uns ansehen können, wie wir Hooks in Vue verwenden würden, habe ich eine Beispiel-App erstellt, die wir erkunden können.
Im Ordner src/hooks habe ich einen Hook erstellt, der das Scrollen bei einem useMounted-Hook verhindert und es bei useDestroyed wieder aktiviert. Das hilft mir, die Seite anzuhalten, wenn wir einen Dialog zum Anzeigen von Inhalten öffnen, und das Scrollen wieder zuzulassen, wenn wir mit dem Anzeigen des Dialogs fertig sind. Das ist eine gute Funktionalität zum Abstrahieren, da sie wahrscheinlich mehrmals in einer Anwendung nützlich wäre.
import { useDestroyed, useMounted } from "vue-hooks";
export function preventscroll() {
const preventDefault = (e) => {
e = e || window.event;
if (e.preventDefault)
e.preventDefault();
e.returnValue = false;
}
// keycodes for left, up, right, down
const keys = { 37: 1, 38: 1, 39: 1, 40: 1 };
const preventDefaultForScrollKeys = (e) => {
if (keys[e.keyCode]) {
preventDefault(e);
return false;
}
}
useMounted(() => {
if (window.addEventListener) // older FF
window.addEventListener('DOMMouseScroll', preventDefault, false);
window.onwheel = preventDefault; // modern standard
window.onmousewheel = document.onmousewheel = preventDefault; // older browsers, IE
window.touchmove = preventDefault; // mobile
window.touchstart = preventDefault; // mobile
document.onkeydown = preventDefaultForScrollKeys;
});
useDestroyed(() => {
if (window.removeEventListener)
window.removeEventListener('DOMMouseScroll', preventDefault, false);
//firefox
window.addEventListener('DOMMouseScroll', (e) => {
e.stopPropagation();
}, true);
window.onmousewheel = document.onmousewheel = null;
window.onwheel = null;
window.touchmove = null;
window.touchstart = null;
document.onkeydown = null;
});
}
Und dann können wir ihn in einer Vue-Komponente wie dieser in AppDetails.vue aufrufen
<script>
import { preventscroll } from "./../hooks/preventscroll.js";
...
export default {
...
hooks() {
preventscroll();
}
}
</script>
Wir verwenden ihn in dieser Komponente, aber jetzt können wir dieselbe Funktionalität in der gesamten Anwendung nutzen!
Zwei Hooks, die sich verstehen
Wir haben bereits erwähnt, dass einer der Hauptunterschiede zwischen Hooks und Mixins darin besteht, dass Hooks tatsächlich Werte von einem zum anderen übergeben können. Sehen wir uns das anhand eines einfachen, wenn auch etwas konstruierten, Beispiels an.
Nehmen wir an, wir müssen in unserer Anwendung Berechnungen in einem Hook durchführen, der woanders wiederverwendet werden soll, und etwas anderes, das diese Berechnung verwenden muss. In unserem Beispiel haben wir einen Hook, der die Fensterbreite nimmt und sie in eine Animation übergibt, um zu signalisieren, dass sie nur auf größeren Bildschirmen ausgelöst werden soll.
Im ersten Hook
import { useData, useMounted } from 'vue-hooks';
export function windowwidth() {
const data = useData({
width: 0
})
useMounted(() => {
data.width = window.innerWidth
})
// this is something we can consume with the other hook
return {
data
}
}
Dann verwenden wir im zweiten Hook dies, um eine Bedingung zu erstellen, die die Animationslogik auslöst
// the data comes from the other hook
export function logolettering(data) {
useMounted(function () {
// this is the width that we stored in data from the previous hook
if (data.data.width > 1200) {
// we can use refs if they are called in the useMounted hook
const logoname = this.$refs.logoname;
Splitting({ target: logoname, by: "chars" });
TweenMax.staggerFromTo(".char", 5,
{
opacity: 0,
transformOrigin: "50% 50% -30px",
cycle: {
color: ["red", "purple", "teal"],
rotationY(i) {
return i * 50
}
}
},
...
Dann übergeben wir in der Komponente selbst eine in die andere
<script>
import { logolettering } from "./../hooks/logolettering.js";
import { windowwidth } from "./../hooks/windowwidth.js";
export default {
hooks() {
logolettering(windowwidth());
}
};
</script>
Jetzt können wir Logik mit Hooks in unserer gesamten Anwendung komponieren! Dies ist wieder ein konstruiertes Beispiel zu Demonstrationszwecken, aber Sie können sehen, wie nützlich dies für groß angelegte Anwendungen sein kann, um Dinge in kleineren, wiederverwendbaren Funktionen zu halten.
Zukunftspläne
Vue Hooks sind bereits heute mit Vue 2.x verfügbar, sind aber noch experimentell. Wir planen, Hooks in Vue 3 zu integrieren, werden uns aber in unserer eigenen Implementierung wahrscheinlich von Reacts API unterscheiden. Wir finden React Hooks sehr inspirierend und denken darüber nach, wie wir seine Vorteile für Vue-Entwickler einführen können. Wir möchten dies auf eine Weise tun, die die idiomatische Verwendung von Vue ergänzt, daher gibt es noch viel zu experimentieren.
Sie können loslegen, indem Sie sich das Repo hier ansehen. Hooks werden wahrscheinlich ein Ersatz für Mixins sein, also ist es wahrscheinlich ein Konzept, das es wert ist, erkundet zu werden, auch wenn die Funktion noch in einem frühen Stadium ist.
(Herzlichen Dank an Evan You und Dan Abramov für die Korrektur dieses Artikels.)
Das ist schön. Ich liebe es, wie Sie wieder einmal zeigen, wie diese Bibliotheken aufeinander aufbauen, um das gleiche Ziel für Menschen mit unterschiedlichem architektonischem Geschmack zu erreichen. Ich hoffe, die Leute können daraus lernen und aufhören, schlecht über eine Technologie zu sprechen, die nicht ihrer strukturellen Vorliebe entspricht.
Chapeau dafür! Ich hatte immer Schwierigkeiten, wenn ich Vue-Komponenten, die ein oder mehrere Mixins verwenden, erneut aufrief, und wusste nicht, woher die Daten stammten, selbst wenn ich diese Mixins geschrieben hatte! Ich denke, vue-hooks werden dazu beitragen, Vue schön zu halten (kein Wortspiel beabsichtigt, sorry).
Hooks sind nutzlos, weil es in Vue Mixins gibt.
Danke, Sarah! Das klärt viele Fragen, die ich zum Einsatz von Hooks mit Vue und zu den Hauptvorteilen hatte.
Aber warum das Problem nicht einfach beheben? Wenn das Problem darin besteht, dass Mixins etwas nicht können, ... machen Sie sie dazu fähig.
Warum habe ich das Gefühl, dass die JavaScript-Community eine Tonne Zeit und Mühe damit verschwendet, das Rad für das Anwendungsdesign neu zu erfinden, nur weil A) JavaScript keine besonders gut designte Sprache ist und B) die Macher von React die Probleme lösten, *die sie* hatten, und nicht ein System entwarfen, das von der realen Welt genutzt werden kann.
Hooks klingen wie PHP-Traits; sie sind glorifizierte Globals und Globals sind im Allgemeinen eine extrem schlechte Designwahl. Sie sind einfach zu benutzen und können eine Rettung sein, aber der Trend in React ist, dass Hooks riesig sind und React zeigt seine Naivität, indem es Hooks als Lösung für ein Problem anbietet, das React hat. Es ist kein Programmierproblem, jede Sprache hat es schon *lange* gelöst, es ist ein React-Problem.