Ember.js: [QUEST] Komponen Glimmer di Ember

Dibuat pada 28 Feb 2018  ·  23Komentar  ·  Sumber: emberjs/ember.js

Masalah pencarian ini melacak implementasi komponen Glimmer di Ember.js.

Rencana

Komponen Glimmer.js memiliki fitur berikut:

  1. Template “HTML Luar” (tanpa tagName , attributeBindings , dll.)
  2. Argumen dalam template diawali dengan @ , seperti {{@firstName}}
  3. Argumen ditetapkan pada komponen sebagai this.args
  4. Kelas komponen menggunakan sintaks kelas JavaScript
  5. Status komponen yang dapat diubah dianotasi dengan properti @tracked
  6. Komponen dipanggil melalui sintaks <AngleBracket />
  7. Atribut dapat "diperciki" melalui …attributes

Sesuai dengan semangat inkrementalisme Ember, kami ingin mendaratkan fungsionalitas ini sepotong demi sepotong melalui sebuah addon. Ini memungkinkan komunitas untuk mulai menggunakan fitur dan memberikan umpan balik di awal proses.

Bahkan, kami sudah mulai menelusuri komponen Glimmer di Ember. Dua fitur pertama sudah mulai mendarat:

  1. Dalam komponen template-only, template adalah “HTML luar” (dengan asumsi fitur opsional template-only-glimmer-components telah diaktifkan.)
  2. Argumen dapat diakses di template melalui awalan @ (misalnya {{@firstName}} ).

Masalah ini mengusulkan penyelesaian proses membawa komponen Glimmer ke Ember dengan mengizinkan add-on menyediakan implementasi komponen alternatif, kemudian mengubah paket @glimmer/component menjadi addon Ember yang mengimplementasikan API komponen Glimmer.js.

Kami akan membagi pekerjaan itu menjadi beberapa fase, masing-masing membuka manfaat untuk aplikasi dan add-on Ember yang ada. Fase 0 adalah tentang menambahkan primitif yang diperlukan ke Ember.js untuk mendukung implementasi komponen alternatif. Fase 1, 2, dan 3 adalah tentang mengaktifkan API komponen Glimmer.js secara bertahap.

Sementara kami mendalami Fase 0 dan 1, kami akan menunda penjelajahan detail teknis fase selanjutnya hingga fase pertama mendekati penyelesaian.

Fase 0: Menyesuaikan Perilaku Komponen

TL;DR: Tambahkan API CustomComponentManager ke Ember.js untuk memungkinkan addon mengimplementasikan API komponen khusus.

Saat ini, semua komponen dalam aplikasi Ember diasumsikan sebagai subkelas dari Ember.Component . Untuk mendukung API komponen alternatif di Ember, kami memerlukan beberapa cara untuk memberi tahu Ember kapan dan bagaimana perilaku komponen harus berubah.

Saat kami mengatakan "perilaku komponen khusus", kami secara khusus bermaksud:

  1. Bagaimana instance komponen dibuat.
  2. Bagaimana instance komponen dihancurkan.
  3. Bagaimana argumen diberikan ke instance komponen.
  4. Bagaimana komponen diberi tahu tentang perubahan siklus hidup ini.

Sementara Glimmer VM memperkenalkan konsep "pengelola komponen", sebuah objek yang membuat keputusan ini, API ini sangat rendah. Akan terlalu dini untuk mengadopsinya secara langsung sebagai API publik di Ember karena sulit untuk ditulis, mudah ditulis dengan cara yang merusak komponen lain, dan belum stabil.

Sebagai gantinya, kami mengusulkan API Ember baru yang disebut CustomComponentManager yang mengimplementasikan pola delegasi. CustomComponentManager menyediakan area permukaan API yang lebih kecil daripada ComponentManager Glimmer VM API yang lengkap, yang memungkinkan pembuat addon jatuh ke dalam "lubang kesuksesan".

Jadi, bagaimana Ember mengetahui manajer komponen mana yang digunakan untuk komponen tertentu? Iterasi asli RFC Komponen Kustom memperkenalkan konsep ComponentDefinition , struktur data yang terdaftar dengan penuh semangat di Ember dan menentukan manajer komponen mana yang akan digunakan.

Salah satu manfaat utama dari pendekatan ComponentDefinition adalah bahwa resolusi manajer komponen dapat terjadi pada waktu pembuatan. Sayangnya, itu berarti kita harus merancang API untuk persisnya bagaimana ini didaftarkan, dan kemungkinan berarti semacam integrasi dengan pipa pembangunan.

Sebagai gantinya, kami mengusulkan API untuk menyetel pengelola komponen saat runtime melalui anotasi pada kelas komponen. Langkah inkremental ini memungkinkan pekerjaan pada manajer komponen kustom untuk melanjutkan sementara solusi jangka panjang dirancang.

Penemuan Manajer Komponen Kustom

Dalam iterasi ini, komponen harus secara eksplisit memilih manajer komponen alternatif. Mereka melakukan ini melalui fungsi componentManager , diekspor oleh Ember, yang memberi anotasi saat runtime manajer komponen mana yang harus digunakan untuk kelas komponen tertentu:

import { componentManager } from '@ember/custom-component-manager';
import EmberObject from '@ember/object';

export default componentManager(EmberObject.extend({
  // ...
}), 'glimmer');

Akhirnya, ini bisa menjadi dekorator kelas:

import { componentManager } from '@ember/custom-component-manager';

export default @componentManager('glimmer') class {
  // ...
}

Saat pertama kali komponen ini dipanggil, Ember memeriksa kelas untuk melihat apakah ia memiliki anotasi pengelola komponen khusus. Jika demikian, ia menggunakan nilai string untuk melakukan pencarian pada wadah. Dalam contoh di atas, Ember akan meminta wadah untuk objek dengan kunci wadah component-manager:glimmer .

Addons dengan demikian dapat menggunakan semantik resolusi normal untuk menyediakan pengelola komponen khusus. Addon komponen Glimmer kami dapat mengekspor manajer komponen dari addon/component-managers/glimmer.js yang akan ditemukan secara otomatis melalui aturan resolusi normal.

Meskipun API ini bertele-tele dan tidak terlalu ergonomis, aplikasi dan add-on dapat mengabstraksikannya dengan memperkenalkan kelas dasar mereka sendiri dengan anotasi. Misalnya, jika addon bernama turbo-component ingin menyediakan pengelola komponen khusus, addon tersebut dapat mengekspor kelas dasar seperti ini:

// addon/index.js
import EmberObject from '@ember/object';
import { componentManager } from '@ember/custom-component-manager';

export default componentManager(EmberObject.extend({
  // ...
}), 'turbo');

Pengguna addon ini dapat mensubklasifikasikan kelas dasar TurboComponent untuk mendefinisikan komponen yang menggunakan manajer komponen yang benar:

import TurboComponent from 'turbo-component';

export default TurboComponent.extend({
  didInsertElementQuickly() {
    // ...
  }
});

API Komponen Kustom

Tidak ada komponen yang merupakan sebuah pulau, dan untuk alasan kompatibilitas mundur, penting agar pengenalan API komponen baru tidak merusak komponen yang ada.

Salah satu contohnya adalah API hierarki tampilan yang ada. Komponen Ember dapat memeriksa komponen induknya melalui properti parentView . Bahkan jika induknya bukan Ember.Component , komponen Ember anak harus tetap memiliki properti parentView non-null.

Saat ini, CurlyComponentManager di Ember bertanggung jawab untuk mempertahankan status ini, serta "status lingkup" ambient lainnya seperti target tindakan.

Untuk mencegah manajer komponen yang diimplementasikan dengan buruk melanggar invarian dalam sistem yang ada, kami menggunakan pola komposisi untuk menyesuaikan perilaku sambil menyembunyikan sudut tajam dari API yang mendasarinya.

import CustomComponentManager from "@ember/custom-component-manager";

export default new CustomComponentManager({
  // major and minor Ember version this manager targets
  version: "3.1",
  create({ ComponentClass, args }) {
    // Responsible for instantiating the component class and passing provided
    // component arguments.
    // The value returned here is passed as `component` in the below hooks.
  },
  getContext(component) {
    // Returns the object that serves as the root scope of the component template.
    // Most implementations should return `component`, so the component's properties
    // are looked up in curly expressions.
  },
  update(component, args) {
    // Called whenever the arguments to a component change.
  },
  destroy(component) {
  }
});

Fase 1: Komponen Ember Object Glimmer

Kelas dasar Component di Ember mendukung daftar panjang fitur, banyak di antaranya tidak lagi banyak digunakan. Fitur-fitur ini dapat membebankan biaya kinerja, bahkan ketika tidak digunakan.

Sebagai langkah pertama, kami ingin memberikan cara untuk ikut serta dalam API komponen Glimmer.js yang disederhanakan melalui paket @glimmer/component . Untuk memudahkan migrasi, kami akan menyediakan implementasi kelas dasar Glimmer Component yang diturunkan dari Ember.Object pada @glimmer/component/compat .

Berikut adalah contoh tampilan "komponen Ember-Glimmer":

// src/ui/components/user-profile/component.js
import Component from '@glimmer/component/compat';
import { computed } from '@ember/object';

export default Component({
  fullName: computed('args.firstName', 'args.lastName', function() {
    let { firstName, lastName } = this.args
    return `${firstName} ${lastName}`;
  })

  isAdmin: false,

  toggleAdmin() {
    this.set('isAdmin', !this.isAdmin);
  }
});
{{!-- src/ui/components/user-profile/template.hbs --}}
<h1>{{fullName}}</h1>
<p>
  Welcome back, {{@firstName}}!
  {{#if isAdmin}}
    <strong>You are an admin.</strong>
  {{/if}}
</p>
<button {{action toggleAdmin}}>Toggle Admin Status</button>

Karakteristik penting dari komponen ini:

  • Template adalah HTML luar . Karena contoh template di atas tidak memiliki elemen root tunggal, ini dirender sebagai "komponen tanpa tag".
  • Tindakan hanyalah fungsi pada komponen dan tidak perlu disarangkan dalam hash actions .
  • Argumen ditetapkan pada properti args daripada menyetel properti individual pada komponen secara langsung.
  • Mereka menggunakan model objek Ember . Ini berarti alat seperti properti terkomputasi dan mixin yang sudah dikenal oleh pengembang Ember terus bekerja.

Sama pentingnya adalah apa yang tidak termasuk:

  • Properti @tracked tidak akan didukung hingga Fase 3.
  • Tidak ada properti layout atau template pada komponen.
  • Tidak ada metode atau properti yang terkait dengan hierarki tampilan, seperti childViews , parentView , nearestWithProperty , dll.
  • Tidak ada tagName , attributeBindings , atau DSL JavaScript khusus lainnya untuk memodifikasi elemen root.
  • Tidak ada send atau sendAction untuk acara pengiriman.
  • Tidak ada kelas ember-view wajib atau ID elemen panduan yang dibuat secara otomatis.
  • Tidak ada metode manual rerender() .
  • Tidak ada properti attrs (gunakan args sebagai gantinya).
  • Argumen yang diteruskan adalah "searah" dan tidak membuat ikatan dua arah.
  • Argumen yang diteruskan tidak ditetapkan sebagai properti pada instance komponen, menghindari kemungkinan tabrakan penamaan yang sulit di-debug.
  • Tidak ada this.$() untuk membuat objek jQuery untuk elemen komponen.
  • Tidak ada komponen manual appendTo ke dalam DOM.
  • Tidak ada dukungan untuk peristiwa siklus hidup berikut:

    • willInsertElement

    • didRender

    • willRender

    • willClearRender

    • willUpdate

    • didReceiveAttrs

    • didUpdateAttrs

    • parentViewDidChange

  • Tidak ada pendengar acara on() untuk acara siklus hidup komponen; kait harus diimplementasikan sebagai metode.

Satu efek samping yang menarik dari kumpulan fitur ini adalah bahwa hal itu sesuai dengan upaya untuk mengaktifkan kelas JavaScript . Sehubungan dengan desain yang diusulkan dalam ES Classes RFC , kami dapat menyediakan implementasi alternatif dari komponen di atas:

// src/ui/components/user-profile/component.js
import Component from '@glimmer/component/compat';
import { computed } from 'ember-decorators/object';

export default class extends Component {
  isAdmin = false;

  @computed('args.firstName', 'args.lastName')
  get fullName() {
    let { firstName, lastName } = this.args;
    return `${firstName} ${lastName}`;
  })

  toggleAdmin() {
    this.set('isAdmin', !this.isAdmin);
  }
});

Fase 2 - Sintaks Kurung Sudut

Fase 2 memungkinkan pemanggilan komponen melalui kurung sudut ( <UserAvatar @user={{currentUser}} /> ) selain curli ( {{my-component user=currentUser}} ). Karena sintaksis ini membedakan antara argumen komponen dan atribut HTML, fitur ini juga memungkinkan atribut yang diteruskan “splatting” ke dalam template komponen melalui …attributes .

{{! src/ui/components/UserAvatar/template.hbs }}
<div ...attributes> {{! <-- attributes will be inserted here }}
  <h1>Hello, {{@firstName}}!</h1>
</div>
<UserAvatar @user={{currentUser}} aria-expanded={{isExpanded}} />

Ini akan membuat output mirip dengan berikut:

<div aria-expanded="true">
  <h1>Hello, Steven!</h1>
</div>

Fase 3 - Properti Terlacak

Fase 3 mengaktifkan properti yang dilacak melalui dekorator @tracked di Ember. Detail interop antara model objek Ember dan properti yang dilacak sedang dikerjakan. Setelah properti yang dilacak mendarat, pengguna akan dapat melepaskan modul @glimmer/component/compat dan menggunakan kelas dasar komponen non-Ember.Object yang normal.

Bersamaan dengan fitur “pelacakan otomatis” yang baru saja digabungkan (yang menyimpulkan dependensi properti yang dihitung secara otomatis), ini akan menghasilkan penyederhanaan lebih lanjut dari kode aplikasi:

import Component, { tracked } from '@glimmer/component';

export default class extends Component {
  <strong i="24">@tracked</strong> isAdmin = false;

  <strong i="25">@tracked</strong> get fullName() {
    let { firstName, lastName } = this.args;
    return `${firstName} ${lastName}`;
  }

  toggleAdmin() {
    this.isAdmin = !this.isAdmin;
  }
}

T&J

Dapatkah saya menambahkan kembali hal-hal seperti nama kelas ember-view atau atribut id ke komponen Glimmer untuk kompatibilitas dengan CSS yang ada?

Ya. Contoh:

<div class="ember-view" id="{{uniqueId}}">
  Component content goes here.
</div>
import Component from '@glimmer/component';
import { guidFor } from '@ember/object/internals';

export default class extends Component {
  get uniqueId() {
    return guidFor(this);
  }
}

Sumber daya

Tugas

Kami akan menggunakan daftar di bawah ini untuk melacak pekerjaan yang sedang berlangsung. Saat kami mempelajari lebih lanjut selama implementasi, kami akan menambah atau menghapus item pada daftar.

API Pengelola Komponen Kustom

  • [x] Perbarui RFC Komponen Kustom untuk mencerminkan perubahan di atas (@chancancode)
  • [x] Tambahkan tanda fitur glimmer-custom-component-manager
  • [x] Menerapkan fungsi componentManager

    • [x] Ekspos sebagai { componentManager } from '@ember/custom-component-manager'

  • [x] Resolver perlu mendeteksi kelas beranotasi
  • [x] Penyelesai perlu mencari manajer komponen yang ditentukan

    • [ ] Panduan untuk penulis addon tentang di mana harus meletakkan manajer komponen sehingga mereka ditemukan

    • [ ] Bagaimana cara kerjanya dengan Penyatuan Modul? (@mixonik)

  • [x] Terapkan CustomComponentManager API

    • [x] Tentukan antarmuka CustomComponentManagerDelegate

    • [x] version

    • [x] create()

    • [x] getContext()

    • [x] update()

    • [x] destroy?()

    • [x] didCreate?()

    • [x] didUpdate?()

    • [x] getView?()

    • [x] Validasi properti version setelah pembuatan

    • [x] Internal

    • [x] Pertahankan childViews dan parentView di komponen yang ada

    • [ ] Instrumen untuk menampilkan kinerja

    • [ ] Instrumen untuk kompatibilitas dengan Ember Inspector

Addon Komponen Glimmer

  • [x] Dukungan menggunakan sparkles-components addon

    • [x] Kelas dasar harus mengimpor dan menambahkan anotasi componentManager saat dikonsumsi dari Ember

    • [x] CustomComponentManager implementasi harus dapat ditemukan melalui wadah Ember

  • [ ] Dukungan menggunakan @glimmer/component sebagai addon Ember

    • [ ] Pertahankan perilaku yang ada saat dikonsumsi dari Glimmer.js

    • [ ] import Component from '@glimmer/component' menyediakan kelas dasar JavaScript biasa

    • [ ] import Component from '@glimmer/component/compat' menyediakan Ember.Object kelas dasar

      menengadah

  • [ ] Kait Siklus Hidup

    • [x] statis create(injections)

    • [x] didInsertElement()

    • [x] willDestroy()

    • [x] didUpdate()

    • [x] Metode pemanggilan delegasi acara ( click() dll.) tidak boleh dipicu

  • [ ] Akses Elemen

    • [ ] this.bounds

    • [ ] this.element menghitung properti alias ke this.bounds

    • [ ] API berbasis pengubah elemen untuk mengekspos elemen

  • [ ] Argumen

    • [x] this.args tersedia di konstruktor

    • [x] this.args diperbarui sebelum didUpdate dipanggil

    • [ ] this.args seharusnya tidak memicu siklus rendering ulang yang tak terbatas (perlu diverifikasi)

  • [ ] Dokumentasi

    • [ ] Bagaimana cara meng-install

    • [ ] Peringatan

    • [ ] Hanya kenari

    • [ ] Pra-1.0

    • [ ] Komponen "kompat" Ember-Glimmer

    • [ ] Template HTML luar

    • [ ] Kait siklus hidup

    • [ ] Mendefinisikan properti yang dihitung



      • [ ] Bagaimana bergantung pada args



    • [ ] Panduan Migrasi / Komponen Glimmer untuk…

    • Panduan untuk menulis komponen Glimmer yang efektif untuk orang yang akrab dengan perpustakaan lain

    • [ ] Komponen Glimmer untuk Pengembang Ember

    • [ ] Komponen Glimmer untuk Pengembang React

    • [ ] Komponen Secercah untuk Pengembang Sudut

    • [ ] Komponen Glimmer untuk Pengembang COBOL

Pertanyaan-pertanyaan terbuka

  1. Apa praktik terbaik untuk tindakan? Apakah ini sesuatu yang kita perlukan untuk memungkinkan manajer komponen terhubung?
  2. Bagaimana Anda menggunakan kelas JavaScript kosong (yang tidak memiliki metode create() statis) sebagai kelas komponen? Protokol untuk menginisialisasi komponen tampaknya sederhana tetapi ternyata rumit.
  3. Di mana tempat terbaik untuk mendokumentasikan “addon API” seperti CustomComponentManager ?
  4. Bagaimana kita menangani versi CustomComponentManager ?
Quest

Komentar yang paling membantu

@dbbk Alasan terbesar kami tidak memerlukan {{...attributes}} adalah karena sintaksisnya tidak ambigu dengan atribut normal, seperti kasus lain di mana kami membutuhkan keriting, dan empat karakter yang lebih sedikit tampaknya lebih mudah diketik dan tidak terlalu berisik secara visual.

Saya pikir itu juga dapat menyebabkan orang menganggap attributes adalah properti dalam cakupan, tetapi tidak—Anda tidak dapat melakukan <SomeComponent something={{attributes}} /> , misalnya, yang disarankan oleh sintaks itu. Ini juga lebih kuat menyiratkan bahwa sintaks spread berfungsi di posisi lain ketika tidak.

Ada beberapa saran untuk mengadopsi @arguments sebagai pengikatan template khusus dan menambahkan sintaks spread ... di Handlebars, tetapi itu membutuhkan desain dan implementasi yang tidak ada dalam roadmap langsung. Saya tidak akan terlalu terkejut jika di masa depan kami mengganti ...attributes dengan {{...@attributes}} atau semacamnya.

@Gaurav0 API komponen Glimmer akan melalui proses RFC sebelum diaktifkan secara default di aplikasi Ember, jika kita ingin melakukannya. Satu hal yang menyenangkan tentang pendekatan manajer komponen kustom adalah bahwa kami tidak mengkanonisasi API "generasi berikutnya" tetapi dapat membiarkan desain yang berbeda bersaing berdasarkan kemampuannya melalui ekosistem addon.

@ Turbo87 Saran bagus, saya akan menambahkan panduan migrasi ke daftar. Kami harus menunjukkan pola yang ada dan cara mendekati pemecahan masalah yang sama dengan API baru.

Saya akan membahas kasus didReceiveAttrs() secara khusus sejak Anda membahasnya. Biasanya orang menggunakan kait ini untuk melakukan beberapa inisialisasi status komponen berdasarkan argumen yang diteruskan, tetapi dalam praktiknya ini berarti Anda dapat melakukan sedikit hal yang tidak perlu dalam kait ini, misalnya untuk menginisialisasi nilai yang tidak akan pernah digunakan. Dan tentu saja orang-orang akhirnya melakukan hal-hal yang mengejutkan di kait-kait ini yang berada di jalur panas rendering.

Alternatifnya adalah beralih dari inisialisasi "push-based" ke inisialisasi "pull-based". Misalnya, jika Anda ingin melakukan beberapa komputasi untuk menghasilkan atau menginisialisasi nilai properti komponen, Anda akan menggunakan properti komputasi terlacak. Ini memastikan pekerjaan hanya dilakukan untuk nilai yang benar-benar digunakan. Contoh:

Alih-alih ini:

import Component from "@ember/component";
import { computed } from "@ember/object";

export default Component.extend({
  didReceiveAttrs() {
    this.set('firstName', this.attrs.firstName || 'Tobias');
    this.set('lastName', this.attrs.lastName || 'Bieniek');
  },

  fullName: computed('firstName', 'lastName', function() {
    return `${this.firstName} ${this.lastName}`;
  })
});

Melakukan hal ini:

import Component, { tracked } from "@glimmer/component";

export default class extends Component {
  <strong i="27">@tracked</strong> get firstName() {
    return this.args.firstName || 'Tobias';
  }

  <strong i="28">@tracked</strong> get lastName() {
    return this.args.lastName || 'Bieniek';
  }

  <strong i="29">@tracked</strong> get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

Saya akui saya skeptis terhadap ini pada awalnya, tetapi @wycats membujuk saya, dan dalam pengalaman saya membangun aplikasi Glimmer.js besar selama sekitar setahun terakhir, kami tidak pernah menemukan kasus penggunaan untuk didReceiveAttrs yang tidak dapat dimodelkan dengan properti komputasi yang lebih efisien.

Semua 23 komentar

Kami akan mengoordinasikan pekerjaan di saluran #st-glimmer-components di komunitas Ember Slack jika Anda tertarik untuk membantu! Pengintai juga diterima jika Anda hanya ingin tahu bagaimana sosis itu dibuat.

Mengenai hal ini;

{{! src/ui/components/UserAvatar/template.hbs }}
<div ...attributes> {{! <-- attributes will be inserted here }}
  <h1>Hello, {{@firstName}}!</h1>
</div>

Saya tidak mengerti mengapa tanda kurung kurawal digunakan untuk menunjukkan variabel dinamis ( {{@firstName}} ), tetapi bukan atribut splatted? Bagi saya secara visual sepertinya <div ...attributes> adalah apa yang akan dirender. Untuk konsistensi, bukankah ini lebih baik sebagai <div {{...attributes}}> ?

Apakah ada RFC untuk komponen secercah di mana kita dapat mendiskusikan bagaimana seharusnya tampilan dan cara kerjanya?

@tomdale masalah di atas menyebutkan mendokumentasikan mode kompatibilitas komponen, tetapi tampaknya tidak menyebutkan mendokumentasikan panduan migrasi. misalnya saya cukup terkejut bahwa didReceiveAttrs() bukan lagi hal yang penting, dan akan sangat bagus untuk memiliki beberapa dokumentasi yang menunjukkan bagaimana memigrasikan kasus penggunaan umum ke pola praktik terbaik yang baru.

@dbbk Alasan terbesar kami tidak memerlukan {{...attributes}} adalah karena sintaksisnya tidak ambigu dengan atribut normal, seperti kasus lain di mana kami membutuhkan keriting, dan empat karakter yang lebih sedikit tampaknya lebih mudah diketik dan tidak terlalu berisik secara visual.

Saya pikir itu juga dapat menyebabkan orang menganggap attributes adalah properti dalam cakupan, tetapi tidak—Anda tidak dapat melakukan <SomeComponent something={{attributes}} /> , misalnya, yang disarankan oleh sintaks itu. Ini juga lebih kuat menyiratkan bahwa sintaks spread berfungsi di posisi lain ketika tidak.

Ada beberapa saran untuk mengadopsi @arguments sebagai pengikatan template khusus dan menambahkan sintaks spread ... di Handlebars, tetapi itu membutuhkan desain dan implementasi yang tidak ada dalam roadmap langsung. Saya tidak akan terlalu terkejut jika di masa depan kami mengganti ...attributes dengan {{...@attributes}} atau semacamnya.

@Gaurav0 API komponen Glimmer akan melalui proses RFC sebelum diaktifkan secara default di aplikasi Ember, jika kita ingin melakukannya. Satu hal yang menyenangkan tentang pendekatan manajer komponen kustom adalah bahwa kami tidak mengkanonisasi API "generasi berikutnya" tetapi dapat membiarkan desain yang berbeda bersaing berdasarkan kemampuannya melalui ekosistem addon.

@ Turbo87 Saran bagus, saya akan menambahkan panduan migrasi ke daftar. Kami harus menunjukkan pola yang ada dan cara mendekati pemecahan masalah yang sama dengan API baru.

Saya akan membahas kasus didReceiveAttrs() secara khusus sejak Anda membahasnya. Biasanya orang menggunakan kait ini untuk melakukan beberapa inisialisasi status komponen berdasarkan argumen yang diteruskan, tetapi dalam praktiknya ini berarti Anda dapat melakukan sedikit hal yang tidak perlu dalam kait ini, misalnya untuk menginisialisasi nilai yang tidak akan pernah digunakan. Dan tentu saja orang-orang akhirnya melakukan hal-hal yang mengejutkan di kait-kait ini yang berada di jalur panas rendering.

Alternatifnya adalah beralih dari inisialisasi "push-based" ke inisialisasi "pull-based". Misalnya, jika Anda ingin melakukan beberapa komputasi untuk menghasilkan atau menginisialisasi nilai properti komponen, Anda akan menggunakan properti komputasi terlacak. Ini memastikan pekerjaan hanya dilakukan untuk nilai yang benar-benar digunakan. Contoh:

Alih-alih ini:

import Component from "@ember/component";
import { computed } from "@ember/object";

export default Component.extend({
  didReceiveAttrs() {
    this.set('firstName', this.attrs.firstName || 'Tobias');
    this.set('lastName', this.attrs.lastName || 'Bieniek');
  },

  fullName: computed('firstName', 'lastName', function() {
    return `${this.firstName} ${this.lastName}`;
  })
});

Melakukan hal ini:

import Component, { tracked } from "@glimmer/component";

export default class extends Component {
  <strong i="27">@tracked</strong> get firstName() {
    return this.args.firstName || 'Tobias';
  }

  <strong i="28">@tracked</strong> get lastName() {
    return this.args.lastName || 'Bieniek';
  }

  <strong i="29">@tracked</strong> get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

Saya akui saya skeptis terhadap ini pada awalnya, tetapi @wycats membujuk saya, dan dalam pengalaman saya membangun aplikasi Glimmer.js besar selama sekitar setahun terakhir, kami tidak pernah menemukan kasus penggunaan untuk didReceiveAttrs yang tidak dapat dimodelkan dengan properti komputasi yang lebih efisien.

@tomdale Saya setuju bahwa didReceiveAttrs tidak diperlukan untuk banyak hal. Tetapi kadang-kadang, itu menyelamatkan saya dari keharusan menulis pengamat di salah satu atribut yang diteruskan dari induknya. Misalnya, bagaimana jika kita ingin memulai permintaan ajax jika salah satu atribut berubah? Saya tidak suka menambahkan efek samping semacam itu ke pengambil.

ya, saya sedang memikirkan kasus penggunaan serupa. salah satu contohnya adalah animasi. katakanlah saya ingin memulai animasi ketika collapsed berubah dari true menjadi false dan yang berbeda untuk arah lain. bagaimana cara kerjanya dengan komponen secercah?

@turbo87 @Gaurav0 Ah oke, jadi sistemnya bekerja karena itu semua adalah performance foot guns yang tidak boleh dilakukan di didReceiveAttrs yang sinkron! Apa pun dengan efek samping seperti animasi atau permintaan jaringan harus dijadwalkan dalam kait asinkron seperti didInsertElement atau didUpdate .

didInsertElement sepertinya tidak cocok dengan contoh kasus di atas. didUpdate sepertinya hanya terpicu ketika komponen dirender ulang, benar? tetapi bagaimana jika saya tidak benar-benar menggunakan properti itu untuk rendering? maka komponen tidak akan dirender saat mengubah argumen, jadi didUpdate tidak akan pernah dipanggil. 🤔

Juga didInsertElement melempar kesalahan keras di Glimmer jika kita mengatur sesuatu dan menyebabkan rerender.

@Turbo87 Mungkin ada kesalahpahaman saat didUpdate dipanggil. Kait ini dipanggil ketika properti terlacak yang digunakan dalam template _atau argumen yang diteruskan_ berubah. Lihat contoh ini: http://tinyurl.com/y7osxpy2. Ketika komponen induk meneruskan sebuah argumen, komponen anak mendapatkan pengait didUpdate dipanggil meskipun tidak pernah "menggunakan" argumen itu.

@Gaurav0 Anda memindahkan tiang gawang. ;) Contoh Anda memulai permintaan ajax sebagai tanggapan atas perubahan argumen, yang tidak akan menyebabkan kesalahan jika terjadi di didInsertElement . Jika Anda ingin menyetel properti secara sinkron pada saat yang sama, Anda harus dapat memodelkan perilaku yang sama dengan lazy getter. Jika Anda perlu menyetel properti secara asinkron setelah ajax, misalnya, itu tidak akan memicu kesalahan karena itu akan terjadi di loop peristiwa yang berbeda.

baiklah, sepertinya itu cukup baik untukku. Saya tidak ingin memperluas hal khusus ini, hanya ingin menyoroti bahwa panduan migrasi menyeluruh akan diperlukan :)

@tomdale apakah ini masih mutakhir? tampaknya tidak aktif untuk beberapa waktu.

@tomdale Ok, skenarionya adalah kita perlu memulai permintaan ajax ketika atribut tertentu telah berubah, dan hanya atribut itu. Kami tidak ingin menulis pengamat. Bisakah kita tetap melakukan ini di didInsertElement?

@Gaurav0 Apa yang menyebabkan atribut berubah? Mengikuti DDAU, saya pikir kode apa pun yang bertanggung jawab untuk mengubah data yang menyebabkan atribut berubah juga harus bertanggung jawab untuk memulai permintaan AJAX.

Atribut berubah dari komponen induk/pengontrol. DAU.

@Gaurav0 @tomdale Contoh lain dari kasus penggunaan ini adalah (kebanyakan) set komponen "tanpa DOM" yang menerapkan perubahan pada beberapa "objek" lainnya. Contoh saat ini adalah ember-leaflet/bar -composability-tools yang memungkinkan untuk secara deklaratif membangun isi peta selebaran. Bagi saya ini sepertinya tidak mungkin dalam komponen Glimmer sekarang karena kurangnya fungsionalitas didReceiveAttrs atau observer jenis apa pun. Bagaimana ini akan/dapat ditangani?

@nickschot Saya pikir saya bisa mengambil yang ini, kami benar-benar melakukan audit selama proses desain berbagai add-on komunitas dan kasus penggunaan, dan secara khusus membahas ember-leaflet .

Jika Anda menggali ember-leaflet / ember-composability-tools , Anda akan benar-benar menemukan bahwa itu tidak pernah benar-benar menggunakan didReceiveAttrs atau didUpdate atau kait siklus hidup apa pun seperti ini . Ini sebenarnya hanya membutuhkan kait init dan willDestroy , agar komponen dapat mendaftarkan/membatalkan pendaftaran pada induknya (dan akhirnya, induk render root). Ini dapat dilakukan di hook constructor dan willDestroy di komponen Glimmer. Komponen induk root _does_ memerlukan kemampuan untuk menggunakan didInsertElement , tetapi dapat menggunakannya melalui pengubah {{did-insert}} .

Dalam kasus umum, Anda dapat menggunakan {{did-insert}} / {{did-update}} untuk mencerminkan sejumlah atribut ke elemen DOM. Ini dilacak secara otomatis, jadi mengonsumsi nilai di dalamnya akan menyebabkannya dijalankan kembali jika nilainya berubah. Anda juga dapat menulis pengubah kustom Anda sendiri jika Anda mau, dan mereka juga dapat melakukannya.

Ada kasus penggunaan yang agak terkait, yang tidak mungkin dilakukan dengan komponen Glimmer - "detektor render" seperti ini di addon ember-google-maps . Ini adalah sesuatu yang jauh lebih jarang (kami hanya menemukan satu ini digunakan), dan yang dapat diatasi melalui MutationObserver untuk menonton DOM, atau melalui manajer komponen khusus yang memilih kemampuan untuk menjalankan kait didUpdate / didRender . Saya benar-benar berpikir komponen <RenderDetector> tujuan umum akan menjadi cara yang baik untuk menangani semua kasus penggunaan ini, karena itu akan mengisolasi kemampuan untuk menonton subpohon komponen untuk pembaruan dalam satu komponen yang mudah ditemukan.

@pzuraq sudah lama, tetapi pembaruan kecil di area ini. Untuk kasus penggunaan saya, saya tidak dapat menggunakan pengubah karena tidak ada DOM sama sekali di dalam komponen ini. Apa yang saya lakukan saat ini adalah menerapkan manajer komponen khusus dengan semantik secercah yang mengaktifkan kembali kait didUpdate.

@nickschot rekomendasi kami ada untuk benar-benar menggunakan pembantu. Helper tidak dapat mengembalikan apa pun dan efek samping, mirip dengan useEffect di React jika Anda sudah familiar (sedangkan pengubah lebih mirip dengan useLayoutEffect ).

API pembantu berbasis kelas memang perlu beberapa pekerjaan, idealnya itu harus cocok dengan API pengubah yang dihadapi pengguna akhir saya pikir (ditambah kami perlu memperkenalkan manajer pembantu untuk memungkinkan mendesain ulang sama sekali), tetapi API saat ini harus memberi Anda semua kemampuan yang Anda butuhkan untuk mencapai efek samping yang Anda cari.

@pzuraq Bagaimana cara kerjanya? Apakah kita perlu membuat pembantu khusus atau pembantu apa pun akan melakukannya?

Apakah sintaksnya seperti ini?

{{concat (did-insert this.myAction)}}

Rasanya seperti menyampaikan argumen dan bukan "memodifikasi".

Atau apakah Anda menyarankan agar kita menggunakan pembantu daripada pengubah? Sesuatu seperti:

{{my-did-insert this.myAction}}

Jika ya, harap kirimkan beberapa tautan ke dokumentasi dan contoh. API publik saat ini dari pembantu kelas hanya memiliki compute dan recompute , tidak ada tentang siklus hidup komponen.

Maafkan pertanyaan bodoh, tetapi untuk pengguna Ember biasa masalah ini cukup samar, dokumentasi kurang dan tersebar.

Atau apakah Anda menyarankan agar kita menggunakan pembantu daripada pengubah?

Benar, persis. Anda dapat melihat contohnya di ember-render-helpers misalnya, yang meniru @ember/render-modifiers API.

Secara umum, compute memicu pertama kali helper dimasukkan ke dalam template/dihitung, dan memicu ulang setiap kali argumennya berubah. Itu juga autotrack di 3.13+.

Jika Anda memerlukan sesuatu di sepanjang baris {{did-insert}} , misalnya, Anda dapat melakukan:

export default class didInsert extends Helper {
  compute(fn) {
    if (!this.rendered) {
      fn();
      this.rendered = true;
    }
  }
}

Dan jangan khawatir, saya mengerti bahwa ini mungkin tampak sedikit membuat frustrasi dan samar. Itulah sifat berada di ujung tombak / sisi kenari, kami belum mendapatkan semua pola baru yang didokumentasikan dan kami sedang mengerjakannya

Saya yakin masalah ini dapat diselesaikan sekarang setelah Octane keluar

Apakah halaman ini membantu?
0 / 5 - 0 peringkat