Ember.js: [META] Kesiapan Menyulam

Dibuat pada 18 Agu 2020  ·  17Komentar  ·  Sumber: emberjs/ember.js

Saat ini, Embroider dalam mode kompatibilitas dapat digunakan di aplikasi baru dan di banyak aplikasi yang sudah ada. Lebih sulit menggunakan Embroider dalam mode staticComponents , yang diperlukan untuk mendapatkan keuntungan dari mode splitAtRoutes .

Masalah # 501 di repositori Embroider melacak masalah lain yang diperlukan untuk menstabilkan Embroider sebagai bagian dari rilis Ember.js.

Masalah ini melacak langkah-langkah yang perlu kita ambil sebelum orang dapat secara praktis menggunakan Ember dengan Embroider sebagai opsi yang didukung dengan pemisahan kode berbasis rute ("Kesiapan menyulam"). Meskipun ada banyak cara terperinci untuk menggunakan Embroider (termasuk mode kompatibilitas yang menawarkan sedikit manfaat nyata tetapi penting untuk memindahkan Ember sendiri ke Embroider secara default), masalah ini difokuskan pada kemampuan untuk menggunakan Embroider dengan aplikasi Ember normal dan mendapatkan manfaat dari pemisahan kode berbasis rute.

Persyaratan Teknis

Seperti yang dijelaskan dalam Embroider README , untuk mengaktifkan pemisahan kode berbasis rute ( splitAtRoutes ), aplikasi harus dapat mengaktifkan tanda ini:

  • [] staticAddonTestSupportTrees
  • [] staticAddonTrees
  • [] staticHelpers
  • [] staticComponents

Jika addon atau aplikasi tidak dapat bekerja dengan adanya flag ini, mereka menggunakan "fitur dinamis klasik".

MVP: Menghentikan dan mengganti (component dynamicString)

Untuk target pertama kesiapan Embroider ("MVP"), kami perlu menghilangkan penghalang pandang paling umum yang kami temukan saat mencoba mengaktifkan tanda statis di aplikasi dunia nyata.

Untuk pencapaian MVP, ini bukan tujuan untuk semua addon ekosistem yang mendukung tanda ini.

Alih-alih, sasarannya adalah agar aplikasi memiliki jalur transisi yang wajar ke splitAtRoutes , dan memungkinkan untuk membangun aplikasi yang substansial dan tidak sepele dalam mode itu. Itu berarti bahwa semua addons yang disertakan dalam cetak biru default harus bermigrasi dari fitur dinamis klasik. Ini juga berarti bahwa addons yang sering digunakan dalam aplikasi dunia nyata, seperti konkurensi Ember, tidak boleh menggunakan fitur dinamis klasik.

staticComponents

Ini adalah flag statis yang paling penting , dan persyaratannya merupakan kendala paling substansial bagi target MVP.

Untuk mengaktifkan staticComponents , aplikasi ( termasuk tambahannya ) harus bebas dari semua penggunaan (component dynamicString) .

Yang penting, aplikasi dan tambahannya diizinkan untuk menggunakan (component "static string") dalam mode staticComponents .

Dalam praktiknya, ini berarti kita perlu bermigrasi dari pola seperti ini :

{{#let (component this.componentName) as | Component |}}

Addon yang saat ini mengambil string sebagai bagian dari API publiknya harus, sebagai gantinya, mengambil komponen, yang berarti bahwa addon ini perlu bermigrasi ke pendekatan yang mengharuskan penggunanya untuk menyediakan komponen yang akan dipanggil, bukan string.

Ini adalah situasi yang sangat sulit, karena this.component didefinisikan sebagai this.componentName = <code i="29">scaffolding/${dasherize(csId!)}/${dasherize(this.args.feature)}</code> . Situasi seperti inilah yang menjadi alasan mengapa kita perlu memikirkan dan meluncurkan strategi transisi dengan hati-hati.

Minimal, untuk memungkinkan pengaya berpindah dari (component dynamicString) , kita perlu membuat versi baru dari kata kunci component yang tidak mengizinkan pemanggilan komponen dinamis.

Selain itu, kita perlu memperbaiki bug yang secara tidak sengaja mengizinkan pemanggilan komponen dengan sintaks braket sudut untuk bekerja dengan cara yang sama seperti kata kunci dinamis component . Hal ini perlu segera dilakukan, karena begitu orang mulai mencoba berpindah dari (component dynamic) , ada kemungkinan besar bahwa orang-orang secara tidak sengaja akan bermigrasi ke <dynamic> , amati bahwa cara ini berfungsi, dan lanjutkan.

Barang Aksi:

  • [] Rancang dan lepaskan versi baru (component) yang hanya mendukung string statis (memerlukan RFC)
  • [] Perbaiki bug yang memungkinkan pemanggilan braket sudut berperilaku seperti pemanggilan komponen dinamis di Ember (perbaikan bug, mungkin API intim)

staticHelpers

Flag staticHelpers tidak mengurangi ekspresi template Ember, tetapi ini berarti bahwa seluruh daftar pembantu aplikasi tidak akan tersedia dalam set modul di loader.

Kami berasumsi bahwa ini akan menyebabkan kerusakan pada aplikasi Ember, tetapi masalah lebih sedikit daripada staticComponents . Saat ini kami tidak mengharapkan staticHelpers untuk mempengaruhi target MVP, tetapi hal itu dapat berubah saat kami mencoba untuk meningkatkan aplikasi ke splitAtRoutes . Jika itu terjadi, kami perlu mengevaluasi kasus penggunaan yang bermasalah dan mempertimbangkan API baru untuk memfasilitasi migrasi.

staticAddonTrees dan staticAddonTestSupportTrees

Asumsi saat ini adalah bahwa tanda ini kompatibel dengan sebagian besar aplikasi dan addon Ember idiomatik, dan oleh karena itu tidak menimbulkan masalah substansial apa pun untuk persyaratan target MVP.

Langkah selanjutnya

Masalah ini dimaksudkan untuk tetap menjadi masalah pelacakan setelah kami mencapai target MVP. Setelah target MVP, kami perlu memfasilitasi migrasi ekosistem lengkap dan meningkatkan ergonomi kasus penggunaan dinamis (yang mempertahankan dukungan untuk pemisahan kode). Impor template kemungkinan akan membantu mencapai tujuan ini.

Help Wanted

Komentar yang paling membantu

@NullVoxPopuli @jherdman Keren!

Secara umum, cara untuk berpikir tentang apa yang perlu dilakukan orang selain (component dynamicString) adalah bahwa mereka membutuhkan import atau (component "staticString") untuk pergi ke suatu tempat .

Pilihannya meliputi:

  • pemanggil komponen akan melakukan (component "staticString")
  • kode dengan dinamisme akan menghitung kemungkinan, memasukkannya ke dalam peta, dan menggunakan (get componentMap dynamicString) untuk mengeluarkannya

Sekarang Anda mungkin berpikir: bagaimana mungkin (get components dynamicString) bisa lebih baik dari (component dynamicString) ? Jawabannya adalah bahwa componentMap harus dibangun di suatu tempat , dan aturan berlaku secara rekursif.

Jadi katakanlah Anda sedang menulis komponen yang memungkinkan Anda menulis <InputField @type="text" /> atau <InputField @type="checkbox" /> (seperti <input> dalam HTML).

Template Anda untuk input-field akan terlihat seperti ini:

{{#let (hash text=(component "inputs/text-field") checkbox=(component "inputs/checkbox")) as |components|}}
  {{component (get components @type)}}
{{/let}}

Saat Anda menulis kode dengan cara ini, Embroider dapat melihat bahwa kode tersebut hanya perlu menyertakan dua komponen dalam bundel. Apakah Anda menulis seperti ini:

{{component (concat "inputs/" <strong i="33">@type</strong> "-field")}}

(ya, hal-hal seperti itu sangat umum)

maka Embroider tidak dapat dengan mudah menganalisis kode dan membatasi komponen yang akan disertakan dalam bundel. Ini lebih buruk lagi jika Anda melakukan pekerjaan di JavaScript (yang juga sangat umum):

export default class extends Component {
  get innerComponent() {
    return `inputs/${this.args.type}-field`;
  }
}

dengan template ini:

{{component this.innerComponent}}

Ini sedikit perubahan, tetapi ini memungkinkan Embroider untuk menentukan komponen mana yang benar-benar digunakan.

Semua 17 komentar

Salah satu cara untuk meringankan biaya transisi adalah dengan mengizinkan addons untuk menunjukkan secara statis argumen mana yang sesuai dengan string statis yang diteruskan oleh pemanggil komponen.

Sebagai spitball (di mana better-component adalah placeholder untuk nama kata kunci komponen statis).

{{better-component <strong i="8">@arg</strong> staticString=true}}

Ini akan memungkinkan addons untuk menunjukkan bahwa "pemanggilan dinamis" tertentu hanya berfungsi jika pemanggil menyediakan string statis.

Kita sebaiknya hanya melakukan ini jika banyak kasus komponen dinamis, dalam praktiknya, disebabkan oleh "argumen string yang diteruskan ke addon" secara langsung.

Idk jika ini adalah topik untuk masalah ini, tetapi saya secara berkala mencoba mencari tahu keadaan statis penuh di emberclear, dan telah menyimpan jejak kertas tentang masalah dan solusinya.

https://github.com/NullVoxPopuli/emberclear/pull/784

Jadi, jika orang mengalami masalah, mungkin dokumentasi Abe dapat membantu? Idk

Juga, saya sangat bersemangat untuk menyulam, dan sudah memiliki rencana besar setelah keadaan statis penuh tercapai
https://github.com/emberjs/rfcs/issues/611

Aplikasi kami adalah sistem pengiriman konten dinamis. Pembuat konten mengumpulkan konten dari "Elemen Aktivitas", yang masing-masing memiliki Komponen Ember yang sesuai, yang namanya ditentukan pada model Data Ember. Inti dari sistem ini kurang lebih terlihat seperti ini:

// example model definition
export default class TextElement extends Model {
  _componentName = 'text-element';
}
  {{#each (sort-by "position" @activityElements) as |activityElement|}}
    {{component (get activityElement "_componentName")}}

Bacaan saya di atas menunjukkan bahwa ini adalah skenario (component dynamicString) . Apakah itu akurat?

Bacaan saya di atas menunjukkan bahwa ini adalah skenario (komponen dynamicString). Apakah itu akurat?

Apa itu @activityElements dan di mana itu didefinisikan?

Ini akan menjadi array contoh Model Data Ember yang diteruskan ke komponen pengontrol .

Kami juga berinvestasi sedikit banyak pada komponen dinamis, FYI. Saya menanyakannya di sini tahun lalu: https://discuss.emberjs.com/t/the-perils-of-dynamic-component-invocation/16784.

Kami juga. Kami benar-benar tidak tahu komponen yang akan digunakan di aplikasi sebelumnya. Mereka berada dalam database (yang tentu saja membuatnya dapat diubah), dikompilasi di backend dan dikirim sesuai permintaan ke frontend. Tidak bisa menggunakan dinamis component helper bagi kita sama seperti tidak bisa untuk-masing-masing melalui sebuah array. Saya sangat berharap ada jalan ke depan untuk kasus penggunaan seperti ini. Saya pasti tidak akan peduli tentang pemisahan kode / gemetar pohon jika aplikasi saya tidak berfungsi karena saya tidak memiliki dinamisme yang saya butuhkan.

Bisakah peta digunakan dari semua komponen yang valid untuk skenario itu?

Sebagai contoh:

{{#let (hash
   Foo=(import 'path/to/foo')
   Etc=...
) as |validComponents|
}}
  {{component (get validComponents @someDynamicValue)}}
{{/let}}

?

Misalnya, Anda tidak dapat benar-benar memiliki komponen dinamis penuh, karena Anda hanya dapat merender apa yang ada di aplikasi Anda - membuat daftar untuk memilih apa yang akan dirender juga akan membantu dalam proses debug. "Oh, nilai ini bukan salah satu komponen yang valid"

Bisakah peta digunakan dari semua komponen yang valid untuk skenario itu?

Ini pasti akan menutupi kasus penggunaan kami.

@NullVoxPopuli @jherdman Keren!

Secara umum, cara untuk berpikir tentang apa yang perlu dilakukan orang selain (component dynamicString) adalah bahwa mereka membutuhkan import atau (component "staticString") untuk pergi ke suatu tempat .

Pilihannya meliputi:

  • pemanggil komponen akan melakukan (component "staticString")
  • kode dengan dinamisme akan menghitung kemungkinan, memasukkannya ke dalam peta, dan menggunakan (get componentMap dynamicString) untuk mengeluarkannya

Sekarang Anda mungkin berpikir: bagaimana mungkin (get components dynamicString) bisa lebih baik dari (component dynamicString) ? Jawabannya adalah bahwa componentMap harus dibangun di suatu tempat , dan aturan berlaku secara rekursif.

Jadi katakanlah Anda sedang menulis komponen yang memungkinkan Anda menulis <InputField @type="text" /> atau <InputField @type="checkbox" /> (seperti <input> dalam HTML).

Template Anda untuk input-field akan terlihat seperti ini:

{{#let (hash text=(component "inputs/text-field") checkbox=(component "inputs/checkbox")) as |components|}}
  {{component (get components @type)}}
{{/let}}

Saat Anda menulis kode dengan cara ini, Embroider dapat melihat bahwa kode tersebut hanya perlu menyertakan dua komponen dalam bundel. Apakah Anda menulis seperti ini:

{{component (concat "inputs/" <strong i="33">@type</strong> "-field")}}

(ya, hal-hal seperti itu sangat umum)

maka Embroider tidak dapat dengan mudah menganalisis kode dan membatasi komponen yang akan disertakan dalam bundel. Ini lebih buruk lagi jika Anda melakukan pekerjaan di JavaScript (yang juga sangat umum):

export default class extends Component {
  get innerComponent() {
    return `inputs/${this.args.type}-field`;
  }
}

dengan template ini:

{{component this.innerComponent}}

Ini sedikit perubahan, tetapi ini memungkinkan Embroider untuk menentukan komponen mana yang benar-benar digunakan.

Saya juga ingin menjelaskan hubungan ke dua fitur dalam penerbangan lainnya dengan lebih hati-hati.

  1. Impor Template
  2. Menggunakan Kelas Komponen sebagai Invokable

Jika kita menulis ulang contoh sebelumnya menggunakan kedua fitur tersebut, terlihat seperti:

---
import TextField from "./text-field";
import Checkbox from "./checkbox";
---
{{#let (hash text=TextField checkbox=Checkbox) as |components|}}
  {{component (get components @type)}}
{{/let}}

Ini memiliki properti bagus untuk menghilangkan aturan khusus yang harus dipahami oleh Embroider, dan membuat model pengguna untuk pemisahan kode lebih banyak lagi tentang modul.

Menggunakan Kelas Komponen sebagai Invokable

Sejak RFC # 481 , kelas komponen telah menjadi unit mandiri yang memiliki semua informasi yang diperlukan untuk VM Glimmer untuk memanggilnya sebagai komponen.

Catatan: Ini tidak hanya benar tentang kelas yang mewarisi dari @ember/component atau @glimmer/component . Sejak RFC 481, definisi Ember tentang "komponen" adalah "objek yang diasosiasikan dengan templat dan pengelola komponen", yang menyertakan komponen khusus seperti komponen- terkait .

Sejak RFC # 432 (pembantu dan pengubah kontekstual) dan RFC # 496 (mode ketat setang), desain untuk masa depan sintaks template Ember adalah: ekspresi yang berisi pembantu, komponen atau pengubah dapat dipanggil sebagai pembantu, komponen atau pengubah.

Implikasi dari koleksi RFC yang disetujui saat ini adalah bahwa <SomeComponentClass /> (atau <this.componentClass> mana this.componentClass ditetapkan ke kelas komponen) akan "berfungsi". Ini juga rencana catatan untuk pekerjaan implementasi.

Karena itu, untuk kejelasan, kita harus membuat RFC baru yang secara eksplisit menentukan perilaku ini.

Menentukan daftar komponen yang valid di HBS di suatu tempat dapat dilakukan untuk kami, tetapi ada beberapa peringatan yang perlu diperhatikan:

  1. Saat ini, kami menggunakan solusi JS yang disebutkan @wycats . Melakukan hal yang sama di penyedia HBS bisa dilakukan, tetapi seperti yang kita semua tahu logika di HBS sedikit lebih sulit. Ini juga _much_ lebih sulit untuk menguji unit bahwa komponen provider menghasilkan komponen yang benar daripada fungsi util yang mengembalikan string yang benar.
  2. Ini tidak ada di JS (dengan API publik), tetapi menggunakan struktur direktori untuk keuntungan kita akan sangat bagus di sini. Misalnya:

    {{#let (lookup-directory "components/inputs/") as |components|}}
    {{/let}}
    

    Hal semacam ini mungkin akan lebih mudah dipertahankan dari waktu ke waktu jika Anda mengetahui jenis polimorfik yang dikelompokkan bersama berdasarkan struktur direktori.

Bagaimanapun, saya merasa seperti komponen dinamis membutuhkan masalah sendiri untuk dibahas daripada mengambil alih yang ini 🙈 😄

Bagaimanapun, saya merasa seperti komponen dinamis membutuhkan masalah sendiri untuk dibahas daripada mengambil alih yang ini 🙈 😄

Saya setuju, dan akan segera membukanya di repo RFC, dan memposting tautan di sini.

@mehulkar @wycats @jherdman bagaimana dengan mengizinkan pengguna untuk hanya mengimpor daftar komponen yang dapat dipanggil secara dinamis? Tampaknya jauh lebih mudah diikuti daripada penolong yang memiliki tingkat tipuan ekstra.

import Component1 from './dynamic/component-1';
import Component2 from './dynamic/component-2';
import Component3 from './dynamic/component-3';

export default class extends Component {
  get innerComponent() {
    switch(this.args.type) {
      case 'one':
        return Component1;
      case 'two':
        return Component2;
      case 'three':
        return Component3;
      default:
        // handle invalid type
    }
  }
}

Kemudian template bisa melakukan: {{#let (component this.innerComponent) as |DynamicComponent|}} atau yang serupa.

Saya lebih suka sesuatu seperti ini yang lebih mudah dicari / dibaca / dianalisis, dan dengan mudah mendeteksi di mana komponen ini digunakan. Pikiran?

@Samsinite ya saya pikir itu idenya. Helper di HBS sebagian besar akan berupa gula sintaksis dan berguna untuk komponen khusus template.

@Samsinite pada dasarnya itulah yang saya usulkan sebagai perbaikan jangka pendek (sebelum impor template).

Ini akan mengharuskan kita untuk mengizinkan kelas komponen menjadi invocable, yang saya bicarakan dalam komentar ini .

Ayo lakukan!

Ah, masuk akal untuk komponen template saja, maaf karena salah paham :). Saya juga menyukai sintaks untuk komponen template saja dari:

---
import TextField from "./text-field";
import Checkbox from "./checkbox";
---
{{#let (hash text=TextField checkbox=Checkbox) as |components|}}
  {{component (get components @type)}}
{{/let}}

juga, pada kenyataannya, saya mungkin akan menggunakannya untuk template saja dan komponen yang didukung js, tergantung pada apa yang orang lain di tim anggap lebih mudah untuk dibaca.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat