Ein umfassendes Verständnis der funktionalen Komponenten von Vue.js

Ein umfassendes Verständnis der funktionalen Komponenten von Vue.js

Vorwort

Wenn Sie Front-End-Entwickler sind und gelegentlich Java-Code gelesen haben, ist Ihnen möglicherweise eine Schreibweise aufgefallen, die der Pfeilfunktion in der ES6-Syntax ähnelt.

(Zeichenfolge a, Zeichenfolge b) -> a.toLowerCase() + b.toLowerCase();

Dieser Lambda-Ausdruck, der nach Java 8 erschien, erscheint in C++/Python. Er ist kompakter als herkömmlicher OOP-Code. Obwohl dieser Ausdruck in Java im Wesentlichen ein funktionaler Schnittstellen-Syntaktikzucker ist, der Klasseninstanzen generiert, sind seine prägnante Schreibweise und das Verhalten , unveränderliche Werte zu verarbeiten und sie einem anderen Wert zuzuordnen, typische Merkmale der funktionalen Programmierung (FP).

Butler Lampson, der Turing-Award-Gewinner von 1992, machte eine berühmte Aussage:

Alle Probleme der Informatik können durch eine andere Ebene der Indirektion gelöst werden
Jedes Problem in der Informatik kann durch Hinzufügen einer Indirektionsebene gelöst werden

Der „Indirektionslevel“ in diesem Satz wird oft als „Abstraktionslevel“ übersetzt, und obwohl einige über seine Strenge diskutiert haben, ist der Satz unabhängig von der Übersetzung sinnvoll. In jedem Fall ist die Übernahme von FP durch OOP-Sprachen ein direktes Spiegelbild der zunehmenden Integration und Betonung der funktionalen Programmierung im Programmierbereich und bestätigt zudem den „Grundsatz der Softwareentwicklung“, dass praktische Probleme durch die Einführung einer weiteren Indirektionsebene gelöst werden können.

Es gibt ein weiteres beliebtes Sprichwort, das nicht unbedingt so streng ist:

OOP ist eine Abstraktion von Daten, während FP zur Abstraktion von Verhalten verwendet wird

Im Unterschied zur objektorientierten Programmierung, die verschiedene Objekte abstrahiert und den Entkopplungsproblemen zwischen ihnen Aufmerksamkeit schenkt, konzentriert sich die funktionale Programmierung auf die kleinste Einzeloperation und verwandelt komplexe Aufgaben in wiederholte Überlagerungen von Funktionsoperationen des Typs f(x) = y. Funktionen sind erstklassige Objekte in FP und können als Funktionsparameter verwendet oder von Funktionen zurückgegeben werden.

Gleichzeitig sollten Funktionen in FP nicht von externen Zuständen abhängen oder diese beeinflussen, was bedeutet, dass für eine gegebene Eingabe die gleiche Ausgabe erzeugt wird – aus diesem Grund werden in FP häufig Wörter wie „unveränderlich“ und „rein“ verwendet; wenn Sie auch die zuvor erwähnte „Lambda-Rechnung“ und „Curring“ erwähnen, klingen Sie wie ein FP-Enthusiast.

Die oben genannten Konzepte und die damit verbundenen Theorien entstanden in der ersten Hälfte des 20. Jahrhunderts. Viele Wissenschaftler haben bei ihrer Forschung zur mathematischen Logik fruchtbare Ergebnisse erzielt. Sogar das populäre ML und die KI haben von diesen Ergebnissen profitiert. Beispielsweise hinterließ Haskell Curry, ein polnisch-amerikanischer Meister der Mathematik seiner Zeit, seinen Namen in typischen funktionalen Praktiken wie der Sprache Haskell und dem Currying.

Funktionale React-Komponenten

Wenn Sie die „Kettensyntax“ von jQuery/RxJS verwendet haben, kann dies tatsächlich als Monadenpraxis in FP angesehen werden. In den letzten Jahren sind die meisten Front-End-Entwickler wirklich mit FP in Kontakt gekommen. Einer davon stammt von den Array-Instanzmethoden im funktionalen Stil, wie z. B. Map/Reduce, die in ES6 eingeführt wurden, und der andere von der funktionalen Komponente (FC – funktionale Komponente) in React.

Funktionale Komponenten in React werden oft als zustandslose Komponenten bezeichnet. Ein intuitiverer Name ist Renderfunktion, da es sich eigentlich nur um eine Funktion handelt, die zum Rendern verwendet wird:

const Willkommen = (Requisiten) => { 
  return <h1>Hallo, {props.name}</h1>; 
}

In Kombination mit TypeScript können Sie auch type und FC<propsType> verwenden, um die Eingabeparameter dieser Funktion einzuschränken, die jsx zurückgibt:

Typ GreetingProps = {
 Name: Zeichenfolge;
}
 
const Greeting:React.FC<GreetingProps> = ({ name }) => {
 return <h1>Hallo {name}</h1>
};

Sie können auch Schnittstellen und Paradigmen verwenden, um Requisitentypen flexibler zu definieren:

Schnittstelle IGreeting<T = 'm' | 'f'> {
 Name: Zeichenfolge;
 Geschlecht: T
}
export const Greeting = ({ Name, Geschlecht }: IGreeting<0 | 1>): JSX.Element => {
 return <h1>Hallo { gender === 0 ? 'Frau' : 'Herr' } {name}</h1>
};

Funktionale Komponenten in Vue (2.x)

Im Abschnitt [Funktionale Komponenten] der Dokumentation auf der offiziellen Vue-Website wird es wie folgt beschrieben:

... wir können eine Komponente als funktional markieren, was bedeutet, dass sie zustandslos ist (keine reaktiven Daten) und keine Instanz hat (kein this-Kontext). Eine funktionsfähige Komponente sieht folgendermaßen aus:
 
Vue.component('meine-Komponente', {
  funktional: wahr,
  // Requisiten sind optionale Requisiten: {
    // ...
  },
  // Um ​​das Fehlen einer Instanz auszugleichen // geben Sie ein zweites Argument als Kontext an render: function (createElement, context) {
    // ...
  }
})
 
...
 
Wenn Sie in Version 2.5.0 und höher [Einzeldateikomponenten] verwenden, können vorlagenbasierte Funktionskomponenten wie folgt deklariert werden:
 
<funktionale Vorlage>
</Vorlage>

Entwickler, die React geschrieben haben und dieses Dokument zum ersten Mal lesen, rufen möglicherweise unbewusst aus: „Ah, das …“ Allein das Schreiben des Wortes functional wird als funktional bezeichnet? ? ?

Tatsächlich können Sie in Vue 3.x „funktionale Komponenten“ schreiben, die reine Rendering-Funktionen sind, genau wie React . Wir werden später darüber sprechen.

Im allgemeiner verbreiteten Vue 2.x ist eine funktionale Komponente (FC) wie in der Dokumentation angegeben eine Komponente ohne Instanz (kein this-Kontext, keine Lebenszyklusmethoden, kein Abhören von Eigenschaften, keine Verwaltung von Zuständen) . Von außen kann es wahrscheinlich als fc(props) => VNode -Funktion betrachtet werden, die einfach einige Props akzeptiert und wie erwartet eine Art Rendering-Ergebnis zurückgibt.

Darüber hinaus basiert die echte FP-Funktion auf einem unveränderlichen Zustand, und die „funktionalen“ Komponenten in Vue sind nicht so ideal – letztere basieren auf variablen Daten und haben im Vergleich zu gewöhnlichen Komponenten einfach kein Instanzkonzept. Aber seine Vorteile liegen dennoch auf der Hand:

Da funktionale Komponenten Implementierungslogik wie Lebenszyklus und Überwachung ignorieren, ist der Rendering-Overhead sehr gering und die Ausführungsgeschwindigkeit hoch

Im Vergleich zu v-if und anderen Anweisungen in gewöhnlichen Komponenten ist die Verwendung der h-Funktion oder die Kombination der jsx-Logik klarer

Das HOC-Muster (Higher-Order Component) lässt sich einfacher implementieren. Dabei handelt es sich um eine Containerkomponente, die eine gewisse Logik kapselt und parametrische untergeordnete Komponenten bedingt rendert.

Mehrere Stammknoten können über ein Array zurückgegeben werden

🌰 Beispiel: Optimieren benutzerdefinierter Spalten in el-table

Lassen Sie uns zunächst intuitiv ein typisches Szenario durchgehen, in dem FC anwendbar ist:

551072df36b6159d4aa452536c412f12.png

Dies ist ein Beispiel für eine benutzerdefinierte Tabellenspalte auf der offiziellen ElementUI-Website. Der entsprechende Vorlagencode lautet:

<Vorlage>
  <el-Tabelle
    :data="Tabellendaten"
    Stil="Breite: 100%">
    <el-table-column
      Bezeichnung="Datum"
      Breite="180">
      <template slot-scope="Umfang">
        <i Klasse="el-icon-time"></i>
        <span style="margin-left: 10px">{{ Umfang.row.date }}</span>
      </Vorlage>
    </el-Tabellenspalte>
    <el-table-column
      Bezeichnung="Name"
      Breite="180">
      <template slot-scope="Umfang">
        <el-popover trigger="hover" placement="oben">
          <p>Name: {{ Umfang.Zeile.Name }}</p>
          <p>Adresse: {{ scope.row.address }}</p>
          <div Slot="Referenz" Klasse="Name-Wrapper">
            <el-tag size="medium">{{ Umfang.Zeile.Name }}</el-tag>
          </div>
        </el-popover>
      </Vorlage>
    </el-Tabellenspalte>
    <el-table-column label="Vorgang">
      <template slot-scope="Umfang">
        <el-Schaltfläche
          Größe="mini"
          @click="handleEdit(scope.$index, scope.row)">Bearbeiten</el-button>
        <el-Schaltfläche
          Größe="mini"
          Typ="Gefahr"
          @click="handleDelete(scope.$index, scope.row)">Löschen</el-button>
      </Vorlage>
    </el-Tabellenspalte>
  </el-Tabelle>
</Vorlage>

In tatsächlichen Geschäftsanforderungen gibt es sicherlich kleine Tabellen wie die in den Dokumentbeispielen, aber sie stehen nicht im Mittelpunkt unserer Aufmerksamkeit. Benutzerdefinierte Tabellenspalten von ElementUI werden häufig in der Rendering-Logik großer Berichte mit zahlreichen Feldern und komplexen Interaktionen verwendet. Sie beginnen normalerweise mit mehr als 20 Spalten, und jede Spalte enthält eine Liste von Bildern, Popups mit Videovorschauen, Absätze, die kombiniert und formatiert werden müssen, und eine unbestimmte Anzahl von Bedienschaltflächen, abhängig von Berechtigungen oder Status. Der zugehörige Vorlagenteil umfasst häufig Hunderte von Zeilen oder sogar mehr. Abgesehen davon, dass es langwierig ist, ist es auch schwierig, ähnliche Logik in verschiedenen Spalten wiederzuverwenden.

Wie es in der Fernsehserie „Friends“ hieß:

Willkommen in der realen Welt! Es ist ätzend – aber du wirst es lieben!

Vue-Einzeldateikomponenten bieten keine Lösungen zum Aufteilen von Vorlagen wie „Include“ – schließlich gibt es genügend Syntaxzucker, und den sollte man am besten nicht haben.

Entwickler mit Mysophobie werden versuchen, komplexe Spaltenvorlagen in unabhängige Komponenten zu kapseln, um dieses Problem zu lösen. Das ist bereits sehr gut, birgt jedoch im Vergleich zur ursprünglichen Schreibmethode Leistungsrisiken.

Wenn man sich an Ihr überwältigendes Selbstvertrauen erinnert, mit dem Sie im Interview die Frage beantwortet haben, wie die Darstellung mehrschichtiger Knoten optimiert werden kann 😼, sollten wir in dieser Praxis offensichtlich noch einen Schritt weiter gehen, um die Bedenken aufzuteilen und Leistungsprobleme zu vermeiden. Funktionale Komponenten sind in diesem Szenario eine geeignete Lösung.

Als Erstes haben wir versucht, die Datumsspalte in der Originalvorlage in eine funktionale Komponente DateCol.vue zu „übersetzen“:

<funktionale Vorlage>
  <div>
    <i Klasse="el-icon-time"></i>
    <span style="margin-left: 10px; color: blue;">{{ props.row.date }}</span>
  </div>
</Vorlage> 

0107d1f090b1cfde7c5c3a6312acd6c4.png

Nach dem Importieren in die Containerseite deklarieren Sie es in Komponenten und verwenden Sie es:

cddd2a771a92e3966399c2366293df6c.png

Es ist im Grunde dasselbe wie zuvor; das einzige Problem besteht darin, dass es durch ein einzelnes Stammelement begrenzt ist und eine zusätzliche Div-Ebene hat, was auch mit Vue-Fragment usw. gelöst werden kann.

Als nächstes refaktorisieren wir die Spalte „Name“ in NameCol.js:

Standard exportieren {
  funktional: wahr,
  rendern(h, {Eigenschaften}) {
    const {row} = Requisiten;
    return h('el-popover', {
        Requisiten: {Trigger: "hover", Platzierung: "top"},
        Bereichsslots: {
          Referenz: () => h('div', {Klasse: "name-wrapper"}, [
            h('el-tag', {Eigenschaften: {Größe: 'medium'}}, [Zeilenname + '~'])
          ])
        }
      }, [
          h('p', null, [`Name: ${ row.name }`]),
          h('p', null, [`Adresse: ${ row.address }`])
      ])
  }
} 

c9bcf10e1dd5fb7cf6598fc80743f28d.png

b93923ff0aa5cb73d08a003bb644ea65.png

Der Effekt ist erstaunlich und die Verwendung von Arrays umgeht die Beschränkung auf ein einzelnes Root-Element. Noch wichtiger ist, dass dieses abstrahierte Widget ein echtes JS-Modul ist. Sie können es in eine .js Datei einfügen, anstatt es mit <script> zu umschließen, und Sie können frei damit machen , was Sie wollen .

Die h-Funktion kann eine gewisse zusätzliche geistige Belastung darstellen, aber solange sie mit JSX-Unterstützung konfiguriert ist, ist sie fast identisch mit der Originalversion.

Darüber hinaus werden wir über die hier beteiligten ScopedSlots und die Ereignisbehandlung sprechen, die später in der dritten Spalte behandelt wird.

Rendering-Kontext

Erinnern Sie sich an den oben erwähnten Dokumentationsabschnitt, in dem es heißt, dass die Renderfunktion folgende Form hat:

rendern: Funktion (createElement, Kontext) {}

Bei der tatsächlichen Codierung wird „createElement“ normalerweise als „h“ geschrieben, und selbst wenn „h“ bei der jsx-Verwendung nicht aufgerufen wird, muss es dennoch geschrieben werden. In Vue3 können Sie import { h } from 'vue' verwenden, um es global zu importieren.

Es stammt vom Begriff „Hyperscript“, der häufig in vielen Virtual-Dom-Implementierungen verwendet wird. „Hyperscript“ selbst steht für „Skript, das HTML-Strukturen generiert“, da HTML das Akronym für „Hypertext Markup Language“ ist. – Evan You

Im Dokument auf der offiziellen Website heißt es weiter:

Alles, was eine Komponente benötigt, wird als Kontextparameter übergeben. Dabei handelt es sich um ein Objekt mit den folgenden Feldern:

props: ein Objekt, das alle Requisiten bereitstellt
children: Array von VNode-Unterknoten
Slots: Eine Funktion, die ein Objekt zurückgibt, das alle Slots enthält
scopedSlots: (2.6.0+) Ein Objekt, das die übergebenen Scoped Slots verfügbar macht. Stellt auch normale Slots als Funktionen bereit.
data: Das gesamte an die Komponente übergebene Datenobjekt, das der Komponente als zweiter Parameter von createElement übergeben wird
parent: ein Verweis auf die übergeordnete Komponente
Listener: (2.3.0+) Ein Objekt, das alle von übergeordneten Komponenten für diese Komponente registrierten Ereignis-Listener enthält. Dies ist ein Alias ​​für data.on.
Injektionen: (2.3.0+) Wenn die Injektionsoption verwendet wird, enthält dieses Objekt die Eigenschaften, die injiziert werden sollen.

Dieser Kontext wird als Schnittstellentyp von RenderContext definiert. Beim Initialisieren oder Aktualisieren von Komponenten in Vue wird er wie folgt gebildet:

9e29f71bf512349a61441dd4d13a4535.png

Die Beherrschung der verschiedenen durch die RenderContext-Schnittstelle definierten Eigenschaften ist für uns die Grundlage für das Spielen mit funktionalen Komponenten.

Vorlage

Im vorherigen Beispiel haben wir eine Vorlage mit dem funktionalen Attribut verwendet, um die Logik der Datumsspalte in der Tabelle in ein unabhängiges Modul zu abstrahieren.

Dies wird auch im obigen schematischen Diagramm teilweise erklärt. Die Vorlage von Vue wird tatsächlich in eine Rendering-Funktion kompiliert , oder die Vorlage und die explizite Render-Funktion folgen derselben internen Verarbeitungslogik und sind mit Eigenschaften wie $options verknüpft.

Mit anderen Worten, wenn wir mit komplexer Logik arbeiten, können wir immer noch die Leistungsfähigkeit von js nutzen, z. B. indem wir regelmäßig Methoden in Vorlagen aufrufen usw. – dies ist natürlich keine echte Vue-Komponentenmethode:

b31e059aa65f9ab946da51dbb2831634.png

emittieren

In funktionalen Komponenten gibt es keine Methode wie this.$emit() .

Ereignisrückrufe können jedoch weiterhin normal verarbeitet werden. Dazu müssen Sie die Eigenschaft context.listeners verwenden. Wie in der Dokumentation erwähnt, handelt es sich dabei um einen Alias ​​für data.on. Im vorherigen Beispiel möchten wir beispielsweise auf Klicks auf das Symbol der Datumsspalte auf der Containerseite warten:

<date-col v-bind="Umfang" @icon-click="beimKlick aufDatum" />

Lösen Sie in DateCol.vue das Ereignis folgendermaßen aus:

<i Klasse="el-icon-time" 
      @click="() => listeners['icon-click'](props.row.date)">
    </i>

Zu beachten ist lediglich, dass die obige Methode zwar in den meisten Fällen ausreichend ist, die Listener jedoch zu einem Array werden, wenn mehrere Ereignisse mit demselben Namen extern abgehört werden. Eine relativ vollständige Kapselungsmethode ist daher:

/**
 * Ereignisauslösende Methode für funktionale Komponenten * @param {object} listeners – Listener-Objekt im Kontext * @param {string} eventName – Ereignisname * @param {...any} args – mehrere Parameter * @returns {void} – Keine */
export const fEmit = (Listener, Ereignisname, ...Argumente) => {
    const cbk = listeners[Ereignisname]
    wenn (_.isFunction(cbk)) cbk.apply(null, args)
    sonst wenn (_.isArray(cbk)) cbk.forEach(f => f.apply(null, args))
}

Filter

<label>{ title | withColon }</label> in herkömmlichen Vue-Vorlagen funktioniert in der Rückgabestruktur von h-Funktionen oder jsx nicht mehr.

Glücklicherweise ist die ursprünglich definierte Filterfunktion auch eine normale Funktion, sodass sie wie folgt geschrieben werden kann:

Filter aus „@/filters“ importieren;
 
const { mit Doppelpunkt } = Filter;
 
//...
 
// Rendern gibt jsx zurück <label>{ withColon(title) }</label>

Spielautomaten

Die Methode zum Verwenden von Slots im Vorlagenteil gewöhnlicher Komponenten kann nicht in der Renderfunktion funktionaler Komponenten verwendet werden, auch nicht im jsx-Modus.

Im vorherigen Beispiel wurde beim Refactoring der Namensspalte in NameCol.js die entsprechende Schreibmethode demonstriert. Sehen wir uns nun ein Beispiel für eine Skelett-Bildschirmkomponente in ElementUI an. Die allgemeine Verwendung von Vorlagen sieht beispielsweise so aus:

<el-skeleton :loading="skeLoading">
    echter Text
    <template slot="Vorlage">
      <p>Inhalt wird geladen</p>
    </Vorlage>
</el-skelett>

Dabei handelt es sich eigentlich um zwei Slots, Standard und Vorlage. Beim Umschalten auf die Renderfunktion der Funktionskomponente lautet die entsprechende Schreibmethode:

Standard exportieren {
  funktional: wahr,
  Requisiten: ['ok'],
  rendern(h, {Eigenschaften}) {
    return h('el-skeleton' ,{
      Requisiten: {wird geladen: props.ok},
      Bereichsslots: {
        Standard: () => 'echter Text',
        Vorlage: () => h('p', null, ['Kontext wird geladen'])
      }
    }, null)
  }
}

Wenn Sie auf einen Slot mit Gültigkeitsbereich stoßen, der Attribute wie v-bind:user="user" übergibt, können Sie „user“ als Eingabeparameter der Slot-Funktion verwenden.

In der offiziellen Website-Dokumentation wird auch der Vergleich zwischen slots() und children erwähnt:

<meine-funktionale-Komponente>
  <p v-Steckplatz:foo>
    Erste
  </p>
  <p>Sekunde</p>
</meine-funktionale-komponente>
 
Für diese Komponente erhalten Sie durch untergeordnete Elemente zwei Absatz-Tags, während slots().default nur das zweite anonyme Absatz-Tag übergibt und slots().foo das erste benannte Absatz-Tag übergibt. Es verfügt sowohl über untergeordnete Elemente als auch über Slots(), sodass Sie Ihre Komponente auf einen Slot-Mechanismus aufmerksam machen oder ihn einfach durch Übergeben untergeordneter Elemente an andere Komponenten weitergeben können.

bereitstellen / injizieren

Beachten Sie neben der in der Dokumentation erwähnten Verwendung von Injektionen auch, dass provide / inject in Vue 2 letztendlich nicht reagiert.

Wenn Sie diese Methode nach der Auswertung verwenden müssen, können Sie vue-reactive-provide ausprobieren

HTML-Inhalt

Das jsx in Vue kann das Schreiben von v-html in der normalen Komponentenvorlage nicht unterstützen. Das entsprechende Elementattribut ist domPropsInnerHTML, beispielsweise:

<strong Klasse = {Typ} domPropsInnerHTML = {Formatwert (Element, Typ)} />

Bei der Render-Methode wird das Wort noch einmal zerlegt und wie folgt geschrieben:

h('p', {
      domProps: {
            innerHTML: '<h1>hallo</h1>'
      }
})

Es ist ohnehin etwas mühsamer zu schreiben, aber glücklicherweise leichter zu merken als dangerouslySetInnerHTML in React.

Stil

Wenn Sie reine .js -/ .ts -Komponenten verwenden, besteht das einzige Problem möglicherweise darin, dass Sie die bereichsbezogenen Stile in .vue Komponenten nicht mehr nutzen können. In Bezug auf die Situation von React gibt es nur wenige Möglichkeiten, dieses Problem zu lösen:

  • Importieren Sie externe Stile und verwenden Sie Namenskonventionen wie BEM
  • Aktivieren Sie CSS-Module in den Vue-Loader-Optionen und wenden Sie styleMod.foo in Ihrer Komponente an.
  • Dynamisches Erstellen von Stil-Arrays oder Objekten im Modul und Zuweisen dieser zu Attributen
  • Verwenden Sie Toolmethoden, um Stilklassen dynamisch zu erstellen:
const _insertCSS = css => {
    let $head = document.head || document.getElementsByTagName('head')[0];
    const style = document.createElement('Stil');
    style.setAttribute('Typ', 'Text/CSS');
    wenn (Stil.StyleSheet) {
        Stil.styleSheet.cssText = css;
    } anders {
        Stil.appendChild(document.createTextNode(css));
    }
    $head.appendChild(Stil);
    $head = null;
};

Typoskript

Sowohl React als auch Vue bieten einige Möglichkeiten zum Überprüfen des Props-Typs. Allerdings sind diese Methoden etwas umständlich zu konfigurieren und für leichte Funktionskomponenten etwas zu schwerfällig.

Als stark typisierte Obermenge von JavaScript kann TypeScript zum genaueren Definieren und Überprüfen von Props-Typen verwendet werden, ist einfacher zu verwenden und verfügt über benutzerfreundlichere automatische Eingabeaufforderungen in VSCode oder anderen Entwicklungstools, die Vetur unterstützen.

Um Vue-Funktionskomponenten mit TS zu kombinieren, wie durch interface RenderContext<Props> für externe Eingabe-Props definiert, können Sie eine benutzerdefinierte TypeScript-Schnittstelle verwenden, um deren Struktur zu deklarieren, wie zum Beispiel:

Schnittstelle IProps {
 Jahr: Zeichenfolge;
 Viertel: Array<'Q1' | 'Q2' | 'Q3' | 'Q4'>;
 Notiz:
  Inhalt: Zeichenfolge;
  Autor: rühren;
 }
}

Geben Sie dann die Schnittstelle als ersten generischen Typ von RenderContext an:

importiere Vue, { CreateElement, RenderContext } von „vue“;
 
...
 
exportiere Standard Vue.extend({
  funktional: wahr,
  rendern: (h: CreateElement, Kontext: RenderContext<IProps>) => {
     Konsole.log(Kontext.Requisiten.Jahr);
   //...
  }
});

Kombinieren der Composition-API

Ähnlich dem Designzweck von React Hooks bringt auch die Vue Composition API bis zu einem gewissen Grad reaktionsfähige Funktionen, Lebenszykluskonzepte wie onMounted und Methoden zum Verwalten von Nebeneffekten auf Funktionskomponenten mit.

Hier besprechen wir nur eine einzigartige Schreibweise in der Composition-API – das Zurückgeben der Renderfunktion in der Setup()-Eintragsfunktion:

Definieren Sie beispielsweise einen Counter.js:

importiere { h, ref } von "@vue/composition-api";
 
Standard exportieren {
  Modell: {
    Requisite: "Wert",
    Veranstaltung: „Zouni“
  },
  Requisiten: {
    Wert: {
      Typ: Nummer,
      Standard: 0
    }
  },
  setup(Eigenschaften, { emit }) {
    const Zähler = ref(Eigenschaften.Wert);
    const increment = () => {
      emittieren("zouni", ++Zähler.Wert);
    };
 
    return () =>
      h("div", null, [h("button", { ein: { klick: inkrementieren } }, ["plus"])]);
  }
};

Auf der Containerseite:

<el-input v-model="cWert" />
<counter v-model="cWert" /> 

fc3645b0441ee1865f2c5e0581aed9f6.gif

Wenn Sie es in Verbindung mit TypeScript verwenden möchten, sind die einzigen Änderungen:

  • importiere { defineComponent } von "@vue/composition-api";
  • exportiere Standard defineComponent<IProps>({ Komponente })

Komponententests

Wenn die starke Typisierungsunterstützung von TypeScript verwendet wird, sind die Parametertypen innerhalb und außerhalb der Komponente besser geschützt.

Was die Komponentenlogik betrifft, sind zum Abschluss des Aufbaus des Sicherheitsgerüsts noch Unit-Tests erforderlich. Gleichzeitig ist das Schreiben von Tests nicht schwierig, da Funktionskomponenten im Allgemeinen relativ einfach sind.

Aufgrund der Unterschiede zwischen FC und herkömmlichen Komponenten gibt es in der Praxis noch einige kleinere Probleme, die beachtet werden müssen:

erneut rendern

Da funktionale Komponenten nur dann ein Rendering auslösen, wenn sich die von ihnen übergebenen Eigenschaften ändern, können Sie den aktualisierten Status nicht einfach durch den Aufruf nextTick() im Testfall abrufen. Sie müssen manuell ein erneutes Rendering auslösen :

it("Alles im Stapel auswählen", async () => {
    lass Ergebnis = MockData;
    // Dies simuliert tatsächlich den Prozess der Aktualisierung von Komponenten jedes Mal, wenn Props von außen übergeben werden. // wrapper.setProps() kann nicht für eine funktionale Komponente aufgerufen werden.
    const update = async () => {
      makeWrapper(
        {
          Wert: Ergebnis
        },
        {
          Zuhörer: {
            Änderung: m => (Ergebnis = m)
          }
        }
      );
      warte auf localVue.nextTick();
    };
    warte auf Update();
    erwarten(wrapper.findAll("Eingabe")).toHaveLength(6);
 
    wrapper.find("tr. gesamtes Etikett").trigger("Klick");
    warte auf Update();
    erwarten(wrapper.findAll("input:checked")).toHaveLength(6);
 
    wrapper.find("tr. gesamtes Etikett").trigger("Klick");
    warte auf Update();
    erwarten(wrapper.findAll("input:checked")).toHaveLength(0);
 
    wrapper.find("tr. gesamtes Etikett").trigger("Klick");
    warte auf Update();
    wrapper.find("tbody>tr:nth-child(3)>td:nth-child(2)>ul>li:nth-child(4)>label").trigger("klicken");
    warte auf Update();
    erwarten(wrapper.find("tr.gesamte Beschriftung Eingabe:geprüft").exists()).toBeFalsy();
  });

Mehrere Stammknoten

Ein Vorteil funktionaler Komponenten besteht darin, dass sie ein Array von Elementen zurückgeben können, was dem Zurückgeben mehrerer Stammknoten in render() entspricht.

Wenn Sie zu diesem Zeitpunkt ShallowMount oder andere Methoden direkt zum Laden von Komponenten im Test verwenden, tritt ein Fehler auf:

[Vue-Warnung]: Von der Renderfunktion wurden mehrere Stammknoten zurückgegeben. Die Renderfunktion sollte einen einzelnen Stammknoten zurückgeben.

Die Lösung besteht darin, eine Verpackungskomponente einzukapseln :

importiere { mount } von '@vue/test-utils'
Zelle aus '@/components/Cell' importieren
 
const WrappedCell = {
  Komponenten: { Zelle },
  Vorlage: `
    <div>
      <Zelle v-bind="$attrs" v-on="$listeners" />
    </div>
  `
}
 
const wrapper = mount(WrappedCell, {
  EigenschaftenDaten: {
    Zellendaten: {
      Kategorie: 'foo',
      Beschreibung: „Bar“
    }
  }
});
 
beschreiben('Cell.vue', () => {
  it('sollte zwei tds mit Kategorie und Beschreibung ausgeben', () => {
    erwarten(wrapper.findAll('td')).toHaveLength(2);
    erwarten(wrapper.findAll('td').at(0).text()).toBe('foo');
    erwarten(wrapper.findAll('td').at(1).text()).toBe('bar');
  });
});

Fragmentkomponente

Ein weiterer Trick, der mit FC verwendet werden kann, besteht darin, dass Sie für einige allgemeine Komponenten, die auf Vue-Fragmente verweisen (im Allgemeinen zum Lösen von Problemen mit mehreren Knoten verwendet), eine Funktionskomponente kapseln können, um die Fragmentkomponente in ihrem Komponententest auszulagern und so Abhängigkeiten zu reduzieren und das Testen zu erleichtern:

lass Wrapper = null;
const makeWrapper = (props = null, opts = null) => {
  Wrapper = Mount (Comp, {
    lokalesVue,
    EigenschaftenDaten: {
      ...Requisiten
    },
    Stummel:
      Fragment: {
        funktional: wahr,
        rendern(h, { Slots }) {
          gibt h zurück("div", slots().default);
        }
      }
    },
    attachmentToDocument: wahr,
    Synchronisierung: falsch,
    ...wählt
  });
};

Funktionale Komponenten in Vue 3

Dieser Teil stimmt im Wesentlichen mit unserer bisherigen Vorgehensweise in der Kompositions-API überein. Lassen Sie uns die Aussage grob aus dem neuen offiziellen Website-Dokument extrahieren:

Echte Funktionskomponenten

In Vue 3 werden alle Funktionskomponenten mithilfe normaler Funktionen erstellt. Mit anderen Worten besteht keine Notwendigkeit, die Komponentenoption { functional: true } zu definieren .

Sie erhalten zwei Parameter: props und context . context Kontextparameter ist ein Objekt, das die attrs , slots und emit -Eigenschaften der Komponente enthält.

Darüber hinaus wird h jetzt global importiert , anstatt implizit in der render bereitgestellt zu werden:

importiere { h } von 'vue'
 
const DynamicHeading = (Eigenschaften, Kontext) => {
  returniere h(`h${props.level}`, Kontext.attrs, Kontext.slots)
}
 
DynamicHeading.props = ['Ebene']
 
Standardmäßige dynamische Überschrift exportieren

Einzelne Dateikomponenten

In 3.x wurde der Leistungsunterschied zwischen zustandsbehafteten und funktionalen Komponenten stark reduziert und ist in den meisten Anwendungsfällen vernachlässigbar. Daher besteht der Migrationspfad für Entwickler, functional Komponenten auf Einzeldateikomponenten verwenden, darin, das Attribut zu entfernen und alle Verweise auf props in $props und attrs in $attrs umzubenennen :

<Vorlage>
  <Komponente
    v-bind:is="`h${$props.level}`"
    v-bind="$attrs"
  />
</Vorlage>
 
<Skript>
Standard exportieren {
  Requisiten: ['Level']
}
</Skript>

Die Hauptunterschiede sind:

  1. Entfernen Sie functional Attribut aus <template>
  2. listeners werden nun als Teil von $attrs übergeben und können entfernt werden

Zusammenfassen

Dies ist das Ende dieses Artikels über die funktionalen Komponenten von Vue.js. Weitere relevante funktionale Komponenten von Vue.js finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, Sie werden 123WORDPRESS.COM auch in Zukunft unterstützen!

Das könnte Sie auch interessieren:
  • Eine kurze Diskussion über die beste Lösung für VUE Anti-Shake und Throttling (funktionale Komponenten)
  • Detaillierte Anwendungsbeispiele für Vue-Funktionskomponenten
  • Funktionale Vue-Komponenten – Sie haben es verdient
  • Eine kurze Diskussion über die Verwendung von Vue-Funktionskomponenten

<<:  Eine detaillierte Analyse und Verarbeitung von MySQL-Alarmen

>>:  Lösung für das Versagen von Docker beim Freigeben von Ports

Artikel empfehlen

So implementiert Webpack das Caching statischer Ressourcen

Inhaltsverzeichnis Einführung Unterscheiden Sie z...

So schreiben Sie HTML-Header in der Webentwicklung für mobile Geräte

Code kopieren Der Code lautet wie folgt: <Kopf...

JavaScript erklärt die Kapselung und Verwendung von Zeitlupenanimationen

Durchführung von Prozessanalysen (1) Wie rufe ich...

Einführung in die Verwendung von CSS3-Farbwerten (RGBA) und Farbverlaufsfarben

Vor CSS3 konnten Verlaufsbilder nur als Hintergru...

So verwenden Sie JavaScript zum Implementieren von Sortieralgorithmen

Inhaltsverzeichnis Blasensortierung Auswahl Sorti...

jQuery verwendet das Canvas-Tag, um den Bestätigungscode zu zeichnen

Das <canvas>-Element ist für clientseitige ...

10 sehr gute CSS-Fähigkeiten sammeln und teilen

Hier können Sie durch geschickten Einsatz von CSS-...

Das schnellste Textsuchtool von Linux: ripgrep (die beste Alternative zu grep)

Vorwort Apropos Textsuchtools: Jeder sollte grep ...

Detaillierte Erklärung der Verwendung von overflow:auto

Bevor ich mit dem Haupttext beginne, werde ich ei...

32 typische spalten-/rasterbasierte Websites

Wenn Sie nach Inspiration für spaltenbasiertes Web...

...

Beispiel für die Kompilierung von LNMP im Docker-Container

Inhaltsverzeichnis 1. Projektbeschreibung 2. Ngin...