Vue: [Ditinggalkan] RFC: Sederhanakan penggunaan slot terbatas

Dibuat pada 11 Des 2018  ·  36Komentar  ·  Sumber: vuejs/vue

Ini adalah tindak lanjut dari https://github.com/vuejs/vue/issues/7740#issuecomment -371309357

Rasional

Masalah dengan penggunaan slot cakupan saat ini:

  • Verbose jika menggunakan <template slot-scope>
  • Terbatas untuk satu elemen/komponen menggunakan slot-scope langsung pada elemen slot.

Usul

Perkenalkan direktif v-scope , yang hanya dapat digunakan pada komponen:

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

Ini akan bekerja sama dengan slot-scope untuk slot cakupan default untuk <comp> (dengan <comp> memberikan nilai cakupan). Jadi ini juga berfungsi dengan mendekonstruksi:

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

Mengapa Arahan Baru

Saya yakin tim membahas secara singkat apa yang saya usulkan di https://github.com/vuejs/vue/issues/7740#issuecomment -371309357 di Slack beberapa waktu lalu, tetapi saya tidak dapat lagi menemukan catatan obrolan. Inilah alasan di balik arahan baru:

  • slot-scope diperkenalkan sebagai atribut khusus alih-alih arahan (atribut yang dimulai dengan awalan v- ) karena slot adalah atribut, dan kami ingin menjaga atribut terkait slot tetap konsisten . slot pada gilirannya diperkenalkan sebagai atribut non-direktif karena kami ingin penggunaannya mencerminkan penggunaan slot yang sebenarnya dalam standar Shadow DOM. Kami pikir akan lebih baik untuk menghindari paralel kita sendiri v-slot ketika ada sesuatu yang secara konseptual sama dalam standar.

  • Awalnya, slot-scope dirancang untuk hanya dapat digunakan pada elemen <template> yang bertindak sebagai wadah abstrak. Tapi itu bertele-tele - jadi kami memperkenalkan kemampuan untuk menggunakannya secara langsung pada elemen slot tanpa pembungkus <template> . Namun, ini juga membuat tidak mungkin mengizinkan penggunaan slot-scope secara langsung pada komponen itu sendiri, karena akan menyebabkan ambiguitas seperti yang diilustrasikan di sini .

  • Saya berpikir untuk menambahkan pengubah atau awalan khusus ke slot-scope sehingga kita dapat menggunakannya pada komponen secara langsung untuk menunjukkan konten slotnya harus diperlakukan sebagai slot cakupan default, tetapi pengubah atau awalan seperti $ tampaknya cocok. Pengubah menurut desain hanya boleh diterapkan pada arahan, sementara sintaks khusus baru untuk kasus penggunaan tunggal tidak konsisten dengan keseluruhan desain sintaks.

  • Untuk waktu yang sangat lama kami menghindari menambahkan lebih banyak arahan, sebagian dari sintaksis templat adalah sesuatu yang ingin kami pertahankan sestabil mungkin, sebagian karena kami ingin menjaga arahan inti seminimal mungkin dan hanya melakukan hal-hal bahwa pengguna tidak dapat dengan mudah melakukannya di userland. Namun, dalam hal ini penggunaan slot yang dicakup cukup penting, dan saya pikir arahan baru dapat dibenarkan untuk membuat penggunaannya secara signifikan lebih sedikit bising.

Kekhawatiran

  • Ekspresi yang diterima oleh v-scope berbeda dari kebanyakan direktif lainnya: ia mengharapkan nama variabel sementara (yang juga dapat berupa dekonstruksi), tetapi bukan tanpa prioritas: ia bertindak seperti bagian alias dari v-for . Jadi secara konseptual v-scope termasuk dalam kelompok yang sama dengan v-for sebagai arahan struktural yang membuat variabel sementara untuk lingkup dalamnya.

  • Ini akan merusak kode pengguna jika pengguna memiliki arahan khusus bernama v-scope dan digunakan pada komponen.

    • Karena arahan khusus di v2 terutama difokuskan pada manipulasi DOM langsung, relatif jarang melihat arahan khusus digunakan pada komponen, terlebih lagi untuk sesuatu yang disebut v-scope , jadi dampaknya harus minimal.

    • Bahkan jika itu benar-benar terjadi, mudah untuk menangani hanya dengan mengganti nama direktif kustom.

discussion intend to implement

Komentar yang paling membantu

Memecahkan masalah

Mencoba mensintesis, sepertinya kami menginginkan solusi yang akan:

  • kurangi boilerplate yang diperlukan untuk mengakses data dari slot tercakup
  • hindari menyiratkan bahwa data untuk slot default tersedia di slot bernama
  • meminimalkan jumlah API baru yang harus kami perkenalkan
  • jangan menemukan kembali API di mana spesifikasi komponen web sudah memiliki solusi yang dapat diterima
  • tetap eksplisit, untuk menghindari kebingungan tentang dari mana data berasal

Saya mungkin punya solusi yang mengatasi semua ini! Dengan $event untuk v-on , kita memiliki preseden untuk menyediakan ekspresi yang dijalankan di dalam fungsi implisit dengan argumen bernama. Orang-orang tampaknya menikmati kenyamanan itu dan saya tidak melihat banyak kebingungan yang disebabkan olehnya, jadi mungkin kita harus mengikuti contoh itu untuk slot terbatas.

Solusi yang diusulkan

Alih-alih menggunakan v-scope , kita dapat membuat ruang lingkup slot tersedia sebagai $slot . Sebagai contoh:

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

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

Untuk konteks, templat anak mungkin terlihat seperti:

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

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

Saat slot disarangkan, objek $slot akan menggabungkan data slot, dengan slot paling dalam mengambil prioritas override. Misalnya, di:

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

penggabungan mungkin terlihat seperti ini:

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

Anda mungkin khawatir/bertanya-tanya tentang bagaimana menangani namespace tumpang tindih dan dalam 99,9% kasus, saya benar-benar tidak berpikir itu akan menjadi masalah. Sebagai contoh:

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

Dalam contoh di atas, $slot.user akan selalu memiliki nilai yang benar, terlepas dari cakupan bersarang. Namun, terkadang Anda benar-benar membutuhkan akses ke kedua properti secara bersamaan, seperti di:

<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>

Untuk kasus tepi yang jarang ini, pengguna masih dapat menggunakan slot-scope dengan perilakunya saat ini sebagai pintu keluar . Tetapi v-scope tetap tidak diperlukan, karena jika saya memahaminya dengan benar, tujuannya adalah untuk menyederhanakan kasus penggunaan yang paling umum, yang objek $slot akan telah diselesaikan tanpa menyebabkan masalah yang sama.

Keuntungan

  • Saat pengguna ingin mengakses data yang disediakan oleh slot, satu-satunya boilerplate adalah $slot , yang sesingkat mungkin namun tetap eksplisit. Sangat jelas bahwa data berasal dari slot, tanpa perlu menelusuri komponen untuk melihat di mana properti didefinisikan.

  • Untuk mengetahui data mana yang dapat mereka akses, pengguna hanya perlu memikirkan slot mana konten akan dirender . Saya pikir dengan v-scope , akan ada banyak kebingungan dengan orang-orang yang menganggapnya berfungsi seperti v-for , karena itulah satu-satunya arahan lain yang mendefinisikan properti konteks cakupan.

  • Pengguna tidak harus terbiasa dan nyaman dengan destrukturisasi untuk menggunakan slot tercakup , sehingga mengurangi kurva pembelajaran.

  • Dalam beberapa kasus, terutama dengan banyak slot bersarang yang semuanya menyediakan status, tidak akan jelas komponen _yang_ mana beberapa status berasal dan pengguna harus mencapai slot-scope lagi. Ini mungkin terdengar seperti kerugian, tetapi ketika saya melihat slot cakupan bersarang, hampir selalu untuk pola penyedia negara bagian, yang menurut saya sangat anti-pola. Berikut alasan saya . Jadi fakta bahwa banyak slot cakupan bersarang akan membutuhkan lebih banyak boilerplate sebenarnya dapat mengurangi penggunaan anti-pola .

  • Area permukaan API berkurang drastis , karena sebagian besar pengembang Vue hanya perlu mengingat $slot , yang hanya sebuah objek.

  • Dengan menggunakan properti $ -prefixed, kami membangun API slot komponen web dengan cara yang memperjelas bahwa $slot adalah hal Vue.

  • Saat ini, perpustakaan sering melakukan sesuatu seperti <slot v-bind="user" /> sehingga penggunanya dapat menyimpan beberapa karakter dengan slot-scope="user" alih-alih slot-scope="{ user } . Tampaknya elegan pada pandangan pertama, tetapi saya telah mengalaminya sebagai anti-pola. Masalah muncul ketika komponen ingin mengekspos data _selain_ pengguna. Kemudian mereka memiliki dua pilihan: membuat perubahan mendadak pada API mereka atau memaksa properti baru ini ke objek user , meskipun mungkin tidak ada hubungannya dengan pengguna. Keduanya bukan pilihan yang bagus. Untungnya, $slot akan menghilangkan godaan untuk membuat komponen kurang tahan masa depan , karena meskipun $slot masih lebih pendek dari $slot.user , Anda kehilangan konteks penting dengan menyebut pengguna sebagai $slot .

Kekurangan

  • Di beberapa slot cakupan bersarang, ada beberapa kasus tepi di mana tidak akan jelas komponen _yang_ mana beberapa data berasal. Namun dalam kasus ini, pengguna masih dapat meraih slot-scope ketika mereka membutuhkan kejelasan maksimum, jadi saya rasa itu bukan masalah besar. Plus, seperti yang saya sebutkan sebelumnya, saya pikir sangat mungkin pengguna menembak diri mereka sendiri dengan komponen penyedia negara jika mereka memiliki masalah ini sejak awal.

Semua 36 komentar

Kelihatan bagus. Saya sedang berpikir untuk menggunakan argumen untuk memberikan nama slot tetapi ini akan memungkinkan beberapa arahan v lingkup untuk digunakan pada komponen yang sama dan oleh karena itu menggunakan kembali konten yang disediakan untuk komponen. Yang saya tidak yakin apakah itu berguna atau dapat menyebabkan masalah

Overral, ini akan meningkatkan penggunaan api dari komponen tanpa render, yang semakin banyak digunakan seiring waktu

Jika saya mengerti dengan benar, v-scope hanya melayani satu kasus penggunaan. Alih-alih menulis:

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

Kita dapat menulis:

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

Itu mengurangi cukup banyak kebisingan dalam kasus ini tetapi tampaknya menjadi satu-satunya kasus. Rasanya seperti berlebihan untuk memperkenalkan arahan baru (yang bekerja sangat berbeda dari arahan lain, bahkan dari v-for karena hanya bekerja pada komponen dan bergantung pada logika <slot> bawahnya). Kekhawatiran lain adalah ketika saya mencari v-scope dalam repo ini , hanya dua kejadian yang membahas tentang pengurangan cakupan data subpohon templat ( https://github.com/vuejs/vue/issues/5269 #issuecomment-288912328, https://github.com/vuejs/vue/issues/6913), seperti cara kerja with di JavaScript.

Saya suka itu. Satu pertanyaan yang tersisa bagi saya adalah bagaimana kami ingin menangani slot bernama. Jika kita ingin mengizinkan slot bernama untuk menggunakan direktif yang sama, kita juga perlu mengizinkannya pada <template> :

<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>

Kita dapat menggunakan kesempatan ini untuk menghentikan sot-scope dan menghapusnya di Vue 3, menggantinya dengan v-scope

Saya pikir kita seharusnya tidak berakhir dengan dua konsep yang berbeda ( v-scope direktif dan slot-scope atribut) untuk hal yang sama.

Sidenote: Sekarang, melihat itu, orang mungkin mendapatkan kesan dari hierarki elemen &
direktif, bahwa mereka dapat mengakses otherScope dan scope di slot bernama. Mungkin sisi negatifnya.

@LinusBorg untuk nama, argumen bisa melakukan v-scope:slotName . Saya pikir intinya hanya mengizinkan ini pada komponen adalah mengganti apa yang dikatakan @Justineo ( https://github.com/vuejs/vue/issues/9180#issuecomment-446168296 )

Sidenote: Sekarang, melihat itu, orang mungkin mendapatkan kesan dari hierarki elemen &
directive, bahwa mereka dapat mengakses otherScope dan scope di slot bernama. Mungkin sisi negatifnya.

Itu berhasil, kan? Saya cukup yakin saya memiliki cakupan slot bersarang

Itu berhasil, kan? Saya cukup yakin saya memiliki cakupan slot bersarang

Saya tidak membuat sarang mereka, mereka adalah slot saudara. Saya mendefinisikan ruang lingkup slot default dengan v-scope pada komponen, dan slot bernama (yang merupakan slot saudara ) dengan <template slot="someName">
Lihat? Ini membingungkan ^^

@LinusBorg Saya pikir penggunaannya akan membingungkan. Menurut saya secara konseptual adalah:

  • Ketika v-scope digunakan pada komponen secara langsung, berarti Anda hanya menggunakan slot default.
  • Jika Anda ingin menggunakan slot bernama... Anda masih perlu menggunakan slot + slot-scope .

Saya setuju bahwa memiliki v-scope dan slot-scope dapat menjadi tidak konsisten/membingungkan, terutama bagi pengguna baru yang tidak mengetahui bagaimana kami sampai pada desain saat ini.

Memecahkan masalah

Mencoba mensintesis, sepertinya kami menginginkan solusi yang akan:

  • kurangi boilerplate yang diperlukan untuk mengakses data dari slot tercakup
  • hindari menyiratkan bahwa data untuk slot default tersedia di slot bernama
  • meminimalkan jumlah API baru yang harus kami perkenalkan
  • jangan menemukan kembali API di mana spesifikasi komponen web sudah memiliki solusi yang dapat diterima
  • tetap eksplisit, untuk menghindari kebingungan tentang dari mana data berasal

Saya mungkin punya solusi yang mengatasi semua ini! Dengan $event untuk v-on , kita memiliki preseden untuk menyediakan ekspresi yang dijalankan di dalam fungsi implisit dengan argumen bernama. Orang-orang tampaknya menikmati kenyamanan itu dan saya tidak melihat banyak kebingungan yang disebabkan olehnya, jadi mungkin kita harus mengikuti contoh itu untuk slot terbatas.

Solusi yang diusulkan

Alih-alih menggunakan v-scope , kita dapat membuat ruang lingkup slot tersedia sebagai $slot . Sebagai contoh:

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

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

Untuk konteks, templat anak mungkin terlihat seperti:

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

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

Saat slot disarangkan, objek $slot akan menggabungkan data slot, dengan slot paling dalam mengambil prioritas override. Misalnya, di:

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

penggabungan mungkin terlihat seperti ini:

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

Anda mungkin khawatir/bertanya-tanya tentang bagaimana menangani namespace tumpang tindih dan dalam 99,9% kasus, saya benar-benar tidak berpikir itu akan menjadi masalah. Sebagai contoh:

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

Dalam contoh di atas, $slot.user akan selalu memiliki nilai yang benar, terlepas dari cakupan bersarang. Namun, terkadang Anda benar-benar membutuhkan akses ke kedua properti secara bersamaan, seperti di:

<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>

Untuk kasus tepi yang jarang ini, pengguna masih dapat menggunakan slot-scope dengan perilakunya saat ini sebagai pintu keluar . Tetapi v-scope tetap tidak diperlukan, karena jika saya memahaminya dengan benar, tujuannya adalah untuk menyederhanakan kasus penggunaan yang paling umum, yang objek $slot akan telah diselesaikan tanpa menyebabkan masalah yang sama.

Keuntungan

  • Saat pengguna ingin mengakses data yang disediakan oleh slot, satu-satunya boilerplate adalah $slot , yang sesingkat mungkin namun tetap eksplisit. Sangat jelas bahwa data berasal dari slot, tanpa perlu menelusuri komponen untuk melihat di mana properti didefinisikan.

  • Untuk mengetahui data mana yang dapat mereka akses, pengguna hanya perlu memikirkan slot mana konten akan dirender . Saya pikir dengan v-scope , akan ada banyak kebingungan dengan orang-orang yang menganggapnya berfungsi seperti v-for , karena itulah satu-satunya arahan lain yang mendefinisikan properti konteks cakupan.

  • Pengguna tidak harus terbiasa dan nyaman dengan destrukturisasi untuk menggunakan slot tercakup , sehingga mengurangi kurva pembelajaran.

  • Dalam beberapa kasus, terutama dengan banyak slot bersarang yang semuanya menyediakan status, tidak akan jelas komponen _yang_ mana beberapa status berasal dan pengguna harus mencapai slot-scope lagi. Ini mungkin terdengar seperti kerugian, tetapi ketika saya melihat slot cakupan bersarang, hampir selalu untuk pola penyedia negara bagian, yang menurut saya sangat anti-pola. Berikut alasan saya . Jadi fakta bahwa banyak slot cakupan bersarang akan membutuhkan lebih banyak boilerplate sebenarnya dapat mengurangi penggunaan anti-pola .

  • Area permukaan API berkurang drastis , karena sebagian besar pengembang Vue hanya perlu mengingat $slot , yang hanya sebuah objek.

  • Dengan menggunakan properti $ -prefixed, kami membangun API slot komponen web dengan cara yang memperjelas bahwa $slot adalah hal Vue.

  • Saat ini, perpustakaan sering melakukan sesuatu seperti <slot v-bind="user" /> sehingga penggunanya dapat menyimpan beberapa karakter dengan slot-scope="user" alih-alih slot-scope="{ user } . Tampaknya elegan pada pandangan pertama, tetapi saya telah mengalaminya sebagai anti-pola. Masalah muncul ketika komponen ingin mengekspos data _selain_ pengguna. Kemudian mereka memiliki dua pilihan: membuat perubahan mendadak pada API mereka atau memaksa properti baru ini ke objek user , meskipun mungkin tidak ada hubungannya dengan pengguna. Keduanya bukan pilihan yang bagus. Untungnya, $slot akan menghilangkan godaan untuk membuat komponen kurang tahan masa depan , karena meskipun $slot masih lebih pendek dari $slot.user , Anda kehilangan konteks penting dengan menyebut pengguna sebagai $slot .

Kekurangan

  • Di beberapa slot cakupan bersarang, ada beberapa kasus tepi di mana tidak akan jelas komponen _yang_ mana beberapa data berasal. Namun dalam kasus ini, pengguna masih dapat meraih slot-scope ketika mereka membutuhkan kejelasan maksimum, jadi saya rasa itu bukan masalah besar. Plus, seperti yang saya sebutkan sebelumnya, saya pikir sangat mungkin pengguna menembak diri mereka sendiri dengan komponen penyedia negara jika mereka memiliki masalah ini sejak awal.

Saya pikir properti $ -prefixed harus konsisten di dalam seluruh instance Vue. Saya tidak yakin apakah itu akan menyebabkan kebingungan dengan secara implisit menyuntikkan $slot ke dalam setiap slot dan mereka sebenarnya mewakili argumen lokal di dalam cakupan slot ini. Ini semacam konvensi (menurut pengamatan saya) bahwa $ -properti yang diawali dengan sesuatu yang tersedia untuk setiap instance Vue, sehingga mereka dapat digunakan di mana saja, termasuk kait siklus hidup, metode, dan fungsi render. Menambahkan $slot akan merusak konvensi ini.

@chrisvfritz ini adalah proposal yang menarik, tetapi agak bergantung pada slot normal dan slot cakupan yang disatukan (jadi mungkin sesuatu yang dapat dipertimbangkan di v3). Masalah terbesar dengan ini adalah haruskah konten slot tersedia di komponen turunan $slots atau $scopedSlots ? Satu-satunya petunjuk adalah kehadiran $slot di suatu tempat dalam ekspresi (bisa di mana saja di bawah pohon), yang tidak seeksplisit slot-scope (yang hanya bisa di root slot). Meskipun secara teknis kami dapat mendeteksi ini di kompilator, untuk pengguna slot akan berpindah dari $slots ke $scopedSlots setiap kali pengguna mulai menggunakan $slot di template...

Apakah ini akan berdampak besar bagi pengguna BEJ?

@donnysim tidak, ini tidak mempengaruhi BEJ dengan cara apapun.

Saya pikir properti $ -prefixed harus konsisten di dalam seluruh instance Vue. Saya tidak yakin apakah itu akan menyebabkan kebingungan dengan secara implisit menyuntikkan $slot ke dalam setiap slot dan mereka sebenarnya mewakili argumen lokal di dalam cakupan slot ini.

@Justineo Saya memiliki pemikiran yang sama pada awalnya, tetapi kami melakukan ini untuk $event dan sepertinya tidak ada yang mengeluh, jadi sudah ada _is_ preseden dan kami memiliki bukti bahwa pengguna biasanya tidak bingung dan sangat menikmati kenyamanannya.

Masalah terbesar dengan ini adalah haruskah konten slot tersedia di komponen turunan $slots atau $scopedSlots ?

@yyx99803 Pertanyaan bagus! Saya punya ide yang mungkin berhasil. Bagaimana jika dalam kasus di mana ambigu (slot bersarang menggunakan $slot ), kami hanya mengkompilasi semua slot ke slot yang dicakup, tetapi juga membuat semua $scopedSlots tersedia di bawah $slots sebagai getter? Misalnya, sesuatu seperti:

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
  })
}

Dengan demikian, dalam hal:

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

Kami dapat mengkompilasi menjadi sesuatu seperti:

// 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))]
                },
              },
            ]),
          }),
        ]
      },
    },
  ]),
}) 

Dan tidak masalah apakah foo berasal dari <A> atau <B> , karena di dalam komponen tersebut this.$slots.default dan this.$scopedSlots.default(someData) akan berfungsi.

Beberapa peringatan:

  • Dalam kasus dengan slot bersarang dan hanya beberapa di antaranya yang benar-benar tercakup, fungsi render yang dikompilasi dari template menggunakan $slot akan sedikit lebih besar daripada jika menggunakan slot-scope . Tidak terlalu signifikan, jadi saya pikir itu baik-baik saja.

  • Saya tidak dapat memikirkan contoh nyata, tetapi mungkin ada kasus Edge di mana pengguna mengulangi lebih dari $slots / $scopedSlots dan memiliki properti baru atau duplikat dapat menghasilkan perilaku yang tidak terduga. Saya telah mengulangi secara dinamis bernama $slots dan $scopedSlots sebelumnya untuk mengaktifkan beberapa pola yang menarik, tetapi perubahan ini tidak akan memengaruhi kasus penggunaan apa pun yang saya temui.

Pikiran?

kami melakukan ini untuk $event dan sepertinya tidak ada yang mengeluh

@chrisvfritz Oh saya melewatkan hal $event . Meskipun jelas pengecualian untuk konvensi (saya dulu percaya itu ), $event hanya tersedia dalam lingkup yang sangat terbatas (hanya di dalam literal atribut v-bind & tidak bersarang).

Dan untuk proxy $scopedSlots pada $slots :

Saya dulu percaya bahwa slot dan slot tercakup adalah konsep yang berbeda dan Vue memiliki kegunaan terpisah untuk keduanya, yang berarti saya dapat memiliki slot dan slot tercakup yang berbagi nama yang sama tetapi melayani tujuan yang berbeda. Tetapi kemudian saya menemukan bahwa Vue sebenarnya mundur ke slot dengan nama yang sama ketika slot cakupan yang ditentukan tidak tersedia. Bagi saya ini berarti kita harus menganggapnya sebagai hal yang sama tetapi karena kita memiliki $slots dan $scopedSlots tersedia pada setiap instance, kita selalu dapat menggunakannya dalam fungsi render dengan cara yang lebih fleksibel/tidak terduga , seperti menggunakan slot default untuk mengganti semua item daftar dan slot cakupan default untuk mengganti satu item. Ini sebenarnya tidak dianjurkan (kami belum menyarankan penggunaan yang disarankan untuk ini di dokumen kami dan panduan gaya AFAIK) karena kami cenderung menggabungkannya ke dalam satu konsep slot di 3.0, tetapi melakukannya di 2.x akan rusak beberapa penggunaan diperbolehkan sejak waktu yang cukup lama kembali.

kami cenderung menggabungkannya ke dalam satu konsep slot di 3.0, tetapi melakukannya di 2.x akan mematahkan beberapa penggunaan yang diizinkan sejak lama.

@Justineo Saya mungkin salah paham, tapi saya tidak menyarankan untuk menggabungkannya dalam 2.x - hanya memperluas perilaku mundur saat ini. Kasus penggunaan yang Anda jelaskan, di mana $slots dan $scopedSlots menggunakan namespace yang sama, masih mungkin karena perilaku slot-scope akan tetap tidak berubah. Sebagai contoh:

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

masih akan bekerja persis sama. Apakah itu masuk akal?

@chrisvfritz

Saya tidak berbicara tentang menggabungkan mereka di 2.x. Saya mengatakan jika Anda melakukan ini:

buat semua $scopedSlots tersedia di bawah $slots sebagai getter

Anda tidak dapat membedakan $slots.foo dari $scopedSlots.foo dan menggunakannya secara terpisah dalam fungsi render lagi.

Anda tidak dapat membedakan $slots.foo dari $scopedSlots.foo dan menggunakannya secara terpisah dalam fungsi render lagi.

Sejujurnya, saya akan senang jika semuanya (slot dan scopedSlots) dapat diakses di scopedSlots sebagai fungsi, itu berarti tidak ada lagi pemeriksaan yang tidak berguna slot apa yang akan dirender. Tidak ada bedanya dari perspektif komponen jenis slot yang digunakan pengembang. Berasal dari pengguna sintaksis JSX dan template campuran.

@donnysim Ya saya setuju dengan Anda bahwa mereka harus digabung. Saya telah menjelaskan mengapa saya memiliki kekhawatiran seperti itu di https://github.com/vuejs/vue/issues/9180#issuecomment -447185512 . Ini tentang kompatibilitas ke belakang.

Anda tidak dapat membedakan $slots.foo dari $scopedSlots.foo dan menggunakannya secara terpisah dalam fungsi render lagi.

@Justineo Anda $slots terlebih dahulu dan tidak mengganti slot yang sudah ditentukan. Lihat contoh implementasi yang saya posting di atas:

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

Perhatikan contoh berikut:

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

Jika pengguna komponen hanya meneruskan vm.$scopedSlots.default , itu harus mengulang this.items dan merender item s ke dalam slot yang dicakup. Tapi sekarang vm.$scopedSlot.default sekarang menjadi slot tercakup dan itu ada, slot tercakup akan dipanggil alih-alih slot.

@Justineo Saya pikir kasus penggunaan ini akan sangat jarang, tetapi polanya masih dimungkinkan dengan mengubah:

if (!this.$slots.default) {

ke:

if (this.$scopedSlots.default) {

atau, jika pengguna terkadang memberikan slot cakupan yang seharusnya diabaikan karena alasan tertentu:

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

Saya masih tidak akan menganggapnya sebagai perubahan yang melanggar. Pertama, kami tidak pernah mendokumentasikan/merekomendasikan penggunaan slot dan slot tercakup dengan nama yang sama, sehingga tidak pernah menjadi bagian dari kontrak publik. Kedua, seperti yang Anda sebutkan, slot tercakup dalam templat sudah jatuh kembali ke slot tanpa cakupan dengan nama yang sama, yang berfungsi sebagai bukti historis yang baik bahwa kami tidak pernah bermaksud agar batas tercakup/tidak tercakup digunakan seperti ini.

Apakah itu masuk akal? Juga, pernahkah Anda melihat kasus penggunaan nyata untuk menggunakan kembali nama slot? Atau ada yang bisa kamu pikirkan? Saya tidak bisa, tetapi jika ada _are_ pola yang berguna dan unik yang memungkinkannya, akan lebih baik untuk mempelajarinya sekarang karena itu juga akan memengaruhi keputusan kita untuk menggabungkannya di Vue 3.

Saya pikir slot dan slot yang dicakup dengan nama yang sama untuk tujuan yang berbeda adalah
ide yang buruk. Saya menggunakan ini dalam versi sebelumnya dari Vue yang dijanjikan dan menghapusnya
karena itu membingungkan. Dan sekarang saya harus memeriksa kedua slot default
dan slot tercakup sehingga pengembang dapat menyediakan slot tanpa menghabiskan data.
Dan jika saya ingat dengan benar, Anda tidak dapat memiliki nama yang sama saat menggunakan
template.

@chrisvfritz

Saya tidak mengatakan pola itu membantu dan harus didukung. Memang bisa menimbulkan kebingungan. Saya hanya mengatakan, adalah mungkin untuk memecahkan kode yang ada. Kami tidak pernah mendokumentasikan berbagi nama yang sama antara slot dan slot tercakup, tetapi kami tidak pernah menyarankan pengguna untuk tidak melakukannya. Dan logika fallback dalam template tidak didokumentasikan. Ini mungkin tidak disengaja tetapi setidaknya saya sendiri dulu percaya itu adalah penggunaan yang sah sampai saya mempelajari logika mundur di templat ...

@posva Terima kasih telah berbagi pengalaman Anda!

@Justineo Menurut pendapat Anda, apakah Anda merasa kasus tepi ini akan menyebabkan terlalu banyak rasa sakit bagi pengguna, atau dapat diterima untuk kenyamanan meningkatkan API slot cakupan di Vue 2.x?

Ini dipindahkan ke "Todo" - Apakah kita memiliki konsensus tentang apa yang harus diterapkan?

Saya telah memikirkan hal ini selama seminggu terakhir. Saya menyukai proposal variabel $slot @chrisvfritz dan mencoba mencari pemblokir implementasi. Inilah yang menurut saya layak:

  • Semua slot dikompilasi sebagai fungsi (seperti di 3.0), jadi tidak ada lagi slot statis (ini mengarah ke pelacakan pembaruan komponen yang lebih akurat)
  • $scopedSlots akan mengekspos setiap slot sebagai fungsi
  • $slots akan mengekspos setiap slot sebagai hasil dari pemanggilan fungsi yang sesuai dengan objek kosong.

Jadi yang berikut ini akan setara:

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

Dengan perubahan internal di atas, $slots dan $scopedSlots pada dasarnya disatukan tetapi harus kompatibel! Ini juga harus mengarah pada migrasi yang lebih mudah ke 3.0.

Beberapa contoh penggunaan menggunakan variabel $slot diusulkan:

<!-- 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>

Saya tidak berpikir menggabungkan cakupan bersarang adalah ide yang bagus. Saya pikir lebih baik untuk secara eksplisit ketika bersarang terlibat (yaitu selalu menggunakan slot-scope jika Anda memiliki slot cakupan bersarang):

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

Dalam contoh terakhir Anda, slot mana yang dimaksud $slot ?

@Akryum itu { ...foo, ...bar } .

@Akryum @Justineo hmm saya tahu ini bisa membingungkan... kami membuat slot-scope dapat digunakan pada elemen root dari sebuah slot , yaitu di sini foo adalah ruang lingkup slot yang disediakan oleh <foo/> , bar disediakan oleh <bar/> dan $slot disediakan oleh <baz/> ... sekarang saya pikir itu adalah kesalahan untuk mengizinkan penggunaan seperti itu, karena penggunaan yang lebih intuitif dalam hal ini akan menjadi seperti ini:

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

ini bisa membingungkan

penggunaan yang lebih intuitif dalam hal ini akan menjadi seperti ini

Sangat setuju. Juga, kita dapat terus menggunakan destructuring.
Sebagai contoh:
```

<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>

Ditutup melalui 7988a554 dan 5d52262f

Ringkasan:

  • Mendukung variabel $slot di semua slot. (Keberadaan $slot menyebabkan slot dikompilasi sebagai slot tercakup).

  • Semua slot, termasuk slot normal, sekarang ditampilkan pada this.$scopedSlots sebagai fungsi. Ini berarti dengan asumsi this.$slots.default ada, this.$scopedSlots.default() akan mengembalikan nilainya. Hal ini memungkinkan pengguna fungsi render untuk selalu menggunakan this.$scopedSlot dan tidak lagi khawatir tentang apakah slot yang dilewatkan tercakup atau tidak. Ini juga konsisten dengan 3.0 di mana semua slot diekspos sebagai fungsi (tetapi pada this.$slots sebagai gantinya).

  • Tidak ada perubahan pada penggunaan slot-scope , tidak ada pengenalan arahan baru. Kami ingin membuat perubahan sintaks seminimal mungkin dan membiarkan potensi kerusakan menjadi 3.0.

@yyx990803 Apakah $slot menggabungkan semua ruang lingkup slot luar, atau hanya merujuk ke slot terdekat?

@Justineo tidak ada penggabungan, hanya yang paling dekat.

Melihat penggunaan slot tercakup dalam aplikasi yang saya akses, tampaknya steno $slot tidak akan benar-benar dapat digunakan di lebih dari setengah kasus tanpa perilaku penggabungan slot bersarang. Masalahnya adalah komponen dasar biasanya digunakan sebagai pengganti elemen HTML mentah, dan komponen ini sering memiliki slotnya sendiri. Sebagai contoh:

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

Dalam contoh di atas, hanya ada satu slot cakupan, untuk komponen <MapMarkers> . <BaseIcon> menerima slot non-cakupan, tetapi karena menerima slot sama sekali, $slot tidak akan terdefinisi atau objek kosong tanpa perilaku penggabungan, memaksa refactor ke clunkier:

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

Jadi inilah kekhawatiran saya:

  • Setidaknya dari sampling saya sendiri, tampaknya _sebagian besar_ manfaat $slot bisa hilang jika hanya mengacu pada slot paling dalam. Bahkan, itu mungkin lebih berbahaya daripada kebaikan, karena ada lebih banyak API untuk dipelajari orang.

  • Pustaka UI mungkin mulai menggunakan alat peraga secara berlebihan dengan v-html mana slot akan lebih sesuai, sebagai tanggapan atas keluhan pengguna tentang tidak dapat menggunakan $slot .

  • Pengguna mungkin mulai menghindari komponen dasar dalam beberapa kasus, untuk menjaga penggunaan slot cakupan lebih elegan, yang mengarah ke kode yang kurang dapat dipelihara.

@yyx990803 Apakah perhatian utama Anda dengan penggabungan $slot akan sulit untuk membedakan dari mana data slot komponen berasal? Jika demikian, mungkin membantu untuk mengetahui bahwa satu-satunya kasus di mana aplikasi yang saya miliki aksesnya menggunakan slot bersarang bersarang adalah untuk komponen penyedia negara, yang seperti yang saya sebutkan sebelumnya saya temukan sebagai anti-pola. Jadi terutama dengan properti slot yang diberi nama baik, saya pikir kasus ambiguitas aktual dalam kasus penggunaan yang sah akan sangat jarang. Dan dengan perilaku penggabungan otomatis, pengguna akan dapat memutuskan sendiri apakah mereka membutuhkan lebih banyak kejelasan - dan ketika mereka melakukannya, untuk itulah slot-scope akan ada.

Sebagai kompromi, inilah alternatif potensial: bagaimana jika alih-alih menggabungkan slot dengan cakupan bersarang, kami memiliki $slot merujuk ke slot terdekat _yang menyediakan cakupan_? Itu bisa menghilangkan kasus dengan ambiguitas paling banyak, sambil tetap mengatasi kekhawatiran saya. Pikiran?

Alasan utama untuk menghindari penggabungan adalah non-eksplisitnya: hanya dengan membaca template, Anda benar-benar tidak tahu properti $slot mana yang berasal dari komponen penyedia mana. Ini mengharuskan Anda untuk sepenuhnya menyadari detail implementasi dari setiap komponen yang Anda lihat untuk memastikan apa yang sedang terjadi, dan itu menambah banyak beban mental. Mungkin kedengarannya baik-baik saja saat Anda menulisnya karena Anda memiliki konteks penuh dalam pikiran Anda pada saat itu, tetapi itu membuat kode jauh lebih sulit untuk dipahami oleh pembaca di masa depan (apakah itu Anda atau anggota tim lain).

$slot merujuk ke slot terdekat yang menyediakan ruang lingkup

Saya tidak berpikir itu akan berhasil. Ini mengarah pada masalah yang sama: Anda perlu mengetahui detail implementasi komponen untuk memastikan apa yang terjadi. Saya akan mengatakan itu bahkan lebih implisit dan berpotensi membingungkan daripada menggabungkan.


Dari segi jangka panjang, saya pikir solusi terbaik adalah mengubah semantik slot-scope sehingga menjadi cara untuk alias/menghancurkan $slot . Jadi contoh Anda akan terlihat seperti:

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

Ini menghilangkan kebutuhan untuk penggabungan, eksplisit tentang variabel mana yang berasal dari penyedia mana, dan tidak terlalu bertele-tele. (Saya pikir inilah yang mungkin akan kita lakukan di 3.0)

Titik canggung yang kami hadapi saat ini adalah kami mengizinkan slot-scope untuk digunakan pada non-templat, dan sekarang hal itu mencegah kami menggunakannya pada komponen itu sendiri.

Mengubah semantiknya di 3.0 juga dapat menyebabkan banyak kebingungan dan rasa sakit migrasi.

Mungkin kita dapat menghindari masalah while dengan memperkenalkan properti baru, slot-alias , yang melakukan persis seperti yang dikatakan - aliasing $slot ke sesuatu yang lain. Tidak seperti slot-scope , itu hanya dapat digunakan pada komponen itu sendiri atau wadah slot templat (ini berarti itu akan bekerja persis sama dengan slot-scope bila digunakan pada <template> ) :

Slot default bersarang:

<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>

Slot bernama tanpa bersarang:

Satu-satunya kasus di mana itu pasti menjadi verbose, diberi nama slot dengan bersarang:

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

(Ini sebenarnya bisa kurang bertele-tele dengan menggunakan keduanya slot-scope DAN slot-alias , tapi saya pikir itu bisa agak membingungkan. Saya mendukung penghentian slot-scope sama sekali)

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

Akhirnya, kita bahkan dapat memberikannya singkatan, () (karena di JSX render props biasanya adalah fungsi panah yang dimulai dengan () ):

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

Bandingkan di atas dengan JSX yang setara:

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

Penutupan karena desainnya sekarang sangat berbeda dari yang awalnya diusulkan. Membuka utas baru sebagai gantinya.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat