Este problema de búsqueda rastrea la implementación de los componentes de Glimmer en Ember.js.
Los componentes de Glimmer.js tienen las siguientes características:
tagName
, attributeBindings
, etc.)@
, como {{@firstName}}
this.args
@tracked
propiedades<AngleBracket />
…attributes
De acuerdo con el espíritu de incrementalismo de Ember, queremos aterrizar esta funcionalidad pieza por pieza a través de un complemento. Esto permite que la comunidad comience a usar funciones y a proporcionar comentarios al principio del proceso.
De hecho, ya hemos comenzado el camino hacia los componentes Glimmer en Ember. Las dos primeras características ya han comenzado a aterrizar:
template-only-glimmer-components
).@
(por ejemplo, {{@firstName}}
).Este problema propone terminar el proceso de traer componentes de Glimmer a Ember al permitir que los complementos proporcionen implementaciones de componentes alternativos, y luego transformar el paquete @glimmer/component
en un complemento de Ember que implemente la API del componente Glimmer.js.
Dividiremos ese trabajo en fases, cada una de las cuales desbloqueará beneficios para las aplicaciones y complementos existentes de Ember. La fase 0 trata de agregar las primitivas necesarias a Ember.js para admitir implementaciones de componentes alternativos. Las fases 1, 2 y 3 tratan sobre la habilitación incremental de la API del componente Glimmer.js.
Mientras profundizamos en las Fases 0 y 1, postergaremos la exploración de los detalles técnicos de las fases posteriores hasta que las primeras estén más cerca de completarse.
TL; DR: agregue una API CustomComponentManager a Ember.js para permitir que los complementos implementen la API de componentes personalizados.
Actualmente, se supone que todos los componentes de una aplicación Ember son subclases de Ember.Component
. Para admitir API de componentes alternativos en Ember, necesitamos alguna forma de decirle a Ember cuándo y cómo debería cambiar el comportamiento de los componentes.
Cuando decimos "comportamiento de componentes personalizados", nos referimos específicamente a:
Si bien Glimmer VM introduce el concepto de "administrador de componentes", un objeto que toma estas decisiones, esta API es de muy bajo nivel. Sería prematuro adoptarlos directamente como API pública en Ember porque son difíciles de escribir, fáciles de crear de una manera que rompe otros componentes y aún no son estables.
En su lugar, proponemos una nueva API de Ember llamada CustomComponentManager
que implementa un patrón de delegado. El CustomComponentManager
proporciona un área de superficie de API más pequeña que el API de Glimmer VM de ComponentManager
completo, que permite a los autores de complementos caer en un "pozo del éxito".
Entonces, ¿cómo sabe Ember qué administrador de componentes usar para un componente determinado? La iteración original del RFC de componentes personalizados introdujo el concepto de ComponentDefinition
, una estructura de datos que se registró con entusiasmo con Ember y especificó qué administrador de componentes usar.
Uno de los principales beneficios del enfoque ComponentDefinition
es que la resolución del administrador de componentes puede ocurrir en el momento de la compilación. Desafortunadamente, eso significa que tenemos que diseñar una API para exactamente cómo se registran, y probablemente significa algún tipo de integración con la canalización de compilación.
En su lugar, proponemos una API para configurar el administrador de un componente en tiempo de ejecución mediante una anotación en la clase del componente. Este paso incremental permite que el trabajo en administradores de componentes personalizados continúe mientras se diseña una solución a más largo plazo.
En esta iteración, los componentes deben optar explícitamente por los administradores de componentes alternativos. Lo hacen a través de una función especial componentManager
, exportada por Ember, que anota en tiempo de ejecución qué administrador de componentes debe usarse para una clase de componente en particular:
import { componentManager } from '@ember/custom-component-manager';
import EmberObject from '@ember/object';
export default componentManager(EmberObject.extend({
// ...
}), 'glimmer');
Eventualmente, esto podría convertirse en un decorador de clases:
import { componentManager } from '@ember/custom-component-manager';
export default @componentManager('glimmer') class {
// ...
}
La primera vez que se invoca este componente, Ember inspecciona la clase para ver si tiene una anotación de administrador de componentes personalizada. Si es así, usa el valor de la cadena para realizar una búsqueda en el contenedor. En el ejemplo anterior, Ember pediría al contenedor el objeto con la clave del contenedor component-manager:glimmer
.
Por lo tanto, los complementos pueden usar la semántica de resolución normal para proporcionar administradores de componentes personalizados. Nuestro complemento de componentes Glimmer puede exportar un administrador de componentes desde addon/component-managers/glimmer.js
que se descubrirá automáticamente a través de las reglas de resolución normales.
Si bien esta API es detallada y no particularmente ergonómica, las aplicaciones y los complementos pueden abstraerla introduciendo su propia clase base con la anotación. Por ejemplo, si un complemento llamado turbo-component
quisiera proporcionar un administrador de componentes personalizado, podría exportar una clase base como esta:
// addon/index.js
import EmberObject from '@ember/object';
import { componentManager } from '@ember/custom-component-manager';
export default componentManager(EmberObject.extend({
// ...
}), 'turbo');
Los usuarios de este complemento pueden subclasificar la clase base TurboComponent
para definir componentes que usen el administrador de componentes correcto:
import TurboComponent from 'turbo-component';
export default TurboComponent.extend({
didInsertElementQuickly() {
// ...
}
});
Ningún componente es una isla y, por razones de compatibilidad con versiones anteriores, es importante que la introducción de una nueva API de componentes no rompa los componentes existentes.
Un ejemplo de esto es la API de jerarquía de vistas existente. Los componentes Ember pueden inspeccionar su componente principal a través de la propiedad parentView
. Incluso si el padre no es Ember.Component
, los componentes secundarios de Ember deberían tener una propiedad parentView
no nula.
Actualmente, el CurlyComponentManager
en Ember es responsable de mantener este estado, así como otro "estado de alcance" ambiental como el objetivo de las acciones.
Para evitar que los administradores de componentes mal implementados violen invariantes en el sistema existente, usamos un patrón de composición para personalizar el comportamiento mientras ocultamos las esquinas afiladas de la API subyacente.
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) {
}
});
La clase base Component
en Ember admite una larga lista de características, muchas de las cuales ya no se utilizan mucho. Estas funciones pueden imponer un costo de rendimiento, incluso cuando no se utilizan.
Como primer paso, queremos proporcionar una forma de participar en la API simplificada del componente Glimmer.js a través del paquete @glimmer/component
. Para facilitar la migración, proporcionaremos una implementación de la clase base Glimmer Component
que hereda de Ember.Object
en @glimmer/component/compat
.
A continuación, se muestra un ejemplo de cómo se ve un "componente 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>
Características destacables de estos componentes:
actions
.args
lugar de establecer propiedades individuales en el componente directamente.Igual de importante es lo que no está incluido:
@tracked
propiedades layout
o template
en el componente.childViews
, parentView
, nearestWithProperty
, etc.tagName
, attributeBindings
u otro DSL JavaScript personalizado para modificar el elemento raíz.send
o sendAction
para enviar eventos.ember-view
clase obligatoria o ID de elemento guid generado automáticamente.rerender()
manual.attrs
(use args
lugar).this.$()
para crear un objeto jQuery para el elemento del componente.appendTo
de componentes manuales en el DOM.willInsertElement
didRender
willRender
willClearRender
willUpdate
didReceiveAttrs
didUpdateAttrs
parentViewDidChange
on()
para los eventos del ciclo de vida de los componentes; los ganchos deben implementarse como métodos.Un efecto secundario interesante de este conjunto de características es que encaja con el esfuerzo de habilitar clases de JavaScript . Junto con el diseño propuesto en el RFC de clases de ES , podemos proporcionar una implementación alternativa del componente anterior:
// 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);
}
});
La fase 2 permite invocar componentes mediante corchetes angulares ( <UserAvatar @user={{currentUser}} />
) además de curlies ( {{my-component user=currentUser}}
). Debido a que esta sintaxis elimina la ambigüedad entre los argumentos de los componentes y los atributos HTML, esta función también permite "distribuir" los atributos pasados en la plantilla del componente a través de …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}} />
Esto generaría una salida similar a la siguiente:
<div aria-expanded="true">
<h1>Hello, Steven!</h1>
</div>
La fase 3 habilita las propiedades rastreadas a través del decorador @tracked
en Ember. Se están resolviendo los detalles de la interoperabilidad entre el modelo de objetos de Ember y las propiedades rastreadas. Una vez que las propiedades rastreadas aterrizan, los usuarios podrán soltar el módulo @glimmer/component/compat
y usar la clase base del componente normal, que no es Ember.Object.
Junto con la función "autotrack" recientemente fusionada (que infiere las dependencias de propiedades calculadas automáticamente), esto debería resultar en una mayor simplificación del código de la aplicación:
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;
}
}
¿Puedo volver a agregar cosas como el nombre de clase ember-view
o el atributo id
generado automáticamente a los componentes de Glimmer para que sean compatibles con CSS existente?
Si. Ejemplo:
<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);
}
}
custom-component-manager
sucursalCustomComponentManager
detrás de una marca de funcióntracked
sucursal@tracked
y objetos EmberUsaremos las listas a continuación para realizar un seguimiento del trabajo en curso. A medida que aprendamos más durante la implementación, agregaremos o eliminaremos elementos de la lista.
glimmer-custom-component-manager
marca componentManager
{ componentManager } from '@ember/custom-component-manager'
CustomComponentManager
APICustomComponentManagerDelegate
version
create()
getContext()
update()
destroy?()
didCreate?()
didUpdate?()
getView?()
version
al momento de la creaciónchildViews
y parentView
en componentes existentessparkles-components
componentManager
anotación cuando se consume desde EmberCustomComponentManager
debería ser detectable a través del contenedor de Ember@glimmer/component
como complemento de Emberimport Component from '@glimmer/component'
proporciona una clase base de JavaScript simpleimport Component from '@glimmer/component/compat'
proporciona Ember.Object
clase basecreate(injections)
didInsertElement()
willDestroy()
didUpdate()
click()
etc.) no deben activarsethis.bounds
this.element
alias de propiedad calculado en this.bounds
this.args
está disponible en el constructorthis.args
se actualiza antes de que se llame a didUpdate
this.args
no debería activar un ciclo de renderizado infinito (es necesario verificarlo)args
create()
estático) como clases de componentes? El protocolo para inicializar un componente parece simple pero sorprendentemente complicado.CustomComponentManager
?CustomComponentManager
versiones ¡Coordinaremos el trabajo en el canal # st-glimmer-components en la comunidad de Ember Slack si estás interesado en ayudar! Los merodeadores también son bienvenidos si solo tienes curiosidad por saber cómo se hace la salchicha.
Con respecto a este;
{{! src/ui/components/UserAvatar/template.hbs }}
<div ...attributes> {{! <-- attributes will be inserted here }}
<h1>Hello, {{@firstName}}!</h1>
</div>
No entiendo por qué se usan llaves para denotar variables dinámicas ( {{@firstName}}
), pero no los atributos salpicados. Para mí, visualmente parece que <div ...attributes>
es lo que se va a renderizar. Por coherencia, ¿no sería mejor que <div {{...attributes}}>
?
¿Existen RFC para componentes brillantes en los que podamos discutir cómo deberían verse y funcionar?
@tomdale, el problema anterior menciona la documentación del modo de compatibilidad de componentes, pero no parece mencionar la documentación de una guía de migración. por ejemplo, me sorprendió bastante que didReceiveAttrs()
ya no existiera, y sería genial tener alguna documentación que muestre cómo migrar casos de uso comunes a nuevos patrones de mejores prácticas.
@dbbk La razón principal por la que no requerimos {{...attributes}}
es porque no es sintácticamente ambiguo con un atributo normal, como los otros casos en los que requerimos curlies, y cuatro caracteres menos parecían más fáciles de escribir y menos ruidosos visualmente.
Creo que también puede hacer que la gente asuma que attributes
es una propiedad dentro del alcance, pero no lo es; no puede hacer <SomeComponent something={{attributes}} />
, por ejemplo, lo que sugiere esa sintaxis. También implica más fuertemente que la sintaxis de propagación funciona en otras posiciones cuando no lo hace.
Ha habido alguna sugerencia de adoptar @arguments
como un enlace de plantilla especial y agregar la sintaxis de extensión ...
en Handlebars, pero eso necesita un diseño e implementación que no está en la hoja de ruta inmediata. No me sorprendería del todo si en el futuro reemplazáramos ...attributes
por {{...@attributes}}
o algo así.
@ Gaurav0 La API del componente Glimmer pasaría por el proceso RFC antes de habilitarse de forma predeterminada en las aplicaciones Ember, en caso de que alguna vez quisiéramos hacerlo. Una cosa buena sobre el enfoque del administrador de componentes personalizados es que no canonizamos una API de "próxima generación", pero podemos permitir que diferentes diseños compitan por sus méritos a través del ecosistema de complementos.
@ Turbo87 Gran sugerencia,
Abordaré el caso de didReceiveAttrs()
específicamente desde que lo mencionaste. Por lo general, la gente usa este gancho para inicializar el estado del componente basándose en argumentos pasados, pero en la práctica esto significa que puede hacer un poco de cosas innecesarias en estos ganchos, por ejemplo, para inicializar valores que nunca terminan usándose. Y, por supuesto, la gente termina haciendo cosas impactantes en estos ganchos que están en el camino de la renderización.
La alternativa es cambiar de la inicialización "basada en empuje" a la inicialización "basada en extracción". Por ejemplo, si quisiera hacer algún cálculo para generar o inicializar el valor de una propiedad de componente, en su lugar usaría una propiedad calculada rastreada. Esto garantiza que el trabajo solo se realice para los valores que realmente se utilizan. Ejemplo:
En lugar de esto:
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}`;
})
});
Hacer esto:
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}`;
}
}
Admitiré que era escéptico de esto al principio, pero didReceiveAttrs
que no se podría modelar con propiedades calculadas más eficientes.
@tomdale Estoy de acuerdo en que didReceiveAttrs
no es necesario para la mayoría de las cosas. Pero a veces, me ha salvado de tener que escribir un observador en uno de los atributos transmitidos por el padre. Por ejemplo, ¿qué pasaría si quisiéramos iniciar una solicitud ajax si uno de los atributos cambia? Odiaría agregar ese tipo de efecto secundario a un captador.
sí, estoy pensando en casos de uso similares. un ejemplo es la animación. digo que quiero iniciar una animación cuando collapsed
cambia de true
a false
y una diferente para la otra dirección. ¿Cómo funcionaría eso con componentes de luz tenue?
@ turbo87 @ Gaurav0 Ah, está bien, entonces el sistema está funcionando porque todos esos son pistolas de alto rendimiento que no deberían ejecutarse en didReceiveAttrs
que es sincrónico! Todo lo que tenga efectos secundarios, como animaciones o solicitudes de red, debe programarse en enlaces asíncronos como didInsertElement
o didUpdate
.
didInsertElement
no parece encajar bien en el caso de ejemplo anterior. didUpdate
solo parece activarse cuando el componente se vuelve a generar, ¿correcto? pero ¿qué pasa si realmente no uso esa propiedad para renderizar? entonces el componente no se volvería a procesar al cambiar el argumento, por lo que nunca se llamaría didUpdate
. 🤔
Además, didInsertElement
arroja un error grave en Glimmer si configuramos algo y provocamos una repetición.
@ Turbo87 Puede haber una idea errónea sobre cuándo se llama a didUpdate
. Este gancho se invoca cuando una propiedad rastreada usada en la plantilla _o un argumento pasado_ cambia. Vea este ejemplo: http://tinyurl.com/y7osxpy2. Cuando el componente padre pasa un argumento, el componente hijo recibe su gancho didUpdate
llamado incluso si nunca "usó" ese argumento.
@ Gaurav0 Estás moviendo los postes de la portería. ;) Su ejemplo estaba iniciando una solicitud ajax en respuesta a un cambio de argumento, que no provocaría que se lanzara un error si sucediera en didInsertElement
. Si desea establecer una propiedad sincrónicamente al mismo tiempo, debería poder modelar el mismo comportamiento con captadores perezosos. Si necesita establecer una propiedad de forma asincrónica después del ajax, por ejemplo, eso no activará el error porque sucederá en un bucle de eventos diferente.
bien, eso me parece suficientemente bueno. No quería ampliar este tema específico de todos modos, solo quería resaltar que se necesitarán guías de migración completas :)
@tomdale, ¿esto todavía está actualizado? parece estar inactivo durante algún tiempo.
@tomdale Ok, el escenario es que necesitamos iniciar una solicitud ajax cuando un atributo en particular ha cambiado, y solo ese atributo. No queremos escribir un observador. ¿Todavía podemos hacer esto en didInsertElement?
@ Gaurav0 ¿Qué está provocando que el atributo cambie? Siguiendo a DDAU, creo que cualquier código que sea responsable de cambiar los datos que hacen que el atributo cambie también debería ser responsable de iniciar la solicitud AJAX.
El atributo está cambiando del componente principal / controlador. DDAU.
@ Gaurav0 @tomdale Otro ejemplo de este caso de uso es un conjunto de componentes (en su mayoría) "sin DOM" que aplican cambios a algún otro "objeto". Un ejemplo actual sería ember-leaflet / ember-composability-tools, que permite construir declarativamente el contenido de un mapa de folletos. Para mí, esto no parece posible en los componentes de Glimmer en este momento debido a la falta de cualquier tipo de funcionalidad didReceiveAttrs
o observer
. ¿Cómo se abordaría o se podría abordar esto?
@nickschot Creo que puedo tomar este, de hecho hicimos una auditoría durante el proceso de diseño de varios complementos de la comunidad y casos de uso, y cubrimos específicamente ember-leaflet
.
Si profundiza en ember-leaflet
/ ember-composability-tools
, en realidad encontrará que nunca usa didReceiveAttrs
o didUpdate
o cualquier gancho de ciclo de vida como este . En realidad, solo necesita los ganchos init
y willDestroy
, para que los componentes se registren / cancelen el registro en su padre (y, en última instancia, en el padre de representación raíz). Esto se puede hacer en los ganchos constructor
y willDestroy
en los componentes de Glimmer. El componente principal raíz _si_ necesita la capacidad de usar didInsertElement
, pero puede usarlo a través del modificador {{did-insert}}
.
En el caso general, puede usar {{did-insert}}
/ {{did-update}}
para reflejar cualquier número de atributos en elementos DOM. Estos se rastrean automáticamente, por lo que consumir un valor en ellos hará que se vuelvan a ejecutar si los valores cambian. También puede escribir sus propios modificadores personalizados si lo desea, y ellos también podrán hacerlo.
Hay un caso de uso que está algo relacionado, que no es posible con los componentes de Glimmer: un "detector de renderizado" como este en el complemento ember-google-maps
. Esto es algo que es mucho menos común (solo encontramos este uso), y que se puede abordar a través de MutationObserver
para ver el DOM, o mediante un administrador de componentes personalizados que opta por la capacidad de ejecutar los ganchos didUpdate
/ didRender
. De hecho, creo que un componente <RenderDetector>
propósito general sería una buena manera de manejar todos estos casos de uso, ya que aislaría la capacidad de observar el subárbol del componente para las actualizaciones en un solo componente fácil de encontrar.
@pzuraq ha pasado un tiempo, pero una pequeña actualización en esta área. Para mi caso de uso, no puedo usar modificadores ya que no hay DOM dentro de estos componentes. Lo que he hecho actualmente es implementar un administrador de componentes personalizado con semántica glimmer que vuelve a habilitar el gancho didUpdate.
@nickschot nuestra recomendación es utilizar un ayudante. Los ayudantes no pueden devolver nada y efectos secundarios, similar a useEffect
en React si está familiarizado (mientras que los modificadores son más similares a useLayoutEffect
).
La API de ayuda basada en clases necesita algo de trabajo, idealmente debería coincidir con la API modificadora de cara al usuario final, creo (además, necesitamos introducir administradores de ayuda para que sea posible rediseñarla), pero la API actual debería darte todo las habilidades que necesita para lograr los efectos secundarios que busca.
@pzuraq ¿Cómo funciona? ¿Necesitamos crear un ayudante personalizado o cualquier ayudante servirá?
¿Es la sintaxis así?
{{concat (did-insert this.myAction)}}
Se siente como pasar un argumento y no "modificar".
¿O estás sugiriendo que deberíamos usar ayudantes en lugar de modificadores? Algo como:
{{my-did-insert this.myAction}}
En caso afirmativo, publique algunos enlaces a documentación y ejemplos. La API pública actual del asistente de clase solo tiene compute
y recompute
, nada sobre el ciclo de vida del componente.
Disculpe las preguntas estúpidas, pero para los usuarios habituales de Ember este asunto es bastante críptico, la documentación es escasa y está dispersa.
¿O estás sugiriendo que deberíamos usar ayudantes en lugar de modificadores?
Exactamente. Puede ver un ejemplo de esto en ember-render-helpers, por ejemplo, que imita la API @ember/render-modifiers
.
En general, compute
activa la primera vez que se inserta un ayudante en la plantilla o se calcula, y se vuelve a activar cada vez que cambia alguno de sus argumentos. También realiza un seguimiento automático en 3.13+.
Si necesita algo como {{did-insert}}
, por ejemplo, puede hacer:
export default class didInsert extends Helper {
compute(fn) {
if (!this.rendered) {
fn();
this.rendered = true;
}
}
}
Y no se preocupe, entiendo que puede parecer un poco frustrante y críptico. Esa es la naturaleza de estar a la vanguardia / el lado canario de las cosas, aún no hemos documentado todos los nuevos patrones y estamos trabajando en ello 🙂
Creo que este problema se puede cerrar ahora que Octane salió 😄
Comentario más útil
@dbbk La razón principal por la que no requerimos
{{...attributes}}
es porque no es sintácticamente ambiguo con un atributo normal, como los otros casos en los que requerimos curlies, y cuatro caracteres menos parecían más fáciles de escribir y menos ruidosos visualmente.Creo que también puede hacer que la gente asuma que
attributes
es una propiedad dentro del alcance, pero no lo es; no puede hacer<SomeComponent something={{attributes}} />
, por ejemplo, lo que sugiere esa sintaxis. También implica más fuertemente que la sintaxis de propagación funciona en otras posiciones cuando no lo hace.Ha habido alguna sugerencia de adoptar
@arguments
como un enlace de plantilla especial y agregar la sintaxis de extensión...
en Handlebars, pero eso necesita un diseño e implementación que no está en la hoja de ruta inmediata. No me sorprendería del todo si en el futuro reemplazáramos...attributes
por{{...@attributes}}
o algo así.@ Gaurav0 La API del componente Glimmer pasaría por el proceso RFC antes de habilitarse de forma predeterminada en las aplicaciones Ember, en caso de que alguna vez quisiéramos hacerlo. Una cosa buena sobre el enfoque del administrador de componentes personalizados es que no canonizamos una API de "próxima generación", pero podemos permitir que diferentes diseños compitan por sus méritos a través del ecosistema de complementos.
@ Turbo87 Gran sugerencia,
Abordaré el caso de
didReceiveAttrs()
específicamente desde que lo mencionaste. Por lo general, la gente usa este gancho para inicializar el estado del componente basándose en argumentos pasados, pero en la práctica esto significa que puede hacer un poco de cosas innecesarias en estos ganchos, por ejemplo, para inicializar valores que nunca terminan usándose. Y, por supuesto, la gente termina haciendo cosas impactantes en estos ganchos que están en el camino de la renderización.La alternativa es cambiar de la inicialización "basada en empuje" a la inicialización "basada en extracción". Por ejemplo, si quisiera hacer algún cálculo para generar o inicializar el valor de una propiedad de componente, en su lugar usaría una propiedad calculada rastreada. Esto garantiza que el trabajo solo se realice para los valores que realmente se utilizan. Ejemplo:
En lugar de esto:
Hacer esto:
Admitiré que era escéptico de esto al principio, pero
didReceiveAttrs
que no se podría modelar con propiedades calculadas más eficientes.