J'écris ceci principalement pour moi à l'avenir, afin d'avoir un endroit où me référer lorsque quelqu'un me demande pourquoi je suis sceptique à propos des composants Web et pourquoi Svelte n'est pas compilé en composants Web par défaut. (Cependant, il peut être compilé en composants Web, ainsi qu'intégré avec eux, comme en témoigne l'excellente cote sur Custom Elements Everywhere ).
Aucun des éléments suivants ne doit être interprété comme une critique du travail acharné effectué sur les composants Web. J'ai peut-être aussi fait des erreurs dans cette publication, auquel cas je serais heureux de modifier. Je ne déclare pas non plus que vous ne devez pas utiliser de composants Web. Ils ont leur propre portée. J'explique juste pourquoi ils ne me conviennent pas.
1. Amélioration progressive
Cela peut être une croyance à l'ancienne, mais je crois que les sites Web devraient fonctionner sans JavaScript autant que possible. Les composants Web sans JS ne fonctionnent pas. Ceci est normal pour les éléments intrinsèquement interactifs, tels que les éléments de formulaire personnalisés (<cool-datepicker>), mais ce n'est pas normal pour la navigation sur le site, par exemple. Ou imaginez un composant <twitter-share>
qui encapsule la logique de construction d'une URL à envoyer à Twitter . Je pourrais l' implémenter sur Svelte , ce qui me rend le HTML suivant sur le serveur:
<a target="_blank" noreferrer href="..." class="svelte-1jnfxx"> Tweet this </a>
En d'autres termes, l'habituel <a>
dans toute sa splendeur disponible.
Lorsque JavaScript est activé, une amélioration progressive se produit - au lieu d'ouvrir un nouvel onglet, une petite fenêtre contextuelle s'ouvre. Mais même sans JS, le composant fonctionne toujours bien.
Dans le cas d'un composant Web HTML, il ressemblerait à ceci:
<twitter-share text="..." url="..." via="..."/>
... qui est inutile et impropre à l'utilisation si JS est bloqué, ou pour une raison quelconque est cassé, ou l'utilisateur a un ancien navigateur.
De plus, class="svelte-1jnfxx"
nous offre une encapsulation de style sans Shadow DOM. Ce qui nous amène au point suivant.
2. CSS dans, euh ... JS
Si vous souhaitez utiliser le Shadow DOM pour encapsuler des styles, vous devrez insérer votre CSS dans la <style>
. La seule façon pratique de le faire, si vous voulez éviter de charger le contenu clignotant (FOUC), est d'incorporer CSS en tant que chaîne en JavaScript qui définit le reste de la logique de votre composant Web.
Cela contredit le conseil d'amélioration des performances qui se lit comme suit: "moins de JavaScript, s'il vous plaît." La communauté CSS-in-JS, en particulier, a été beaucoup critiquée pour ne pas utiliser de fichiers CSS pour CSS, et nous voici à nouveau avec des composants Web.
À l'avenir, nous pourrons utiliser des modules CSS ainsi que des feuilles de style constructibles pour résoudre ce problème. Nous aurons également la possibilité de styliser les éléments internes du DOM Shadow via ::theme
et ::part
. Mais ce n'était pas sans problèmes ici.
C'est une couronne douloureuse pour moi - j'ai annoncé ces choses comme "The Future" pendant plusieurs années, mais afin de suivre le présent, nous avons dû remplir la plate-forme avec un tas de fonctionnalités différentes, élargissant l'écart entre les navigateurs.
Au moment de la rédaction de cet article, sur https://crbug.com , l'outil de suivi des bogues de Chrome, 61 000 bogues ouverts qui montrent l'énorme complexité de l'écriture d'un navigateur moderne.
Chaque fois que nous ajoutons une nouvelle fonctionnalité à la plate-forme, nous augmentons la complexité - créons le potentiel de nouveaux bogues et rendons moins probable que Chrome aura un nouveau concurrent. Cela crée également des difficultés pour les développeurs qui sont encouragés à apprendre ces nouvelles fonctionnalités (dont certaines, telles que les importations HTML ou la version originale de la norme Custom Elements, n'ont pas pris racine en dehors de Google et sont maintenant en cours de suppression).
4. Polyphiles
Le fait que vous ayez besoin d'utiliser des polyfichiers pour prendre en charge des navigateurs plus anciens ne contribue pas au développement de la situation. Et cela n'aide pas du tout que les articles sur les feuilles de style constructibles écrits dans Google (salut Jason!) Ne mentionnent pas que cette fonctionnalité n'est disponible que dans Chrome. (Les trois auteurs de la spécification travaillent pour Google. Webkit semble avoir des doutes sur certains aspects de cette norme).
5. Composition
Il peut être utile de contrôler quand le contenu d'un slot doit être rendu. Imaginez que vous disposez d' <html-include>
pour charger du contenu supplémentaire lorsqu'il est visible:
<p>Toggle the section for more info:</p> <toggled-section> <html-include src="./more-info.html"/> </toggled-section>
Tout d'un coup! Même si nous n'avons pas encore ouvert la toggled-section
more-info.html
, mais le navigateur a déjà demandé more-info.html
, ainsi que toutes les images et autres ressources qui s'y trouvent.
En effet, le contenu des emplacements est rendu à l' avance dans les composants Web. En réalité, il s'avère que dans la plupart des cas, vous souhaitez rendre le contenu des slots paresseusement. Svelte v2 a adopté un modèle de redning proactif pour répondre aux normes Web, mais c'était la principale source d'inconvénients - nous ne pouvions pas créer quelque chose de similaire au React Router, par exemple. Dans Svelte v3, nous nous sommes éloignés du comportement des composants Web et n'avons jamais regardé en arrière.
Malheureusement, c'était l'une des caractéristiques fondamentales des DOM. Ce qui nous amène à ...
6. Confusion entre propriétés et attributs
Les propriétés et les attributs sont fondamentalement la même chose, non?
const button = document.createElement('button'); button.hasAttribute('disabled');
Enfin, presque:
typeof button.disabled;
Il y a des noms qui ne correspondent pas:
div = document.createElement('div'); div.setAttribute('class', 'one'); div.className;
... et il y a ceux qui ne sont pas du tout convenus:
input = document.createElement('input'); input.getAttribute('value');
Mais nous pourrions traiter ces bizarreries, l'interaction du format de chaîne (HTML) et du DOM. Il existe un nombre fini de ces fonctionnalités, elles sont documentées, afin que nous puissions au moins en savoir plus, si nous avons le temps et la patience.
Les composants Web font la différence. Il n'y a plus de garantie sur la relation entre les propriétés et les attributs, et vous, en tant que développeur de composants Web, devez prendre en charge les deux. Ce qui nous amène à une telle chose:
class MyThing extends HTMLElement { static get observedAttributes() { return ['foo', 'bar', 'baz']; } get foo() { return this.getAttribute('foo'); } set foo(value) { this.setAttribute('foo', value); } get bar() { return this.getAttribute('bar'); } set bar(value) { this.setAttribute('bar', value); } get baz() { return this.hasAttribute('baz'); } set baz(value) { if (value) { this.setAttribute('baz', ''); } else { this.removeAttribute('baz'); } } attributeChangedCallback(name, oldValue, newValue) { if (name === 'foo') {
Vous pouvez faire le contraire - les getters et setters attributeChangedCallback
appellent. Dans tous les cas, la commodité de travailler avec elle est tout simplement déprimante. Dans le même temps, il existe un moyen simple et sans ambiguïté dans les frameworks de transférer des données vers un composant.
7. Conception qui fuit
Cet élément est un peu vague, mais il me semble étrange que attributeChangedCallback
soit juste une méthode de classe. Vous pouvez littéralement faire ce qui suit:
const element = document.querySelector('my-thing'); element.attributeChangedCallback('w', 't', 'f');
Les attributs n'ont pas changé, mais le code se comporte comme s'il s'était produit. Bien sûr, il y a toujours eu de nombreuses façons de nuire à JavaScript, mais quand je vois un détail d'implémentation ressortir de cette façon, il me semble que quelque chose ne va pas dans la conception.
8. Mauvais DOM
Ok, nous avons déjà établi que le DOM est mauvais. Mais il est toujours difficile d'exagérer à quel point il est gênant de créer des applications interactives.
Il y a quelques mois, j'ai écrit un article, «Write less code», pour illustrer comment Svelte peut écrire des composants plus efficacement que des frameworks comme React et Vue. Il n'y avait aucune comparaison avec le DOM vanille, mais le devrait. En bref, nous avons un composant simple <Adder a={1} b={2}/>
:
<script> export let a; export let b; </script> <input type="number" bind:value={a}> <input type="number" bind:value={b}> <p>{a} + {b} = {a + b}</p>
C’est tout. Maintenant, écrivez la même chose via le composant Web:
class Adder extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); this.shadowRoot.innerHTML = ` <input type="number"> <input type="number"> <p></p> `; this.inputs = this.shadowRoot.querySelectorAll('input'); this.p = this.shadowRoot.querySelector('p'); this.update(); this.inputs[0].addEventListener('input', e => { this.a = +e.target.value; }); this.inputs[1].addEventListener('input', e => { this.b = +e.target.value; }); } static get observedAttributes() { return ['a', 'b']; } get a() { return +this.getAttribute('a'); } set a(value) { this.setAttribute('a', value); } get b() { return +this.getAttribute('b'); } set b(value) { this.setAttribute('b', value); } attributeChangedCallback() { this.update(); } update() { this.inputs[0].value = this.a; this.inputs[1].value = this.b; this.p.textContent = `${this.a} + ${this.b} = ${this.a + this.b}`; } } customElements.define('my-adder', Adder);
Ouais.
Notez que si nous modifions simultanément a
et b
, nous aurons alors deux mises à jour distinctes. La plupart des frameworks ne souffrent pas de ce problème.
9. Noms mondiaux
Je ne m'y attarderai pas très longtemps; il suffit de dire que les dangers de travailler dans un même espace de noms partagé sont connus et démontés depuis longtemps.
10. Tous ces problèmes ont déjà été résolus.
La plus grande tristesse est que nous avons déjà de bons modèles de composants. Nous apprenons encore, mais la tâche de base - synchroniser la vue avec un certain état en mettant à jour le DOM dans un style orienté composant - a déjà été résolue il y a plusieurs années. Et nous ajoutons toujours des fonctionnalités à la plateforme Web juste pour rattraper ce que nous avons déjà dans les bibliothèques et les frameworks.
Puisque nos ressources ne sont pas infinies, le temps passé sur une tâche signifie un manque d'attention à une autre tâche. Une énergie considérable a été dépensée pour les composants Web, malgré l'indifférence générale des développeurs. Que pourrions-nous réaliser en dépensant cette énergie pour autre chose?