Behandeln Sie Randfälle
Auf dieser Seite wird davon ausgegangen, dass Sie Komponentengrundlagen gelesen haben. Wenn Sie noch nicht viel über Komponenten wissen, empfehle ich Ihnen, es zuerst zu lesen.
Was hier aufgezeichnet wird, sind Funktionen im Zusammenhang mit der Handhabung von Randfällen, also einigen Sonderfällen, die einige kleine Anpassungen der Vue-Regeln erfordern. Bitte beachten Sie jedoch, dass diese Funktionen alle Nachteile oder gefährliche Szenarien haben. Wir werden dies jeweils vermerken, achten Sie daher bitte bei der Nutzung der jeweiligen Funktion darauf.
Verzeichnis
Zugriffselemente & Komponenten
In den meisten Fällen ist es am besten, nicht das Innere einer anderen Komponenteninstanz zu berühren oder DOM-Elemente manuell zu manipulieren. Aber es gibt sicherlich Situationen, in denen es angebracht ist, diese Dinge zu tun.
Greifen Sie auf die Stamminstanz
in einer untergeordneten Komponente jeder new Vue
-Instanz zu , auf deren Stamminstanz über das Attribut $root
zugegriffen werden kann. In dieser Stamminstanz beispielsweise:
// Vue 根实例 new Vue({ data: { foo: 1 }, computed: { bar: function () { /* ... */ } }, methods: { baz: function () { /* ... */ } } })
Alle untergeordneten Komponenten können auf diese Instanz zugreifen oder sie als globalen Speicher verwenden.
// 获取根组件的数据 this.$root.foo // 写入根组件的数据 this.$root.foo = 2 // 访问根组件的计算属性 this.$root.bar // 调用根组件的方法 this.$root.baz()
Dies ist praktisch für Demos oder sehr kleine Anwendungen mit einer kleinen Anzahl von Komponenten. Dieses Modell erstreckt sich jedoch nicht auf mittlere und große Anwendungen. Daher empfehlen wir in den meisten Fällen dringend, Vuex zur Verwaltung des Anwendungsstatus zu verwenden.
Zugriff auf übergeordnete Komponenteninstanz
ähnelt $root
, $parent
Eigenschaften können verwendet werden, um von einer untergeordneten Komponente aus auf eine Instanz einer übergeordneten Komponente zuzugreifen. Es bietet die Möglichkeit, jederzeit später auf die übergeordnete Komponente zuzugreifen, anstatt Daten in Form von Requisiten an die untergeordnete Komponente zu übergeben.
In den meisten Fällen wird das Erreichen der übergeordneten Komponente das Debuggen und Verstehen Ihrer Anwendung erschweren, insbesondere wenn Sie die Daten der übergeordneten Komponente ändern. Wenn wir später auf diese Komponente zurückblicken, ist es schwierig herauszufinden, wo diese Änderung ihren Ursprung hat.
Darüber hinaus müssen Sie, wenn es angebracht ist, einige Komponentenbibliotheken gezielt freigeben. Zum Beispiel innerhalb einer abstrakten Komponente, die mit der JavaScript-API interagiert, ohne HTML zu rendern, wie diese hypothetischen Google Maps-Komponenten:
<google-map> <google-map-markers v-bind:places="iceCreamShops"></google-map-markers> </google-map>
Die <google-map>
-Komponente kann ein map
-Attribut definieren, das alle untergeordneten Komponenten benötigen um darauf zuzugreifen. In diesem Fall möchte <google-map-markers>
möglicherweise über etwas wie this.$parent.getMap
auf die Karte zugreifen, um ihr eine Reihe von Markierungen hinzuzufügen. Sie können sich dieses Muster hier ansehen.
Bitte beachten Sie, dass die Interna der nach diesem Muster erstellten Komponente jedoch immer noch anfällig für Probleme sind. Stellen Sie sich zum Beispiel vor, wir fügen eine neue <google-map-region>
-Komponente hinzu. Wenn <google-map-markers>
darin erscheint, wird nur das Markup in diesem Bereich gerendert:
<google-map> <google-map-region v-bind:shape="cityBoundaries"> <google-map-markers v-bind:places="iceCreamShops"></google-map-markers> </google-map-region> </google-map>
Dann finden Sie im Inneren von <google-map-markers>
möglicherweise einige Hacks wie Das für sich selbst:
var map = this.$parent.map || this.$parent.$parent.map
Es wird ziemlich schnell außer Kontrolle geraten. Aus diesem Grund empfehlen wir Dependency Injection, wenn Sie einer tiefer liegenden Komponente kontextbezogene Informationen bereitstellen müssen.
Zugriff auf untergeordnete Komponenteninstanzen oder untergeordnete Elemente
Trotz der Existenz von Requisiten und Ereignissen manchmal Möglicherweise müssen Sie dennoch direkt in JavaScript auf eine untergeordnete Komponente zugreifen. Um dies zu erreichen, können Sie der Kindkomponente über das Attribut ref
eine ID-Referenz zuweisen. Zum Beispiel:
<base-input ref="usernameInput"></base-input>
Jetzt können Sie in der Komponente, in der Sie dieses ref
definiert haben, Folgendes verwenden:
this.$refs.usernameInput
um in Notfällen auf diese <base-input>
Instanz zuzugreifen. Fokussieren Sie beispielsweise programmgesteuert das Eingabefeld einer übergeordneten Komponente. Im Beispiel gerade kann die <base-input>
-Komponente auch ein ähnliches ref
verwenden, um Zugriff auf das angegebene Element darin bereitzustellen, zum Beispiel:
<input ref="input">
kann sogar Methoden über seine übergeordnete Komponente definieren:
methods: { // 用来从父级组件聚焦输入框 focus: function () { this.$refs.input.focus() } }
Dadurch kann die übergeordnete Komponente über den folgenden Code fokussiert werden<base-input>
Eingabefeld in:
this.$refs.usernameInput.focus()
Wenn ref
und v-for
zusammen verwendet werden, ist die Referenz, die Sie erhalten, ein Array, das diese Unterkomponenten der entsprechenden Datenquelle enthält.
$refs
werden erst wirksam, nachdem die Komponente das Rendern abgeschlossen hat, und sie reagieren nicht. Dies dient nur als „Flucht“ für die direkte Manipulation untergeordneter Komponenten – Sie sollten den Zugriff auf$refs
in Vorlagen oder berechneten Eigenschaften vermeiden.
Abhängigkeitsinjektion
Vorher, bevor wir es beschreibenBeim Zugriff auf die übergeordnete Komponenteninstanz wird ein Beispiel ähnlich diesem angezeigt:
<google-map> <google-map-region v-bind:shape="cityBoundaries"> <google-map-markers v-bind:places="iceCreamShops"></google-map-markers> </google-map-region> </google-map>
In dieser Komponente alle Nachkommen von <google-map>
müssen auf eine getMap
-Methode zugreifen, um zu wissen, mit welcher Karte sie interagieren sollen. Leider lässt sich die Verwendung des $parent
-Attributs nicht gut auf tiefer verschachtelte Komponenten skalieren. Hier kommt die Abhängigkeitsinjektion ins Spiel, die zwei neue Instanzoptionen verwendet: provide
und inject
. Mit der Option
provide
können wir die Daten/Methoden angeben, die wir den Nachkommenkomponenten bereitstellen möchten. In diesem Beispiel ist es die <google-map>
-Methode in getMap
:
provide: function () { return { getMap: this.getMap } }
Dann können wir in jeder Nachkommenkomponente die Option inject
verwenden, um den angegebenen Wert zu erhalten, den wir diesen Instanzeigenschaften hinzufügen möchten von:
inject: ['getMap']
Das vollständige Beispiel können Sie hier sehen. Im Vergleich zu $parent
ermöglicht uns diese Verwendung den Zugriff auf getMap
in jeder untergeordneten Komponente, ohne die gesamte <google-map>
-Instanz verfügbar zu machen. Dadurch können wir die Komponente besser weiterentwickeln, ohne befürchten zu müssen, dass wir etwas ändern/entfernen, von dem Unterkomponenten abhängen. Gleichzeitig ist die Schnittstelle zwischen diesen Komponenten immer klar definiert, genau wie props
.
Tatsächlich kann man sich die Abhängigkeitsinjektion als Teil der „allgemein gültigen Requisite“ vorstellen, außer:
Die Vorgängerkomponente muss nicht wissen, welche Nachkommenkomponenten es sind Verwenden Sie es, um Eigenschaften bereitzustellen
Nachkommenkomponenten müssen nicht wissen, woher die injizierten Eigenschaften stammen
Abhängigkeitsinjektion hat jedoch immer noch negative Auswirkungen. Es koppelt die Komponenten in Ihrer Anwendung an die Art und Weise, wie sie aktuell organisiert sind, was die Umgestaltung erschwert. Außerdem reagieren die bereitgestellten Eigenschaften nicht. Dies ist beabsichtigt, da ihre Verwendung zur Erstellung einer zentralisierten Datenskala nicht ausreicht, um
$root
dafür zu verwenden. Wenn die Eigenschaft, die Sie freigeben möchten, anwendungsspezifisch und nicht generisch ist oder Sie die bereitgestellten Daten in einer Vorgängerkomponente aktualisieren möchten, bedeutet dies, dass Sie möglicherweise zu einem Vuex wie Vuex wechseln müssen eine echte State-Management-Lösung.
Weitere Informationen zur Abhängigkeitsinjektion finden Sie im API-Referenzdokument.
Programmatische Event-Listener
Jetzt wissen Sie es< Verwendung von 🎜>, es kann von $emit
angehört werden, aber Vue Instanzen stellen in ihrer Ereignisschnittstelle auch andere Methoden bereit. Wir können: v-on
- Ein Ereignis anhören über
$on(eventName, eventHandler)
- Ein Ereignis gleichzeitig anhören über
$once(eventName, eventHandler)
- Bestanden
Hören Sie auf, auf ein Ereignis zu lauschen
$off(eventName, eventHandler)
// 一次性将这个日期选择器附加到一个输入框上 // 它会被挂载到 DOM 上。 mounted: function () { // Pikaday 是一个第三方日期选择器的库 this.picker = new Pikaday({ field: this.$refs.input, format: 'YYYY-MM-DD' }) }, // 在组件被销毁之前, // 也销毁这个日期选择器。 beforeDestroy: function () { this.picker.destroy() }Hier gibt es zwei potenzielle Probleme:
- Es ist eine Instanz der Komponente erforderlich Speichern Sie dies in
, damit möglichst nur Lifecycle-Hooks darauf zugreifen können. Dies ist kein ernstes Problem, kann aber als Unordnung angesehen werden.
picker
- Unser Build-Code ist unabhängig von unserem Bereinigungscode, was es schwieriger macht, alles, was wir erstellen, programmgesteuert zu bereinigen.
mounted: function () { var picker = new Pikaday({ field: this.$refs.input, format: 'YYYY-MM-DD' }) this.$once('hook:beforeDestroy', function () { picker.destroy() }) }Mit dieser Strategie kann ich sogar mehrere Eingabefeldelemente gleichzeitig haben. Mit einem anderen Pikaday, Jede neue Instanz bereinigt sich programmgesteuert im Post:
mounted: function () { this.attachDatepicker('startDateInput') this.attachDatepicker('endDateInput') }, methods: { attachDatepicker: function (refName) { var picker = new Pikaday({ field: this.$refs[refName], format: 'YYYY-MM-DD' }) this.$once('hook:beforeDestroy', function () { picker.destroy() }) } }Schauen Sie sich das an
das fiddle kann den vollständigen Code lernen. Beachten Sie, dass es in der Regel am besten ist, mehr modulare Komponenten zu erstellen, wenn Sie viele Einrichtungs- und Aufräumarbeiten in einer einzelnen Komponente durchführen müssen. In diesem Beispiel empfehlen wir die Erstellung einer wiederverwendbaren -Komponente. <input-datepicker>
Um mehr über programmatische Listener zu erfahren, schauen Sie sich bitte die Instanzmethoden/-ereignisse bezogenen APIs an.
Beachten Sie, dass sich das Ereignissystem von Vue von der EventTarget-API des Browsers unterscheidet. Obwohl sie ähnlich funktionieren, sind
$emit
,$on
und$off
keine Aliase fürdispatchEvent
,addEventListener
undremoveEventListener
.
Zirkelverweis
Rekursive Komponenten
Komponenten können sich in ihren eigenen Vorlagen selbst aufrufen. Dies ist jedoch nur über die Option name
möglich:
name: 'unique-name-of-my-component'
Wenn Sie eine Komponente global mit Vue.component
registrieren, wird die globale ID automatisch auf die Option name
der Komponente gesetzt.
Vue.component('unique-name-of-my-component', { // ... })
Wenn Sie nicht aufpassen, können rekursive Komponenten Endlosschleifen verursachen:
name: 'stack-overflow', template: '<div><stack-overflow></stack-overflow></div>'
Komponenten wie die oben genannten verursachen den Fehler „Maximale Stapelgröße überschritten“. Stellen Sie daher bitte sicher, dass der rekursive Aufruf an Bedingungen geknüpft ist (z. B. mit einem false
, das mit v-if
endet).
Zirkelverweise zwischen Komponenten
Angenommen, Sie müssen einen Dateiverzeichnisbaum erstellen, z. B. access Like Dart oder Explorer. Möglicherweise haben Sie eine <tree-folder>
-Komponente mit einer Vorlage, die so aussieht:
<p> <span>{{ folder.name }}</span> <tree-folder-contents :children="folder.children"/> </p>
und eine <tree-folder-contents>
-Komponente mit einer Vorlage, die so aussieht:
<ul> <li v-for="child in children"> <tree-folder v-if="child.children" :folder="child"/> <span v-else>{{ child.name }}</span> </li> </ul>
Wenn Sie genau hinsehen, erkennen Sie Es stellt sich heraus, dass diese Komponenten im Rendering-Baum Abkömmlinge und Vorfahren voneinander sind – ein Paradoxon! Dieses Paradox wird automatisch gelöst, wenn Komponenten global über Vue.component
registriert werden. Wenn Sie dies tun, können Sie dies überspringen.
Wenn Sie jedoch ein Modulsystem verwenden, um Komponenten abzuhängen/zu importieren, z. B. über Webpack oder Browserify, wird eine Fehlermeldung angezeigt:
Failed to mount component: template or render function not defined.
Um zu erklären, was hier vor sich geht, lassen Sie uns Folgendes tun Setzen Sie zuerst die beiden Komponenten ein, die als A und B bezeichnet werden. Das Modulsystem stellt fest, dass es A benötigt, aber zuerst hängt A von B ab, aber B hängt von A ab, aber A hängt von B ab und so weiter. Dies wird zu einer Schleife und ich weiß nicht, wie ich eine der Komponenten vollständig analysieren kann, ohne die andere zu durchlaufen. Um dieses Problem zu lösen, müssen wir dem Modulsystem einen Punkt geben, an dem „A ohnehin B braucht, aber wir müssen B nicht zuerst analysieren.“
In unserem Beispiel stellen Sie die Komponente <tree-folder>
auf diesen Punkt ein. Wir wissen, dass die untergeordnete Komponente, die das Paradoxon verursacht, die <tree-folder-contents>
-Komponente ist, daher warten wir, bis der Lebenszyklus-Hook beforeCreate
sie registriert:
beforeCreate: function () { this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default }
Alternativ können Sie bei der lokalen Registrierung der Komponente Folgendes verwenden: Webpack Asynchronous import
:
components: { TreeFolderContents: () => import('./tree-folder-contents.vue') }
Dieses Problem ist gelöst!
Ersatz für Vorlagendefinition
Inline-Vorlage
Wann inline-template
Wenn diese spezielle Funktion in einer untergeordneten Komponente angezeigt wird, verwendet die Komponente den darin enthaltenen Inhalt als Vorlage, anstatt ihn als verteilten Inhalt zu verwenden. Dies macht das Schreiben von Vorlagen flexibler.
<my-component inline-template> <div> <p>These are compiled as the component's own template.</p> <p>Not parent's transclusion content.</p> </div> </my-component>
Inline-Vorlagen müssen innerhalb des DOM-Elements definiert werden, zu dem Vue gehört.
Allerdings macht
inline-template
den Umfang der Vorlage schwieriger zu verstehen. Als Best Practice wählen Sie bitte die Optiontemplate
innerhalb der Komponente oder ein.vue
-Element in der Datei<template>
aus, um die Vorlage zu definieren.
X-Template
Eine andere Möglichkeit, eine Vorlage zu definieren, ist in einem <script>
Element, geben Sie ihm den Typ text/x-template
und übergeben Sie dann eine ID Verweisen Sie auf die Vorlage. Beispiel:
<script type="text/x-template" id="hello-world-template"> <p>Hello hello hello</p> </script> Vue.component('hello-world', { template: '#hello-world-template' })
x-template muss außerhalb des DOM-Elements definiert werden, zu dem Vue gehört.
Diese können für Demos oder sehr kleine Anwendungen verwendet werden, bei denen die Vorlage besonders groß ist. Ansonsten vermeiden Sie bitte die Verwendung, da dadurch die Vorlage von anderen Definitionen der Komponente getrennt wird.
Updates steuern
Dank des reaktionsfähigen Systems von Vue weiß es immer Bescheid wann aktualisiert werden muss (wenn Sie es richtig machen). Es gibt jedoch Randfälle, in denen Sie eine Aktualisierung erzwingen möchten, obwohl sich die reaktiven Daten scheinbar nicht geändert haben. Es gibt auch Situationen, in denen Sie unnötige Updates verhindern möchten.
Update erzwingen
Wenn Sie feststellen, dass Sie ein Update in Vue benötigen Bei einem erzwungenen Update haben Sie in 99,9 % der Fälle irgendwo etwas falsch gemacht.
Möglicherweise haben Sie die Überlegungen zur Änderungserkennung für Arrays oder Objekte nicht bemerkt, oder Sie verlassen sich möglicherweise auf eines, das nicht vom reaktiven System von Vue verfolgt wird Status.
Wenn Sie jedoch die oben genannten Schritte ausgeführt haben und dennoch in seltenen Fällen feststellen, dass Sie ein Update manuell erzwingen müssen, können Sie dies über $forceUpdate
tun.
Erstellen Sie statische Komponenten mit geringem Overhead über v-once
Rendern Sie gewöhnliche HTML-Elemente in Vue ist sehr schnell, aber manchmal haben Sie möglicherweise eine Komponente, die viele statische Inhalte enthält. In diesem Fall können Sie das Attribut v-once
zum Stammelement hinzufügen, um sicherzustellen, dass diese Inhalte nur einmal berechnet und zwischengespeichert werden, wie folgt:
Vue.component('terms-of-service', { template: ` <div v-once> <h1>Terms of Service</h1> ... a lot of static content ... </div> ` })
Auch hier sollten Sie versuchen, es nicht zu übertreiben . In den seltenen Fällen, in denen Sie viele statische Inhalte rendern müssen, ist dies praktisch, aber wenn Sie nicht sehr auf die Verlangsamung des Renderns achten, ist dies völlig unnötig – und es wird beim Posten viel Ärger verursachen. Stellen Sie sich beispielsweise vor, dass ein anderer Entwickler mit
v-once
nicht vertraut ist oder es in der Vorlage übersieht. Er verbringt möglicherweise viele Stunden damit, herauszufinden, warum die Vorlage nicht korrekt aktualisiert wird.