Vue: [Abandoned] RFC: Vereinfachte Slot-Nutzung

Erstellt am 11. Dez. 2018  ·  36Kommentare  ·  Quelle: vuejs/vue

Dies ist eine Fortsetzung von https://github.com/vuejs/vue/issues/7740#issuecomment -371309357

Rational

Probleme mit der aktuellen bereichsbezogenen Slot-Nutzung:

  • Ausführlich bei Verwendung von <template slot-scope>
  • Beschränkt auf ein Element/eine Komponente wird slot-scope direkt auf dem Slot-Element verwendet.

Vorschlag

Führen Sie eine neue v-scope Direktive ein, die nur für Komponenten verwendet werden kann:

<comp v-scope="scope">
  {{ scope.msg }}
</comp>

Es würde genauso funktionieren wie slot-scope für den standardmäßigen Scoped-Slot für <comp> (wobei <comp> den Scope-Wert bereitstellt). So funktioniert es auch beim Dekonstruieren:

<comp v-scope="{ msg }">
  {{ msg }}
</comp>

Warum eine neue Richtlinie

Ich glaube, das Team hat vor einiger Zeit kurz diskutiert, was ich in https://github.com/vuejs/vue/issues/7740#issuecomment -371309357 auf Slack vorgeschlagen habe, aber ich konnte den Chat-Datensatz nicht mehr finden. Hier ist die Begründung für eine neue Richtlinie:

  • slot-scope wurde als spezielles Attribut anstelle einer Direktive eingeführt (Attribute, die mit dem Präfix v- ), weil slot ein Attribut ist und wir die Slot-bezogenen Attribute konsistent halten wollten . slot wurde wiederum als nicht-direktives Attribut eingeführt, da wir möchten, dass die Verwendung die tatsächliche Slot-Nutzung im Shadow-DOM-Standard widerspiegelt. Wir dachten, es wäre am besten, unsere eigenen parallelen v-slot zu vermeiden, wenn es etwas gibt, das konzeptionell im Standard gleich ist.

  • Ursprünglich war slot-scope so konzipiert, dass es nur für <template> Elemente verwendet werden kann, die als abstrakte Container fungieren. Aber das war ausführlich - also haben wir die Möglichkeit eingeführt, es direkt auf einem Slot-Element zu verwenden, ohne dass <template> . Dies macht es jedoch auch unmöglich, slot-scope direkt auf der Komponente selbst zu verwenden, da dies zu Mehrdeutigkeiten führen würde, wie hier dargestellt .

  • Ich dachte darüber nach, slot-scope Modifikatoren oder spezielle Präfixe hinzuzufügen, damit wir sie direkt für eine Komponente verwenden können, um anzuzeigen, dass der Slot-Inhalt als Standard-Slot behandelt werden sollte, aber weder ein Modifikator noch ein Präfix wie $ scheint die richtige Wahl zu sein. Der Modifier by design sollte nur auf Direktiven angewendet werden, während eine neue spezielle Syntax für einen einzelnen Anwendungsfall nicht mit dem gesamten Syntaxdesign vereinbar ist.

  • Wir haben uns lange davor gescheut, weitere Direktiven hinzuzufügen, zum Teil wollen wir die Template-Syntax so stabil wie möglich halten, zum Teil wollen wir die Kerndirektiven auf ein Minimum beschränken und nur Dinge tun die Benutzer im Userland nicht einfach tun können. In diesem Fall ist jedoch die bereichsbezogene Slot-Nutzung wichtig genug, und ich denke, eine neue Richtlinie kann gerechtfertigt sein, um ihre Nutzung deutlich geräuschärmer zu machen.

Anliegen

  • Der von v-scope akzeptierte Ausdruck unterscheidet sich von den meisten anderen Direktiven: Er erwartet einen temporären Variablennamen (der auch eine Dekonstruktion sein kann), aber nicht ohne Vorrang: Er verhält sich genau wie der Alias-Teil von v-for . Also fällt v-scope konzeptionell in das gleiche Lager wie v-for als strukturelle Direktive, die temporäre Variablen für ihren inneren Geltungsbereich erzeugt.

  • Dies würde den Code des Benutzers beschädigen, wenn der Benutzer eine benutzerdefinierte Anweisung namens v-scope und für eine Komponente verwendet wird.

    • Da benutzerdefinierte Direktiven in v2 hauptsächlich auf direkte DOM-Manipulationen ausgerichtet sind, ist es relativ selten, dass benutzerdefinierte Direktiven für Komponenten verwendet werden, noch mehr für etwas, das zufällig v-scope , daher sollte die Auswirkung minimal sein.

    • Selbst wenn es tatsächlich passiert, ist es einfach, die Zolldirektive einfach umzubenennen.

discussion intend to implement

Hilfreichster Kommentar

Das Problem auflösen

Wenn wir versuchen zu synthetisieren, hört es sich so an, als ob wir eine Lösung wollen, die:

  • Reduzieren Sie die Boilerplate, die für den Zugriff auf Daten von Scoped-Slots erforderlich ist
  • Vermeiden Sie zu implizieren, dass Daten für den Standard-Slot in benannten Slots verfügbar sind
  • Minimieren Sie die Anzahl der neuen APIs, die wir einführen müssen
  • Erfinden Sie APIs nicht neu, wenn die Webkomponentenspezifikation bereits eine akzeptable Lösung hat
  • bleiben Sie explizit, um Verwirrung darüber zu vermeiden, woher die Daten stammen

Vielleicht habe ich eine Lösung, die all dies anspricht! 🤞 Mit $event für v-on haben wir einen Präzedenzfall für die Bereitstellung von Ausdrücken, die innerhalb einer impliziten Funktion mit einem benannten Argument ausgeführt werden. Die Leute scheinen diese Bequemlichkeit zu genießen und ich sehe nicht viel Verwirrung, die dadurch verursacht wird, also sollten wir vielleicht diesem Beispiel für bereichsbezogene Slots folgen.

Vorgeschlagene Lösung

Anstatt v-scope , könnten wir den Slot-Bereich als $slot verfügbar machen. Zum Beispiel:

<TodoList :todos="todos">
  {{ $slot.todo.text }}

  <template slot="metaBar">
    You have {{ $slot.totals.incomplete }} todos left.
  </template>
</TodoList>

Für den Kontext könnte die untergeordnete Vorlage so aussehen:

<ul>
  <li v-for="todo in todos">
    <slot :todo="todo" />
  </li>
</ul>

<div>
  <slot name="metaBar" v-bind="itemsStats" />
</div>

Wenn Slots verschachtelt sind, würde das $slot Objekt die Slot-Daten zusammenführen, wobei die innersten Slots Vorrang haben. Zum Beispiel bei:

<Outer>
  <Middle>
    <Inner>
      {{ $slot }}
    </Inner>
  </Middle>
</Outer>

Zusammenführen könnte so aussehen:

$slot = {
  ...outerSlotData,
  ...middleSlotData,
  ...innerSlotData
}

Sie machen sich vielleicht Sorgen oder fragen sich, wie Sie mit Namensraumüberschneidungen umgehen sollen, und in 99,9 % der Fälle glaube ich nicht, dass dies ein Problem sein wird. Zum Beispiel:

<UserData id="chrisvfritz">
  My email is {{ $slot.user.email }}, but you can reach Sarah at
  <UserData id="sdras">
    {{ $slot.user.email }}
  </UserData>.
</UserData>

Im obigen Beispiel hat $slot.user trotz der Verschachtelungsbereiche immer den richtigen Wert. Manchmal benötigen Sie jedoch wirklich gleichzeitig Zugriff auf beide Eigenschaften, wie in:

<UserData id="chrisvfritz">
  <template slot-scope="me">
    <UserData id="sdras">
      <template slot-scope="sarah">
        <CollaborationPageLink :user-ids="[me.user.id, sarah.user.id]">
          View your collaboration history
        </CollaborationPageLink>
      </template>
    </UserData>
  </template>
</UserData>

Für diese seltenen Grenzfälle könnten Benutzer slot-scope mit seinem aktuellen Verhalten immer noch als Fluchtluke verwenden . Aber v-scope wäre immer noch unnötig, denn wenn ich das richtig verstehe, wäre es der Zweck, die gängigsten Anwendungsfälle zu vereinfachen, was das $slot Objekt bereits erreicht hätte, ohne die gleichen Probleme zu verursachen.

Vorteile

  • Wenn ein Benutzer auf Daten zugreifen möchte, die von einem Slot bereitgestellt werden, ist die einzige Boilerplate $slot , die so kurz wie möglich ist, aber explizit bleibt. Es ist sofort offensichtlich, dass die Daten von einem Slot stammen, ohne dass Sie die Komponente durchsuchen müssen, um zu sehen, wo eine Eigenschaft definiert ist.

  • Um zu wissen, auf welche Daten sie zugreifen können, müssen die Benutzer nur darüber nachdenken, auf welchen Slot(s) die Inhalte gerendert werden . Ich denke, mit v-scope würde es viel Verwirrung geben, wenn die Leute annehmen, dass es wie v-for funktioniert, weil dies die einzige andere Direktive ist, die bereichsbezogene Kontexteigenschaften definiert.

  • Benutzer müssen nicht mit der Destrukturierung vertraut und vertraut sein, um bereichsbezogene Slots zu verwenden , wodurch die Lernkurve

  • In einigen Fällen, insbesondere bei vielen verschachtelten Slots, die alle einen Status bereitstellen, ist es nicht offensichtlich, _von welcher_ Komponente ein Status stammt, und Benutzer müssen erneut slot-scope . Dies mag nach einem Nachteil klingen, aber wenn ich verschachtelte Slots mit Geltungsbereich sehe, handelt es sich fast immer um das Zustandsanbietermuster, das meiner Meinung nach ein Anti-Muster ist. Hier ist meine Argumentation . Die Tatsache, dass viele verschachtelte Scoped-Slots mehr Boilerplate erfordern, könnte die Verwendung von Anti-Patterns tatsächlich verringern .

  • Die API-Oberfläche wird drastisch reduziert , da sich die meisten Vue-Entwickler nur $slot merken müssen, was nur ein Objekt ist.

  • Durch die Verwendung einer Eigenschaft $ Präfix $slot eine Sache von Vue ist.

  • Derzeit machen Bibliotheken oft etwas wie <slot v-bind="user" /> damit ihre Benutzer ein paar Zeichen mit slot-scope="user" anstelle von slot-scope="{ user } sparen können. Es wirkt auf den ersten Blick elegant, aber ich habe es als Anti-Muster erlebt. Das Problem tritt auf, wenn die Komponente Daten _andere als_ der Benutzer verfügbar machen möchte. Dann haben sie zwei Möglichkeiten: Nehmen Sie eine grundlegende Änderung an ihrer API vor oder erzwingen Sie diese neue Eigenschaft auf das user Objekt, auch wenn dies möglicherweise nur sehr wenig mit dem Benutzer zu tun hat. Beides ist keine gute Option. Glücklicherweise würde $slot die Versuchung beseitigen, Komponenten weniger zukunftssicher zu machen , denn obwohl $slot immer noch kürzer ist als $slot.user , verlieren Sie wichtigen Kontext, indem Sie den Benutzer als $slot .

Nachteile

  • In einigen verschachtelten Slots mit Gültigkeitsbereich gibt es einige Grenzfälle, in denen nicht offensichtlich ist, _aus welcher_ Komponente einige Daten stammen. In diesen Fällen können Benutzer jedoch immer noch nach slot-scope greifen, wenn sie maximale Deutlichkeit benötigen, daher denke ich, dass dies keine große Sache ist. Außerdem halte ich es, wie ich bereits erwähnt habe, für sehr wahrscheinlich, dass sich der Benutzer mit Komponenten des staatlichen Anbieters in den Fuß schießt, wenn er dieses Problem überhaupt hat.

Alle 36 Kommentare

Sieht gut aus. Ich habe darüber nachgedacht, das Argument zu verwenden, um einen Slot-Namen bereitzustellen, aber dies würde es ermöglichen, mehrere Direktiven gegen den Geltungsbereich für dieselbe Komponente zu verwenden und daher den für die Komponente bereitgestellten Inhalt wiederzuverwenden. Wobei ich mir nicht sicher bin, ob es sinnvoll ist oder zu Problemen führen könnte

Dies wird die API-Nutzung von renderlosen Komponenten verbessern, die mit der Zeit häufiger verwendet werden 🙌

Wenn ich das richtig verstehe, dient v-scope nur einem Anwendungsfall. Anstatt zu schreiben:

<foo>
  <template slot-scope="{ item }">{{ item.id }}</template>
</foo>

Wir können schreiben:

<foo v-scope="{ item }">{{ item.id }}</foo>

Es reduziert in diesem Fall ziemlich viel Lärm, aber es scheint der einzige Fall zu sein. Es fühlt sich wie eine Art Overkill an, eine neue Direktive einzuführen (die ganz anders funktioniert als andere Direktiven, sogar ab v-for weil sie nur auf Komponenten funktioniert und auf der darunter liegenden <slot> Logik beruht). Ein weiteres Problem besteht darin, dass, wenn ich in diesem Repository nach v-scope suche , die einzigen beiden Vorkommen darüber diskutieren, den Datenbereich eines Vorlagen-Unterbaums zu reduzieren (https://github.com/vuejs/vue/issues/5269 #issuecomment-288912328, https://github.com/vuejs/vue/issues/6913), wie with in JavaScript funktioniert.

Ich mag das. Bleibt mir noch die Frage, wie wir mit benannten Slots umgehen wollen. Wenn wir zulassen möchten, dass benannte Slots dieselbe Direktive verwenden, müssen wir dies auch für <template> zulassen:

<comp v-scope="scope">
  {{ scope.msg }} <!-- default slot, inheriting from the scope from above -->
  <template slot="someName" v-scope="otherScope">
    <p>{{ otherScope }}</p>
    <div> whatever</div>
  </template>
</comp>

Wir könnten diese Gelegenheit nutzen, um sot-scope zu verwerfen und in Vue 3 zu entfernen und durch v-scope ersetzen

Ich denke, wir sollten definitiv nicht mit zwei verschiedenen Konzepten ( v-scope Direktive und slot-scope Attribut) für dasselbe enden.

Randnotiz: Wenn man sich das jetzt anschaut, könnte man den Eindruck gewinnen, dass die Hierarchie der Elemente &
Anweisungen, dass sie auf otherScope und scope im benannten Slot zugreifen können. Könnte ein Nachteil sein.

@LinusBorg für den Namen, ein Argument könnte v-scope:slotName bewirken . Ich denke, der Sinn, dies nur für Komponenten zuzulassen, besteht darin, das zu ersetzen, was @Justineo gesagt hat (https://github.com/vuejs/vue/issues/9180#issuecomment-446168296)

Randnotiz: Wenn man sich das jetzt anschaut, könnte man den Eindruck gewinnen, dass die Hierarchie der Elemente &
Direktiven, dass sie auf otherScope und scope im benannten Slot zugreifen können. Könnte ein Nachteil sein.

Das geht, oder? 🤔 Ich bin mir ziemlich sicher, dass ich Slot-Scopes verschachtelt habe

Das geht, oder? 🤔 Ich bin mir ziemlich sicher, dass ich Slot-Scopes verschachtelt habe

Ich habe sie jedoch nicht verschachtelt, es sind Geschwister-Slots. Ich habe den Bereich des Standard-Slots mit v-scope für die Komponente definiert und den benannten Slot (der ein gleichgeordneter Slot ist) mit einem <template slot="someName">
Sehen? Es ist verwirrend ^^

@LinusBorg Ich denke, die Verwendung wäre verwirrend. Ich denke konzeptionell ist es:

  • Wenn v-scope direkt auf der Komponente verwendet wird, bedeutet dies, dass Sie nur den Standard-Slot verwenden.
  • Wenn Sie benannte Slots verwenden möchten... müssen Sie immer noch slot + slot-scope .

Ich stimme zu, dass sowohl v-scope als auch slot-scope inkonsistent/verwirrend sein können, insbesondere für neue Benutzer, die nicht wissen, wie wir zum aktuellen Design gekommen sind.

Das Problem auflösen

Wenn wir versuchen zu synthetisieren, hört es sich so an, als ob wir eine Lösung wollen, die:

  • Reduzieren Sie die Boilerplate, die für den Zugriff auf Daten von Scoped-Slots erforderlich ist
  • Vermeiden Sie zu implizieren, dass Daten für den Standard-Slot in benannten Slots verfügbar sind
  • Minimieren Sie die Anzahl der neuen APIs, die wir einführen müssen
  • Erfinden Sie APIs nicht neu, wenn die Webkomponentenspezifikation bereits eine akzeptable Lösung hat
  • bleiben Sie explizit, um Verwirrung darüber zu vermeiden, woher die Daten stammen

Vielleicht habe ich eine Lösung, die all dies anspricht! 🤞 Mit $event für v-on haben wir einen Präzedenzfall für die Bereitstellung von Ausdrücken, die innerhalb einer impliziten Funktion mit einem benannten Argument ausgeführt werden. Die Leute scheinen diese Bequemlichkeit zu genießen und ich sehe nicht viel Verwirrung, die dadurch verursacht wird, also sollten wir vielleicht diesem Beispiel für bereichsbezogene Slots folgen.

Vorgeschlagene Lösung

Anstatt v-scope , könnten wir den Slot-Bereich als $slot verfügbar machen. Zum Beispiel:

<TodoList :todos="todos">
  {{ $slot.todo.text }}

  <template slot="metaBar">
    You have {{ $slot.totals.incomplete }} todos left.
  </template>
</TodoList>

Für den Kontext könnte die untergeordnete Vorlage so aussehen:

<ul>
  <li v-for="todo in todos">
    <slot :todo="todo" />
  </li>
</ul>

<div>
  <slot name="metaBar" v-bind="itemsStats" />
</div>

Wenn Slots verschachtelt sind, würde das $slot Objekt die Slot-Daten zusammenführen, wobei die innersten Slots Vorrang haben. Zum Beispiel bei:

<Outer>
  <Middle>
    <Inner>
      {{ $slot }}
    </Inner>
  </Middle>
</Outer>

Zusammenführen könnte so aussehen:

$slot = {
  ...outerSlotData,
  ...middleSlotData,
  ...innerSlotData
}

Sie machen sich vielleicht Sorgen oder fragen sich, wie Sie mit Namensraumüberschneidungen umgehen sollen, und in 99,9 % der Fälle glaube ich nicht, dass dies ein Problem sein wird. Zum Beispiel:

<UserData id="chrisvfritz">
  My email is {{ $slot.user.email }}, but you can reach Sarah at
  <UserData id="sdras">
    {{ $slot.user.email }}
  </UserData>.
</UserData>

Im obigen Beispiel hat $slot.user trotz der Verschachtelungsbereiche immer den richtigen Wert. Manchmal benötigen Sie jedoch wirklich gleichzeitig Zugriff auf beide Eigenschaften, wie in:

<UserData id="chrisvfritz">
  <template slot-scope="me">
    <UserData id="sdras">
      <template slot-scope="sarah">
        <CollaborationPageLink :user-ids="[me.user.id, sarah.user.id]">
          View your collaboration history
        </CollaborationPageLink>
      </template>
    </UserData>
  </template>
</UserData>

Für diese seltenen Grenzfälle könnten Benutzer slot-scope mit seinem aktuellen Verhalten immer noch als Fluchtluke verwenden . Aber v-scope wäre immer noch unnötig, denn wenn ich das richtig verstehe, wäre es der Zweck, die gängigsten Anwendungsfälle zu vereinfachen, was das $slot Objekt bereits erreicht hätte, ohne die gleichen Probleme zu verursachen.

Vorteile

  • Wenn ein Benutzer auf Daten zugreifen möchte, die von einem Slot bereitgestellt werden, ist die einzige Boilerplate $slot , die so kurz wie möglich ist, aber explizit bleibt. Es ist sofort offensichtlich, dass die Daten von einem Slot stammen, ohne dass Sie die Komponente durchsuchen müssen, um zu sehen, wo eine Eigenschaft definiert ist.

  • Um zu wissen, auf welche Daten sie zugreifen können, müssen die Benutzer nur darüber nachdenken, auf welchen Slot(s) die Inhalte gerendert werden . Ich denke, mit v-scope würde es viel Verwirrung geben, wenn die Leute annehmen, dass es wie v-for funktioniert, weil dies die einzige andere Direktive ist, die bereichsbezogene Kontexteigenschaften definiert.

  • Benutzer müssen nicht mit der Destrukturierung vertraut und vertraut sein, um bereichsbezogene Slots zu verwenden , wodurch die Lernkurve

  • In einigen Fällen, insbesondere bei vielen verschachtelten Slots, die alle einen Status bereitstellen, ist es nicht offensichtlich, _von welcher_ Komponente ein Status stammt, und Benutzer müssen erneut slot-scope . Dies mag nach einem Nachteil klingen, aber wenn ich verschachtelte Slots mit Geltungsbereich sehe, handelt es sich fast immer um das Zustandsanbietermuster, das meiner Meinung nach ein Anti-Muster ist. Hier ist meine Argumentation . Die Tatsache, dass viele verschachtelte Scoped-Slots mehr Boilerplate erfordern, könnte die Verwendung von Anti-Patterns tatsächlich verringern .

  • Die API-Oberfläche wird drastisch reduziert , da sich die meisten Vue-Entwickler nur $slot merken müssen, was nur ein Objekt ist.

  • Durch die Verwendung einer Eigenschaft $ Präfix $slot eine Sache von Vue ist.

  • Derzeit machen Bibliotheken oft etwas wie <slot v-bind="user" /> damit ihre Benutzer ein paar Zeichen mit slot-scope="user" anstelle von slot-scope="{ user } sparen können. Es wirkt auf den ersten Blick elegant, aber ich habe es als Anti-Muster erlebt. Das Problem tritt auf, wenn die Komponente Daten _andere als_ der Benutzer verfügbar machen möchte. Dann haben sie zwei Möglichkeiten: Nehmen Sie eine grundlegende Änderung an ihrer API vor oder erzwingen Sie diese neue Eigenschaft auf das user Objekt, auch wenn dies möglicherweise nur sehr wenig mit dem Benutzer zu tun hat. Beides ist keine gute Option. Glücklicherweise würde $slot die Versuchung beseitigen, Komponenten weniger zukunftssicher zu machen , denn obwohl $slot immer noch kürzer ist als $slot.user , verlieren Sie wichtigen Kontext, indem Sie den Benutzer als $slot .

Nachteile

  • In einigen verschachtelten Slots mit Gültigkeitsbereich gibt es einige Grenzfälle, in denen nicht offensichtlich ist, _aus welcher_ Komponente einige Daten stammen. In diesen Fällen können Benutzer jedoch immer noch nach slot-scope greifen, wenn sie maximale Deutlichkeit benötigen, daher denke ich, dass dies keine große Sache ist. Außerdem halte ich es, wie ich bereits erwähnt habe, für sehr wahrscheinlich, dass sich der Benutzer mit Komponenten des staatlichen Anbieters in den Fuß schießt, wenn er dieses Problem überhaupt hat.

Ich denke, eine Eigenschaft mit $ -Präfix sollte innerhalb einer ganzen Vue-Instanz konsistent sein. Ich bin mir nicht sicher, ob es Verwirrung stiften wird, indem implizit ein $slot in jeden Slot eingefügt wird, und sie stehen tatsächlich für ein lokales Argument innerhalb dieser Slot-Bereiche. Es ist eine Art Konvention (nach meiner Beobachtung), dass $ -präfixierte Eigenschaften für etwas stehen, das jeder Vue-Instanz zur Verfügung steht, sodass sie überall verwendet werden können, einschließlich Lifecycle-Hooks, Methoden und Renderfunktionen. Das Hinzufügen dieses speziellen $slot wird diese Konvention brechen.

@chrisvfritz Dies ist ein interessanter Vorschlag, der jedoch davon abhängt, dass normale Slots und Scoped-Slots vereinheitlicht werden (also vielleicht etwas, das in v3 in Betracht gezogen werden kann). Das größte Problem dabei ist, ob der Slot-Inhalt in $slots oder $scopedSlots der untergeordneten Komponente verfügbar sein sollte? Der einzige Hinweis ist das Vorhandensein von $slot irgendwo in Ausdrücken (kann sich überall im Baum befinden), was nicht so eindeutig ist wie slot-scope (das sich nur an der Slot-Root befinden kann). Obwohl wir dies technisch im Compiler erkennen können, wird der Slot für den Benutzer von $slots zu $scopedSlots verschoben, wenn der Benutzer anfängt, $slot in der Vorlage zu verwenden...

Wird dies große Auswirkungen auf JSX-Benutzer haben?

@donnysim nein, dies hat keine Auswirkungen auf JSX.

Ich denke, eine Eigenschaft mit $ -Präfix sollte innerhalb einer ganzen Vue-Instanz konsistent sein. Ich bin mir nicht sicher, ob es Verwirrung stiften wird, indem implizit ein $slot in jeden Slot eingefügt wird, und sie stehen tatsächlich für ein lokales Argument innerhalb dieser Slot-Bereiche.

@Justineo Ich hatte zuerst den gleichen Gedanken, aber wir tun dies für $event und niemand scheint sich zu beschweren, also gibt es bereits einen Präzedenzfall und wir haben Beweise dafür, dass Benutzer normalerweise nicht verwirrt sind und die Bequemlichkeit wirklich genießen.

Das größte Problem dabei ist, ob der Slot-Inhalt in $slots oder $scopedSlots der untergeordneten Komponente verfügbar sein sollte.

@yyx990803 Tolle Frage! Ich habe eine Idee, die funktionieren könnte. Was ist, wenn wir in Fällen, in denen es mehrdeutig ist (verschachtelte Slots mit $slot ), nur alle Slots zu einem Scoped-Slot kompilieren, aber auch alle $scopedSlots unter $slots als Getter verfügbar machen? Zum Beispiel so etwas wie:

for (const slotName in vm.$scopedSlots) {
  // Don't override existing slots of the same name,
  // since that's _technically_ possible right now.
  if (vm.$slots[slotName]) continue

  Object.defineProperty(vm.$slots, slotName, {
    get: vm.$scopedSlots[slotName],
    enumerable: true,
    configurable: true,
    writable: true
  })
}

Auf diese Weise im Fall von:

<A>
  <B>
    {{ $slot.foo }}
  </B>
</A>

Wir könnten zu etwas kompilieren:

// Assume new render helper:
// _r = mergeSlotData

_c('A', {
  scopedSlots: _u([
    {
      key: 'default',
      fn: function(_sd) {
        var $slot = _r($slot, _sd)
        return [
          _c('B', {
            scopedSlots: _u([
              {
                key: 'default',
                fn: function(_sd) {
                  var $slot = _r($slot, _sd)
                  return [_v(_s($slot.foo))]
                },
              },
            ]),
          }),
        ]
      },
    },
  ]),
}) 

Und es spielt keine Rolle, ob foo von <A> oder <B> , da innerhalb dieser Komponenten sowohl this.$slots.default als auch this.$scopedSlots.default(someData) funktionieren.

Ein paar Vorbehalte:

  • In Fällen mit verschachtelten Slots, von denen nur einige tatsächlich bereichsbezogen sind, wären die kompilierten Renderfunktionen aus Vorlagen mit $slot etwas größer als bei Verwendung von slot-scope . Allerdings nicht sehr signifikant, also finde ich es in Ordnung.

  • Mir fällt kein wirkliches Beispiel ein, aber es kann Randfälle geben, in denen ein Benutzer über $slots / $scopedSlots iteriert und neue oder doppelte Eigenschaften zu unerwartetem Verhalten führen können. Ich habe bereits dynamisch mit den Namen $slots und $scopedSlots iteriert, um einige interessante Muster zu aktivieren, aber diese Änderung würde keinen der Anwendungsfälle beeinflussen, auf die ich gestoßen bin.

Die Gedanken?

wir machen das für $event und niemand scheint sich zu beschweren

@chrisvfritz Oh, ich habe das $event Ding verpasst. Obwohl es eindeutig eine Ausnahme von der Konvention ist (ich glaube, es ist 😅), ist $event nur in einem sehr begrenzten Bereich verfügbar (nur innerhalb der v-bind-Attributliterale und keine Verschachtelung).

Und um $scopedSlots auf $slots zu vervollständigen:

Früher habe ich geglaubt, Slots und Scoped Slots sind unterschiedliche Konzepte und Vue hat eine separate Verwendung für sie, was bedeutet, dass ich sowohl einen Slot als auch einen Scoped Slot haben kann, die denselben Namen haben, aber unterschiedliche Zwecke erfüllen. Später stellte ich jedoch fest, dass Vue tatsächlich auf einen Slot mit demselben Namen zurückgreift, wenn der angegebene Scoped-Slot nicht verfügbar ist. Für mich bedeutet dies, dass wir sie als dasselbe betrachten sollten, aber da wir auf jeder Instanz sowohl $slots als auch $scopedSlots Verfügung haben, können wir sie immer flexibler/unerwarteter in Renderfunktionen verwenden , wie die Verwendung eines Standard-Slots zum Überschreiben aller Listenelemente und eines standardmäßigen Bereichs-Slots zum Überschreiben eines einzelnen Elements. Dies wird eigentlich nicht empfohlen (wir haben die empfohlene Verwendung dafür in unseren Dokumenten und dem Styleguide AFAIK nicht vorgeschlagen), da wir sie wahrscheinlich in 3.0 zu einem einzigen Slot-Konzept zusammenführen, aber dies in 2.x wird brechen einige nutzung erlaubt seit ziemlich langer zeit zurück.

Wir werden sie wahrscheinlich in 3.0 zu einem einzigen Slot-Konzept zusammenführen, aber dies in 2.x wird einige der seit langer Zeit erlaubten Nutzungen unterbrechen.

@Justineo Ich schlage nicht vor, sie bereits in 2.x zusammenzuführen - sondern nur das aktuelle Fallback-Verhalten zu erweitern. Der von Ihnen beschriebene Anwendungsfall, bei dem sowohl $slots als auch $scopedSlots denselben Namespace verwenden, wäre dennoch möglich, da das Verhalten von slot-scope unverändert bleiben würde. Zum Beispiel:

<div>
  Default slot
  <template slot-scope="foo">
    Default scoped slot {{ foo }}
  </template>
</div>

würde immer noch genauso funktionieren. Ist das sinnvoll?

@chrisvfritz

Ich habe nicht darüber gesprochen, sie in 2.x zusammenzuführen. Ich sagte, wenn du das tust:

alle $scopedSlots unter $slots als Getter verfügbar machen

Sie können $slots.foo von $scopedSlots.foo und sie separat in Renderfunktionen verwenden.

Sie können $slots.foo nicht mehr von $scopedSlots.foo unterscheiden und sie separat in Renderfunktionen verwenden.

Ehrlich gesagt würde ich mich freuen, wenn alles (Slots und scopedSlots) in scopedSlots als Funktionen zugänglich wäre, es bedeutet nur keine nutzlosen Überprüfungen mehr, welcher Slot gerendert werden soll. Es macht aus Komponentensicht wirklich keinen Unterschied, welche Art von Steckplatz der Entwickler verwendet. Kommt von einem gemischten Benutzer mit JSX- und Vorlagensyntax.

@donnysim Ja, ich stimme dir zu, dass sie zusammengeführt werden sollten. Ich habe in https://github.com/vuejs/vue/issues/9180#issuecomment -447185512 erklärt, warum ich solche Bedenken habe. Es geht um Abwärtskompatibilität.

Sie können $slots.foo von $scopedSlots.foo und sie separat in Renderfunktionen verwenden.

@Justineo Du _kannst_ eigentlich, solange wir zuerst $slots und keine bereits definierten Slots ersetzen. Siehe die Beispielimplementierung, die ich oben gepostet habe:

for (const slotName in vm.$scopedSlots) {
  // Don't override existing slots of the same name,
  // since that's _technically_ possible right now.
  if (vm.$slots[slotName]) continue

  Object.defineProperty(vm.$slots, slotName, {
    get: vm.$scopedSlots[slotName],
    enumerable: true,
    configurable: true,
    writable: true
  })
}

@chrisvfritz

Betrachten Sie das folgende Beispiel:

render (h) {
  if (!this.$slots.default) {
    return h(
      'div',
      this.items.map(item => this.$scopedSlots.default(item))
    )
  }
  return h('div', this.$slots.default)
}

Wenn der Komponentenbenutzer nur vm.$scopedSlots.default , sollte es this.items durchlaufen und item s in den bereichsbezogenen Slot rendern. Aber jetzt, da vm.$scopedSlot.default jetzt der Scoped-Slot ist und existiert, wird der Scoped-Slot anstelle des Slots aufgerufen.

@Justineo Ich denke, dieser Anwendungsfall wäre sehr selten, aber das Muster wäre immer noch durch Ändern möglich:

if (!this.$slots.default) {

zu:

if (this.$scopedSlots.default) {

oder, wenn der Benutzer manchmal einen bereichsbezogenen Slot bereitstellt, der aus irgendeinem Grund ignoriert werden soll:

if (!Object.getOwnPropertyDescriptor(this.$slots, 'default').value) {

Ich würde es trotzdem nicht als Breaking Change bezeichnen. Erstens haben wir die Verwendung von Slots und Scoped-Slots mit demselben Namen nie dokumentiert/empfohlen, daher war dies nie Teil des öffentlichen Auftrags. Zweitens fallen, wie Sie bereits erwähnt haben, bereichsbezogene Slots in Vorlagen bereits auf einen gleichnamigen Slot ohne Bereich zurück, was als guter historischer Beweis dafür dient, dass wir nie beabsichtigt haben, die bereichsbezogene/nicht bereichsbezogene Grenze so zu verwenden.

Ist das sinnvoll? Haben Sie auch echte Anwendungsfälle für die Wiederverwendung von Slot-Namen gesehen? Oder fällt dir was ein? Ich kann nicht, aber wenn es nützliche und einzigartige Muster gibt, die es ermöglicht, wäre es gut, sie jetzt kennenzulernen, da dies auch unsere Entscheidung beeinflussen würde, sie in Vue 3 zusammenzuführen.

Ich denke, bereichsbezogene Slots und Slots mit demselben Namen für unterschiedliche Zwecke sind
eine schlechte Idee. Ich habe dies in der versprochenen Pre-Version von Vue verwendet und es entfernt
weil es verwirrend war. Und jetzt muss ich nach dem Standard-Slot suchen
und Scoped-Slot, damit der Entwickler einen Slot bereitstellen kann, ohne die Daten zu verbrauchen.
Und wenn ich mich richtig erinnere, können Sie bei der Verwendung nicht denselben Namen haben
Vorlagen.

@chrisvfritz

Ich habe nicht gesagt, dass das Muster hilfreich ist und unterstützt werden sollte. Es kann in der Tat Verwirrung stiften. Ich sagte nur, es ist möglich , bestehenden Code zu brechen. Wir haben nie dokumentiert, dass Slots und Scoped-Slots denselben Namen verwenden, aber wir haben den Benutzern auch nie empfohlen, dies nicht zu tun. Und die Fallback-Logik in Vorlagen ist nicht dokumentiert. Es ist vielleicht nicht beabsichtigt, aber zumindest habe ich selbst geglaubt, dass es eine legitime Verwendung ist, bis ich die Fallback-Logik in Vorlagen gelernt habe ...

@posva Danke, dass du deine Erfahrung

@Justineo Glauben Sie, dass dieser

Dies wurde nach "Todo" verschoben - Haben wir einen Konsens darüber, was implementiert werden soll?

Darüber habe ich letzte Woche nachgedacht. Ich mag den Variablenvorschlag von @chrisvfritz $slot und habe versucht, die Implementierungsblocker auszuarbeiten. Folgendes halte ich für machbar:

  • Alle Slots sind als Funktionen kompiliert (wie in 3.0), also keine statischen Slots mehr (dies führt zu einer genaueren Nachverfolgung von Komponentenaktualisierungen)
  • $scopedSlots jeden Slot als Funktion verfügbar
  • $slots jeden Slot als Ergebnis des Aufrufs der entsprechenden Funktion mit einem leeren Objekt verfügbar.

Folgendes wäre also äquivalent:

this.$slots.default
// would be the same as
this.$scopedSlots.default({} /* $slot */)

Mit den oben genannten internen Änderungen sind $slots und $scopedSlots im Wesentlichen vereinheitlicht, sollten aber abwärtskompatibel sein! Es sollte auch zu einer einfacheren Migration auf 3.0 führen.

Einige Verwendungsbeispiele, die die vorgeschlagene Variable $slot :

<!-- list and item composition -->
<fancy-list>
  <fancy-item :item="$slot.item"/>
</fancy-list>
<!-- hypothetical data fetching component -->
<fetch :url="`/api/posts/${id}`">
  <div slot="success">{{ $slot.data }}</div>
  <div slot="error">{{ $slot.error }}</div>
  <div slot="pending">pending...</div>
</fetch>

Ich glaube nicht, dass das Zusammenführen verschachtelter Bereiche eine gute Idee ist. Ich denke, es ist besser, explizit zu sein, wenn es um die Verschachtelung geht (dh verwenden Sie immer slot-scope wenn Sie verschachtelte Slots mit Gültigkeitsbereich haben):

<!-- nested usage -->
<foo>
  <bar slot-scope="foo">
    <baz slot-scope="bar">
      {{ foo }} {{ bar }} {{ $slot }}
    </baz>
  </bar>
</foo>

Auf welchen Slot bezieht sich in Ihrem letzten Beispiel $slot ?

@Akryum es ist { ...foo, ...bar } .

@Akryum @Justineo hmm Ich kann sagen, das könnte verwirrend sein ... wir haben slot-scope für das Root-Element eines Slots nutzbar gemacht, dh hier ist foo der Slot-Bereich, der von <foo/> bereitgestellt wird bar wird von <bar/> bereitgestellt und $slot wird von <baz/> bereitgestellt die intuitivere Verwendung wäre in diesem Fall etwa so:

<!-- nested usage -->
<foo slot-scope="foo">
  <bar slot-scope="bar">
    <baz slot-scope="baz">
      {{ foo }} {{ bar }} {{ baz }}
    </baz>
  </bar>
</foo>

das könnte verwirrend sein

intuitivere Bedienung wäre in diesem Fall etwa so

Absolute Zustimmung. Außerdem können wir die Destrukturierung weiterhin verwenden.
Zum Beispiel:
```vue

<FieldValidation field="login" slot-scope="{ value, setValue, error }">
  <LoginInput
    :value="value"
    :error="error"
    @input="setValue"
  />
</FieldValidation>

<FieldValidation field="password" slot-scope="{ value, setValue, error }">
  <PasswordInput
    :value="value"
    :error="error"
    @input="setValue"
  />
</FieldValidation>

Geschlossen über 7988a554 und 5d52262f

Zusammenfassung:

  • Unterstützt die Variable $slot in allen Slots. (Das Vorhandensein von $slot bewirkt, dass der Slot als Scoped-Slot kompiliert wird).

  • Alle Slots, einschließlich der normalen Slots, sind jetzt auf this.$scopedSlots als Funktionen verfügbar. Diese Mittel unter der Annahme this.$slots.default vorhanden ist , this.$scopedSlots.default() wird seinen Wert zurück. Dadurch können Renderfunktionsbenutzer immer this.$scopedSlot und sich keine Gedanken mehr darüber machen, ob der übergebene Slot bereichsbezogen ist oder nicht. Dies stimmt auch mit 3.0 überein, wo alle Slots als Funktionen verfügbar gemacht werden (aber stattdessen auf this.$slots ).

  • Keine Änderung der Verwendung von slot-scope , keine Einführung einer neuen Direktive. Wir möchten die Syntaxänderungen minimal machen und potenzielle Brüche bei 3.0 belassen.

@yyx990803 Führt $slot alle äußeren Slot-Bereiche zusammen oder bezieht es sich nur auf den nächstgelegenen Slot?

@Justineo keine Verschmelzung, nur die nächste.

Wenn man sich die bereichsbezogene Slot-Nutzung in den Apps ansieht, auf die ich Zugriff habe, scheint die Kurzschrift $slot in etwas mehr als der Hälfte der Fälle ohne das Zusammenführungsverhalten verschachtelter Slots nicht wirklich verwendbar zu sein. Das Problem ist, dass Basiskomponenten normalerweise anstelle von rohen HTML-Elementen verwendet werden und diese Komponenten oft ihre eigenen Slots haben. Zum Beispiel:

<MapMarkers :markers="cities">
  <BaseIcon name="map-marker">
    {{ $slot.marker.name }}
  </BaseIcon>
</MapMarkers>

Im obigen Beispiel gibt es nur einen einzelnen bereichsbezogenen Slot für die <MapMarkers> Komponente. <BaseIcon> akzeptiert einen Slot ohne Gültigkeitsbereich, aber da er überhaupt einen Slot akzeptiert, wäre $slot ohne das Zusammenführungsverhalten undefiniert oder ein leeres Objekt, was eine Refactoring zu dem klobigeren erzwingt:

<MapMarkers :markers="cities">
  <template slot-scope="{ marker }">
    <BaseIcon name="map-marker">
      {{ marker.name }}
    </BaseIcon>
  </template>
</MapMarkers>

Hier also meine Bedenken:

  • Zumindest aus meiner eigenen Stichprobe scheint es, dass _der größte Teil des Vorteils von $slot könnte, wenn er sich nur auf den innersten Slot bezieht. Tatsächlich kann es sogar mehr schaden als nützen, da es mehr APIs gibt, die die Leute lernen können.

  • UI-Bibliotheken könnten beginnen, Requisiten mit v-html übermäßig zu verwenden, wo Slots besser geeignet wären, als Reaktion auf Benutzerbeschwerden, dass $slot .

  • Benutzer könnten in einigen Fällen anfangen, Basiskomponenten zu vermeiden, um die bereichsbezogene Slot-Nutzung eleganter zu halten, was zu weniger wartbarem Code führt.

@yyx990803 Ist Ihr Hauptanliegen $slot Verschmelzung von slot-scope dafür existieren.

Als Kompromiss gibt es jedoch eine mögliche Alternative: Was wäre, wenn wir $slot um verschachtelte Scoped-Slots zusammenzuführen, sondern auf den nächstgelegenen Slot verweisen _der einen Scope bereitstellte_? Das könnte Fälle mit der größten Mehrdeutigkeit beseitigen und gleichzeitig meine Bedenken ausräumen. Die Gedanken?

Der Hauptgrund für das Vermeiden von Zusammenführungen ist die Nicht-Explizitheit: Allein durch das Lesen der Vorlage wissen Sie nicht wirklich, welche $slot Eigenschaft von welcher Anbieterkomponente stammt. Dies erfordert, dass Sie die Implementierungsdetails jeder Komponente, die Sie betrachten, genau kennen, um sicher zu sein, was vor sich geht, und das erhöht die mentale Belastung. Es mag in dem Moment, in dem Sie es schreiben, in Ordnung klingen, da Sie zu diesem Zeitpunkt den vollständigen Kontext im Kopf haben, aber es macht den Code für einen zukünftigen Leser (egal ob Sie oder ein anderes Teammitglied) viel schwieriger zu verstehen.

$slot bezieht sich auf den nächstgelegenen Slot, der einen Bereich bereitstellte

Ich glaube nicht, dass das funktionieren würde. Es führt zum gleichen Problem: Sie müssten die Implementierungsdetails der Komponenten kennen, um sicher zu sein, was vor sich geht. Ich würde sagen, es ist sogar noch impliziter und potenziell verwirrender als das Zusammenführen.


Langfristig denke ich, dass die beste Lösung darin besteht, die Semantik von slot-scope so zu ändern, dass ein Aliasing/Destrukturierung von $slot . Dein Beispiel würde also so aussehen:

<MapMarkers :markers="cities" slot-scope="{ marker }">
  <BaseIcon name="map-marker">
    {{ marker.name }}
  </BaseIcon>
</MapMarkers>

Dadurch ist keine Zusammenführung erforderlich, es wird explizit angegeben, welche Variablen von welchem ​​Anbieter stammen, und ist nicht übermäßig ausführlich. (Ich denke, das werden wir wahrscheinlich in 3.0 tun)

Der unangenehme Punkt, an dem wir uns gerade befinden, ist, dass wir die Verwendung von slot-scope für Nicht-Vorlagen zugelassen haben, und das hindert uns nun daran, es für die Komponente selbst zu verwenden.

Eine Änderung der Semantik in 3.0 könnte auch zu viel Verwirrung und Migrationsschmerz führen.

Vielleicht können wir das while-Problem umgehen, indem wir eine neue Eigenschaft, slot-alias , einführen, die genau das tut, was sie sagt - $slot mit einem Aliasnamen versehen. Im Gegensatz zu slot-scope kann es nur auf der Komponente selbst oder einem Template-Slot-Container verwendet werden (dies bedeutet, dass es mit slot-scope genauso funktionieren würde, wenn es auf einem <template> ). :

Verschachtelte Standard-Slots:

<MapMarkers :markers="cities" slot-alias="{ marker }">
  <BaseIcon name="map-marker">
    {{ marker.name }}
  </BaseIcon>
</MapMarkers>
<foo slot-alias="foo">
  <bar slot-alias="bar">
    <baz slot-alias="baz">
      {{ foo }} {{ bar }} {{ baz }}
    </baz>
  </bar>
</foo>

Benannte Slots ohne Verschachtelung:

Der einzige Fall, in dem es unweigerlich ausführlich wird, sind Slots mit Verschachtelung:

<foo>
  <template slot="a" slot-alias="a">
     <bar slot-alias="b">
        {{ a }} {{ b }}
     </bar>
  </template>
</foo>

(Dies kann in der Tat weniger ausführlich sein, wenn Sie sowohl slot-scope slot-alias , aber ich denke, das kann ziemlich verwirrend sein. Ich bin dafür, slot-scope insgesamt abzulehnen.)

<foo>
   <bar slot="a" slot-scope="a" slot-scope="b">
      {{ a }} {{ b }}
   </bar>
</foo>

Schließlich können wir ihm sogar eine Abkürzung geben, () (da Render-Requisiten in JSX normalerweise Pfeilfunktionen sind, die mit () ):

<MapMarkers :markers="cities" ()="{ marker }">
  <BaseIcon name="map-marker">
    {{ marker.name }}
  </BaseIcon>
</MapMarkers>
<foo ()="foo">
  <bar ()="bar">
    <baz ()="baz">
      {{ foo }} {{ bar }} {{ baz }}
    </baz>
  </bar>
</foo>

Vergleichen Sie das Obige mit dem entsprechenden JSX:

<foo>
  {foo => (
    <bar>
      {bar => (
        <baz>
          {baz => `${foo} ${bar} ${baz}`}
        </baz>
      )}
    </bar>
  )}
</foo>

Das Schließen, da das Design jetzt ganz anders ist als ursprünglich vorgeschlagen. Eröffne stattdessen einen neuen Thread.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

aviggngyv picture aviggngyv  ·  3Kommentare

6pm picture 6pm  ·  3Kommentare

franciscolourenco picture franciscolourenco  ·  3Kommentare

robertleeplummerjr picture robertleeplummerjr  ·  3Kommentare

bdedardel picture bdedardel  ·  3Kommentare