Open mfo opened 2 years ago
pour le sujet IE11 ; on peut instrumenter les comptes via une custom variable matomo afin d'en avoir le coeur net si vous le souhaitez. ex d'implem : https://github.com/betagouv/monstage/blob/staging/app/views/layouts/_analytics.html.erb#L9
Point de détail : je suis pas trop chaud pour intégrer le tag manager de Matomo :
Mais en revanche chaud pour une variable custom mise directement dans le code.
L’arrivée prochaine du chantier des champs conditionnels avec son lot de dynamisme dans les interfaces nous a incités à porter le regard sur notre stack “frontend".
La semaine dernière j’ai travaillé sur l’introduction de turbo dans le projet. Je vous fais ici un retour. Sous le nom turbo
en réalité se cachent plusieurs technologies. Les plus importantes c’est turbo-drive
, turbo-stream
, turbo-frames
et stimulus
. Je vais vous en dire deux mots sur chacune :
turbo-drive
c’est le successeur de turbo-links
mais avec les <form>
en plus. La techno est censée aussi remplacer les data-*
de rails-ujs
. Mais il y a des limitations. Le data-deisable
n’existe plus. On ne peut pas avoir de data-confirm
sans le comportement “remote”. Et l’usage de data-method
sur les link_to
est fortement découragé.
turbo-stream
c’est un format pour envoyer des actions
depuis le serveur vers le client pour appliquer des modifications sur le DOM
. Ça remplace le fait d’envoyer du js
. C’est bien plus propre. Mais officiellement il n’y a pas de moyen d’ajouter des actions custom. Dans une PR je contourne cette limitation et j’ajoute un moyen d’implémenter des actions custom.
turbo-frames
je n’ai pas encore trop touché, mais en théorie c’est comme les <frame>
mais en plus moderne.
stimulus
c’est un framework js qui permet d’annoter le DOM
avec des actions déclaratives. Le principal intérêt c’est que c’est basé sur MutationObsever
et donc pour faire du "sprinkle” ça évite de se baser sur les événements du type after render
. Je me suis servie pour instancier nos composants react
plutôt que de le faire sur les événements.
Le constat que je fais c’est qu’aujourd’hui nous avons beaucoup de manières différentes de coder des interactions dans DS. En introduisant turbo
j’aimerai qu’on en réduise le nombre de techniques et pas qu’on ajoute juste une autre.
Aujourd’hui, nous avons plusieurs techniques en place :
rails-ujs
c’est ce qui permet l’usage des attributs data-remote
, data-confirm
, data-method
, data-params
et data-disable
. Nous avons aussi une extension pour l’usage de data-remote
sur les <input>
avec un data-debounce
.
Nous utiliserons de manière extensive le format :js
dans les réponses serveur pour modifier le DOM
. Nous avons une série de helpers qui nous permet de faire des opérations standards sur les éléments DOM
.
delegate()
: on utilise de manière extensive des “sprinkle" de js via la délégation. On utilise aussi l’événement ds:page:update
pour certaines interactions qu’on ne peut pas faire via la délégation.
DS.*
: C’est un namespace global qui contient des fonctions qu’on utilise via onclick=DS.myAction();
dans les templates.
react
: Nous avons des composants react qu’on instancie via un helper dans les templates react_component(“Mycomponent”, {})
. Ces derniers sont utilisés pour les ilots d'interactivité comme les autocomplets ou la carte. Ils s'en servent souvent d'événement globaux et de requêtes au format :js
pour interagir avec le reste de l'application.
Comme je l'ai expliqué plus haut, turbo
c'est un ensemble de techniques qui peuvent être utilisées de manières différentes. Il y a le dogme officiel DHH/Rails. Tel que je le comprends c'est :
turbo-drive
turbo-frames
autant que possibleturbo-stream
quand vraiment vous ne pouvez pas faire de turbo-frame
stimulus
pour de l'interactivité dans le navigateur ou si toutes les autres approches ne fonctionnent pasIl faut migrer ce que nous avons vers une des techniques de turbo
.
Le plus direct c'est de migrer ujs
+ format :js
vers turbo-drive
+ turbo-stream
. J'ai un peu commencé. La principale difficulté est dans le fait que c'est risquer d'activer turbo-drive
sur l'ensemble d'une vieille app comme la nôtre. Il faut donc activer turbo-drive
au cas par cas en remplaçant data-remote
et data-confirm
par data-turbo
et data-turbo-confirm
. Ce n’est pas trivial, car la logique d'application déclarative n'est pas la même qu'avec ujs
– ce n’est pas l'élément interactif qui doit porter data-turbo=“true”
, mais un de ces parents. Donc relou, mais pas impossible.
Les interactions bindé sur ds:page:update
doivent être supprimé, car turbo-stream
ne sait pas emmètre d'événement à la suite de remplacement de DOM
et incite à utiliser stimulus
qui se base sur les MutationObserver
pour ajouter de l'interactivité. L'approche par délégation continue de fonctionner aussi évidemment. C'est la raison pour laquelle nous avons migré l'instanciation des composants react
vers stimulus
.
L'approche par délégation pose le problème de maintenabilité – il est difficile de savoir quand un snippet n'est plus utilisée. Donc mon souhait sera de s'en éloigner de cette approche. Enfin les onclick
via le namespace DS.*
pose un soucie similaire même s’il est un peu plus facile de trouver les usages du code avec un grep
.
Mon expérience des discussions avec vous c’est que nous avons des sensibilités très différentes vis-à-vis de la manière la plus confortable de faire du frontend. Pour être honnête, ce problème existe aussi pour la partie backend, mais Rails (pour le meilleure et pour le pire) fait un meilleur job de proposer des solutions “clés en main” sur la partie backend. turbo
est clairement une tentative par Basecamp de proposer une solution “clés en main”. Les retours que j'ai pour l’instant c'est que les nouveaux projets alignés sur le dogme DHH se passent raisonnablement bien. Mais dans notre cas on ne peut pas faire l'abstraction de l'existent et l'énergie disponible à mettre dans la migration est limité.
Nous avons besoin de nous aligner dans l’équipe sur ce vers quoi on veut y aller. Je veux bien faire du taff pour migrer, mais j'ai besoin de motivation de votre part. Si je devais faire la migration à la sauce "Tchak" ça ne ressemblerait pas au dogme DHH. Si vous voulez le dogme DHH il faut le dire clairement pour que je sache pourquoi je le fais. Peut-être que ce dogme est finalement la seule solution pour trouver un accord. Car ce que j'entends quand je demande "vous voulez quoi pour faire du frontend ?" on me répond "je veux le code le plus simple possible". Ça ne m'aide pas. Nous n’avons clairement pas la même définition de "simple". Mon "simple” à moi serait de migrer progressivement vers du React
SSR
. Ce n’est clairement pas celui de l’équipe 🤣 C'est normal. J'ai donc besoin de savoir si vous voulez qu'on essaye un tour de table pour arriver à un set de pratiques (autour de turbo
) ou vous pensez qu'on n’arrivera pas à se mettre d'accord et qu'il faut juste appliquer le dogme DHH ?
Pour prendre un exemple – personnellement la partie qui me pose le plus de problèmes dans turbo
c'est stimulus
. Les <button data-controller="toggle" data-action="click->toggle#onClick" data-toggle-value="true" />
je trouve ça imbitable et inmaintenable. Qit à faire du turbo
je préfère un button_to
avec turbo-drive
avec dans un controller
un render turbo_stream: turbo_stream.toggle('.card')
ce qui serait évidemment très controversé. Mais peut être que ce type d'interaction (interaction entre des éléments dans la page) peut être modélisée via les turbo-frame
?
en gros dsl mais je suis en boucle la dessus.
TLDR ; dans le fond : je pense que l'interactivité portée par le JS sur une page web est over rated (ok pour du FB ou des sites qui sont des apps genre, mobile. la on est sur une app metier. un cycle de vie complet client/server me semble être ce qui reste de plus productif/facile/maitrisable tout en étant acceptable par l'utilisateur).
concernant la substance de ma position et ton push :
Je vais apporter une précision. Quand je dis :
Mon "simple” à moi serait de migrer progressivement vers du
React
SSR
remix
sans la partie compilateur/serveur)DS
il y a deux parties :
js
(faire l'effort d'implémenter un flow alternatif au autocomplete par exemple) mais en conditions normales d'usage elle devrait bénéficier de “progressive enhancement”.SPA
pure. Mais avec une techno comme remix
la question ne se pose pas – tout est SSR
. Dans tous les cas, je ne ferai pas l’effort (très couteux) sur cette partie de faire fonctionner l’application en mode dégradé sans js
.remix
la migration serait extrêmement progressive (page par page) avec une technique d’application “ombrelle”.Merci pour ce travail.
Dans mon lointain souvenir ou je faisais du js, on était obligé de modéliser le métier coté back et front, avec des points de frictions entre le 2.
Dans la mesure ou on garde le back en rails, on aura a nouveau ce pb. je suppose qu'il est plus coûteux que l'utilisation de la full stack rails DHH.
@tchak , je n'ai pas bien compris, tu proposes du remix pour une intégration partielle ou tu trouves cela pertinent uniquement pour une spa ?
@LeSim Je ne préconise pas une SPA. Tu à raison que s'il faut modéliser le métier deux fois c'est null. Je vous ai préparé une demo pour illustrer l'usage possible de remix
avec rails
. Oui, ça suppose qu'il y a un serveur node
entre l'app rails
et le navigateur.
Backend : https://github.com/tchak/rails-remix-demo/blob/b6bf791aa73979dff14ef48c943ddae73bffe42e/app/controllers/todos_controller.rb Frontend : https://github.com/tchak/rails-remix-demo/blob/b6bf791aa73979dff14ef48c943ddae73bffe42e/app/frontend/routes/index.tsx
Pour moi l'avantage de cette solution c'est que l'UI est complètement déclarative, dynamique, typée et fonctionne sans JavaScript en dernier recours. Toutes les interactions sont faites via les form
et donc peuvent être envoyées directement vers rails
. Il n'y a pas de “state management” coté JavaScript. La session est gérée par rails
aussi. Le node
ne fait que crasher du HTML et optimise les transitions (si le JavaScript est disponible). Mais les réponses de rails
c’est du json
et des redirects sans aucune “magie”. Ce que j'appelle "optimise les transitions" c'est que si le JavaScript est chargé sur la page les submit se font via fetch
(comme avec turbo
) et le serveur node
intercepte les redirections et retourne une 204 avec un header spécial qui fait que le redirect devient un fetch
en arrière-plan suivi d’un update de l'état de la page.
Et ce que vous pouvez voir aussi dans ma démo c'est le fait que les pages qui ne sont pas implémentées dans remix
sont simplement servies depuis rails
. C'est ça que j'appelle l'application "ombrelle". Donc il est possible de migrer page par page. En théorie la migration c'est de porter une page de haml
vers tsx
et de mettre les variables d'instance dans le controller dans un render json
.
Je pense qu'il faut qu'on ferme cette parenthèse sur les SPA/remix. Quelle que soit ma conviction sur ce qui serait “le monde idéal”, il faut qu'on avance de manière pragmatique.
Je pense que l'usage des view_components
sera un bon pas en avant. J'ai un plan pour faire en sorte que les view_components
puissent utiliser React et Stimulus en fonction des besoins. Si déjà on arrive à ça en se débarrassant de tout le js ad hoc, ça va être top.
Problème précis :
mapbox
. Solution envisagée verslibremap
en v2 [qui a le support ie11]. TODO: faire la migration, bravo @tchak qui est deja dessus #6876webpack
versvite-rails
qui est basé suresbuilt
pour de la compilation rapide (plus des hooks pour étendre le support du JS généré par esbuilt ex ; support IE 11 ?). question, est-ce vraiment plus rapide avec vite-rails ? TODO: vérifier que le build va plus vite -> voir en équipe une fois libremap/typescript rdy (et typescript est conditionné a la facilité d'intégration via webpack)ds:page:update
#7186DS.*
link_to method: *
parbutton_to method:*
afin de pouvoir activerturbo
sur lebody