Bonjour Ă tous!
Ce récent article m'a
incité à écrire cette publication (je l'ai vue hier).
Racontez les principales fonctionnalités de Headless / content-first / api-first, etc. Je ne serai pas CMS, le matériel est plein et probablement beaucoup connaissent déjà cette tendance. Et je veux vous dire pourquoi et comment j'écris mon système, pourquoi je ne pouvais pas choisir parmi les systèmes existants, ce que je pense des autres systèmes que j'ai rencontrés auparavant et quelles perspectives je vois pour tout cela. La fiction sera volumineuse (pour le matériel dans deux ans), mais j'essaierai d'écrire plus intéressant et utile. Peu importe, s'il vous plaît, sous le chat.
En général, l'histoire est vraiment longue et je vais essayer de la raconter en premier. Soit pour clarifier quelles sont les vraies raisons de créer votre propre moteur, soit tout simplement parce que sans cela, il sera difficile sur le terrain d'expliquer pourquoi je le fais de cette façon, et non d'une manière ou d'une autre.
Mais pour commencer, je vais néanmoins brièvement écrire les principaux critères de sélection pour le CMS sans tête moderne, pourquoi je ne pouvais toujours pas choisir une solution toute faite pour moi.
Juste pour que les gens ne s'arrêtent pas pour lire beaucoup de hêtres, ne comprenant pas ce qui finira par être dit.En bref: je voulais que tout soit au même endroit: à la fois l'arrière et l'avant (et non ni ceci ni cela), et l'API GraphQL, et que la base de données soit gérée et bien plus encore, y compris le bouton «Make Beautiful». Je n'ai pas trouvé ça. Moi non plus, je ne l'ai pas encore fait, mais dans l'ensemble, cela s'est avéré beaucoup, et surtout, cela me permet de faire de vrais projets.
Et donc, mon approche peut difficilement être qualifiée de scientifique et justifiée. Le fait est que j'écris généralement très souvent quelque chose qui m'appartient. J'aime programmer ici. Et il y a deux ans (et avant cela encore 8 ans), je me suis assis sur le MODX CMF (sous lequel j'ai aussi inventé beaucoup de mes béquilles). Et pendant trois ans, nous avons lancé un projet assez important, dans le cadre duquel, il me semblait, je pouvais utiliser MODX. Mais il s'est avéré que je ne pouvais pas ... La principale raison était que c'était une startup sans aucune exigence technique, avec un tas d'idées qui changeaient et se complétaient chaque jour (et plusieurs fois par jour). Et maintenant, chaque fois que sous une nouvelle idée, il était nécessaire d'ajouter une nouvelle entité, d'enregistrer / modifier des champs pour ceux existants, de créer / supprimer / modifier des relations entre ces entités (respectivement, avec un changement dans la structure de la base de données), j'ai à un moment donné Il a fallu plusieurs heures pour changer ces entités. En effet, outre le fait qu'il fallait enregistrer ces changements dans le schéma, il fallait changer la base de données (presque manuellement), mettre à jour l'API, réécrire le code du programme, etc., etc. En conséquence, le front a dû être mis à jour dans tout cela. En conséquence, j'ai décidé que nous devrions chercher quelque chose de nouveau, plus pratique, qui simplifierait en quelque sorte tout cela. Je préciserai à nouveau qu'à l'époque j'étais un backend php, alors ne soyez pas surpris et ne riez pas que j'ai commencé à découvrir différents constructeurs frontaux, moins de processeurs, npm, etc. etc. Mais quoi qu'il en soit, peu à peu dans notre projet un front est apparu sur react + less, une API sur GraphQL, et un serveur sur express.
Mais tout n'était pas aussi rose qu'il semblerait à beaucoup maintenant. Permettez-moi de vous rappeler que c'était il y a plus de deux ans. Si vous êtes sur le Web JS moderne depuis moins de deux ans, je vous conseille de lire cet article:
N raisons d'utiliser l'appli Create React (habr). Trop paresseux, bref: avec l'avènement des react-scripts, vous ne pouvez plus vous soucier de la configuration de webpack, etc. Tout cela passe en arrière-plan. Les gars gentils ont déjà configuré le webpack de sorte que la plupart des projets de réaction sont presque garantis de travailler dessus, et le développeur final s'est concentré directement sur la programmation du produit final, plutôt que de configurer un tas de dépendances, de chargeurs, etc. Mais c'est plus tard. Et avant cela, je devais juste configurer ce webpack, suivre la mise à jour du tas de tout ce qui volait avec lui pour rattraper son retard, etc. etc. Mais ce n'est qu'une partie du travail, seulement essentiellement le front. Et vous avez également besoin d'un serveur. Et vous avez également besoin d'une API. Et vous avez également besoin de SSR (rendu côté serveur), que, par ailleurs, react-script ne fournit toujours pas, à ma connaissance. En général, tout était beaucoup plus compliqué à l'époque que maintenant, il n'y en avait pas beaucoup, et tout le monde se débattait du mieux qu'il pouvait. Et comment ai-je béquillé alors ...
Imaginez:
- Configuration native du webpack séparément pour l'avant et le serveur.
- Propre implémentation de SSR, de sorte que async fonctionne normalement avec react-server, et que les styles immédiatement prêts arrivent et soient indexés normalement, et les statuts de serveur pour les pages non trouvées ont été donnés.
- Pas de redux. Eh bien, je n'ai pas aimé redux tout de suite. J'ai aimé l'idée d'utiliser mon flux de réaction natif (même si j'ai dû le réécrire un peu pour moi).
- Schémas et résolveurs GraphQL prescrits manuellement, sans déploiement automatique de la base de données (le serveur API a été utilisé comme intermédiaire pour un site MODX).
- Pas de réactif-apollo / apollo-client, etc. Tout est écrit indépendamment avec des requêtes via fetch, des référentiels dans un navigateur basé sur un flux personnalisé.
En conséquence: jusqu'à présent, l'une des premières versions de celui-ci a un projet avec une fréquentation de 500+, et dans la saison (hiver) 1000-1700 étudiants uniques par jour. Disponibilité 2 mois. En effet, j'ai redémarré manuellement le serveur après une mise à jour préventive du logiciel. Et avant ce redémarrage, la disponibilité était encore de 6 mois et plus. Mais le plus intéressant est la consommation de mémoire. Il y a actuellement près de 700 mégaoctets de processus js. Oui, oui, je ris avec toi ici aussi :) Bien sûr, c'est beaucoup. Et avant cela, j'ai fait un peu de prévention et amélioré cet indicateur. Auparavant, il y avait un total de 1000M + par processus ... Néanmoins, cela fonctionnait et était tout à fait tolérable. Et avant que
Google ne modifie les algorithmes de PageSpeed ​​Insights en novembre, le site avait une mesure de performance 97/100.
PreuveUne conclusion intermédiaire basée sur ce projet basé sur un système qui s'est développé sans ce projet (le projet a été laissé de côté):
Avantages- L'API du projet est devenue plus flexible grâce à l'utilisation de GraphQL, et le nombre de demandes de serveur a été considérablement réduit.
- Le projet a accès à un grand nombre de composants sur npm.
- La gestion de projet est devenue plus transparente grâce à l'utilisation des dépendances, git, etc.
- Les scripts et les styles dispersés sont certainement plus agréables qu'un tas de scripts séparés sur les anciens sites, lorsque vous ne savez pas ce que vous pouvez supprimer de ce zoo sans conséquences (et vous voyez souvent plusieurs versions d'un bogue sur un site).
- Le site est devenu plus interactif, les pages fonctionnent sans redémarrage, le retour aux pages précédemment consultées ne nécessite pas d'appels répétés au serveur.
- L'édition des données a lieu directement sur la page, sur le principe de "éditer ce que vous voyez et où vous voyez", sans panneau d'administration séparé.
Inconvénients (principalement pour le développeur)- Tout est très compliqué. Vraiment. Il est tout simplement irréaliste de connecter un développeur tiers au projet. Je pouvais moi-même à peine comprendre quoi et comment cela fonctionne et d'où mes jambes poussent. Si vous regardez la page 3 des avantages, où il est dit à propos de la transparence, alors la transparence est seulement en ce que si vous accrochez quelque chose quelque part, vous pouvez immédiatement voir ce qui est cassé (les scripts ne se construisent pas, etc.), mais en validant et en différant Vous pouvez trouver où cela a accroché. Eh bien, si vous avez réussi à ajouter quelque chose de nouveau et que cela fonctionne, au moins vous comprenez clairement que, oui, tout s'est bien passé. Mais dans l’ensemble, c’est toujours un enfer infernal.
- Difficultés de mise en cache. Plus tard, j'ai découvert apollo-client pour moi. Et avant cela, comme je l'ai dit, j'ai écrit mes stockages basés sur les flux. En raison de ces stockages, il était possible d'obtenir les données nécessaires pour le rendu à partir de différents composants, mais le volume de cache côté client était très important (chaque ensemble d'entités typiques avait son propre référentiel). En conséquence, il était difficile de vérifier si l'objet avait été demandé plus tôt ou non (c'est-à -dire, cela vaut-il la peine de faire une demande au serveur pour le trouver), si toutes les données associées sont disponibles, etc.
- Difficultés avec les schémas, la structure de la base de données et les résolveurs (fonctions API pour recevoir / modifier des données). Comme je l'ai dit, j'ai écrit des schémas manuellement, et des résolveurs aussi. Dans quoi, dans les résolveurs, j'ai essayé de fournir la mise en cache et le traitement des demandes imbriquées et d'autres subtilités. À ce moment, j'ai dû aller très loin dans l'essence et le code du programme GraphQL. L'avantage est que je comprends généralement assez bien comment GraphQL fonctionne, quels sont ses avantages et ses inconvénients, et comment mieux le cuisiner. L'inconvénient est que, bien sûr, vous ne pouvez pas écrire toutes ces commodités et petits pains écrits par des commandes comme apollo en un. Du coup, quand j'ai découvert apollo, bien sûr, j'ai commencé à utiliser leurs composants avec grand plaisir (mais surtout à l'avant, je vais vous expliquer pourquoi ci-dessous).
En général, ce projet utilisant des technologies obsolètes est personnellement le mien à 100%, donc je peux me permettre de l'abandonner jusqu'à des temps meilleurs. Mais il y a d'autres projets pour lesquels j'ai dû aller plus loin et développer la plateforme. Et plusieurs fois, j'ai dû tout réécrire à partir de zéro. De plus, je parlerai plus en détail des tâches individuelles que j'ai rencontrées et des solutions que j'ai développées et appliquées en conséquence.
Schéma d'abord. D'abord le circuit, puis tout le resteUn site (interface web, client léger, etc.) est tout l'affichage d'informations (enfin, la gestion de l'information, si elle est autorisée et la fonctionnalité le permet). Mais d'abord, tout de même, une base de données (tables, colonnes, etc.). Ayant rencontré plusieurs approches différentes pour travailler avec la base de données sur mon chemin, j'ai le plus apprécié l'approche Schema-first. Autrement dit, vous décrivez le schéma des entités et des types de données (manuellement ou via l'interface), déployez le schéma et vous appliquez immédiatement les modifications décrites à la base de données (les tables / colonnes sont créées / supprimées, ainsi que les relations entre elles). Selon l'implémentation, vous générerez également toutes les fonctions de résolveur nécessaires à la gestion de ces données. Surtout dans cette direction, j'ai aimé le projet
prisma.io .
Avec votre permission, puisque même sur le hub je n'ai pas vu un seul article sur le prisme, j'attirerai un peu l'attention sur eux, car le projet est vraiment très intéressant, et sans eux je n'aurais pas maintenant une telle plateforme qui me rendait si heureux . En fait, c'est pourquoi j'ai appelé ma plateforme prisma-cms, car prisma.io y joue un très grand rôle.
En fait, prisma.io est un projet SaaS, mais avec une grosse mise en garde: ils mettent presque tout ce qu'ils font sur un github. Autrement dit, vous pouvez utiliser leurs serveurs pour un prix très raisonnable (et configurer votre propre base de données et API pour vous-même en quelques minutes), ou vous pouvez tout déployer entièrement à la maison. Dans ce cas, le prisme doit être logiquement divisé en deux parties distinctes importantes:
- Prisma-server, c'est-à -dire le serveur sur lequel tourne également la base de données.
- Prisma-client. C'est essentiellement aussi un serveur, mais par rapport à la source de données (prisma-server) c'est un client.
Maintenant, je vais essayer d'expliquer cette situation confuse. En général, l'essence du prisme est qu'en utilisant un seul point de terminaison API, vous pouvez travailler avec différentes sources de données. Oui, ici, tout le monde dira qu'ils ont tous créé GraphQL et que le prisme n'est pas nécessaire ici. En général, tout le monde aura raison, mais il y a un point grave: GraphQL ne définit que les principes et le travail global, mais en soi, il ne fournit pas de travail avec les sources de données finales de la boîte. Il dit: "Vous pouvez créer une API pour décrire les demandes que les utilisateurs peuvent envoyer, mais la façon dont vous gérez ces demandes est à vous de déranger." Et le prisme utilise également, bien sûr, GraphQL (à propos, et beaucoup d'autres choses, y compris divers produits apollo). Mais le prisme plus à cela ne fait que travailler avec la base de données. Autrement dit, décrivant le schéma et son déploiement, les tables et colonnes nécessaires (ainsi que les relations entre elles) seront immédiatement créées dans la base de données spécifiée, et généreront immédiatement toutes les fonctions CRUD nécessaires. Autrement dit, avec un prisme, vous n'obtenez pas seulement un serveur GraphQL, mais une API de travail à part entière qui vous permet immédiatement de travailler avec la base de données. Ainsi, Prisma-server fournit une base de données et une interaction avec elle, et prisma-client vous permet d'écrire vos résolveurs et d'envoyer des requêtes à prisma-server (ou ailleurs, même pour quelques prisma-servers). Et il s'avère que vous ne pouvez déployer prisma-client que par vous-même (et SaaS prisma.io sera utilisé comme prisma-server), et vous pouvez déployer prisma-server par vous-même, et en général en aucun cas dépendre d'un prisme, c'est tout le vôtre.
Ici, j'ai choisi un prisme pour moi, comme base de ma plate-forme. Mais j'ai dû le faire tourner pour obtenir une plate-forme complète.
1. Schémas de fusion
À cette époque, le prisme n'était pas en mesure de combiner les circuits. Autrement dit, la tâche est la suivante:
Vous avez un modèle utilisateur décrit dans un module
type User { id: ID! @unique username: String! @unique email: String @unique }
et dans un autre module
type User { id: ID! @unique username: String! @unique firstname: String lastname: String }
Dans le cadre d'un projet, vous souhaitez combiner ces deux schémas automatiquement pour obtenir la sortie
type User { id: ID! @unique username: String! @unique email: String @unique firstname: String lastname: String }
Mais alors ce prisme ne pouvait pas faire. Il s'est avéré implémenter cela en utilisant la
bibliothèque merge-graphql-schemas .
Travailler avec un serveur prismatique arbitraire.
Dans le prisme, la configuration est écrite dans un fichier de configuration spécial. Si vous souhaitez modifier l'adresse du serveur à prismes utilisé, vous devez modifier le fichier. Une bagatelle, pas agréable. Je voulais rendre l'URL possible à spécifier dans la commande, par exemple endpoint = http: // endpoint-address yarn deploy (yarn start). Cela a été tué pendant plusieurs jours ... Mais maintenant, vous pouvez utiliser un projet de prisme pour un nombre illimité de points de terminaison. Soit dit en passant, jusqu'à présent, prisma-cms fonctionne facilement, même avec une base de données locale, même avec des serveurs à prisme SaaS.
Modules / Plugins
Ce n'était généralement pas suffisant. Comme je l'ai dit, la tâche principale du prisme est de fournir du travail avec diverses bases de données. Et ils font un excellent travail. Déjà , ils prennent en charge le travail avec MySQL, PostgreSQL, Amazon RDS et MongoDB, plusieurs autres types de sources en cours de route. Mais ils ne fournissent aucune infrastructure modulaire. Il n'y a jusqu'à présent aucun marché ou quelque chose comme ça. Il n'y a que quelques blancs typiques. Mais vous ne pouvez pas choisir deux ou trois de plusieurs blancs et installer sur un seul projet. Nous devrons en choisir un. Je voulais qu'il soit possible d'installer un nombre différent de modules sur le projet final, et que lors du déploiement des circuits et des résolveurs, ils se réjouissent et obtiennent un projet unique avec la fonctionnalité totale. Et bien qu'il n'y ait pas encore d'interface graphique, il existe déjà plus de deux douzaines de modules et composants fonctionnels qui peuvent être combinés sur le projet final. Ici, je vais immédiatement décider un peu des définitions personnelles: un module est ce qui est installé à l'arrière (extension de la base de données et de l'API), et un composant est ce qui est installé à l'avant (pour ajouter divers éléments d'interface). Jusqu'à présent, il n'y a pas d'interface graphique pour connecter les modules, mais ce n'est pas difficile pour moi d'écrire de cette façon (ce n'est pas souvent fait):
constructor(options = {}) { super(options); this.mergeModules([ LogModule, MailModule, UploadModule, SocietyModule, EthereumModule, WebrtcModule, UserModule, RouterModule, ]); }
Après avoir ajouté de nouveaux modules, il suffit de refaire un déploiement avec une seule commande et c'est tout, nous avons déjà de nouvelles tables / colonnes et des fonctionnalités augmentées.
5 frontaux, sensibles aux changements dans le backend
Ce n'était pas suffisant du tout. Cela sera suivi d'une digression. Le fait est que tous les CMS API-first que j'ai vus disent "Nous sommes géniaux de fournir l'API, et vous vissez le devant que vous voulez." C'est ce qu'ils "vissent tout ce que vous aimez" signifie en fait "dérangez comme vous le souhaitez." Exactement la même chose que les cadres d'interface utilisateur disent, "regardez quels boutons sympas nous sommes et faites tout cela, et confondez-vous avec le backend vous-même". Ça tuait toujours. Je voulais juste trouver un CMS complet écrit en javascript, en utilisant GraphQL et en fournissant à la fois l'arrière et l'avant. Mais je n'en ai pas trouvé un comme ça. Je voulais vraiment que les changements d'API soient immédiatement perçus à l'avant. Et pour cela, plusieurs sous-étapes ont été réalisées:
5.1 Génération de fragments d'API
A l'avant, des fragments du fichier de schéma sont enregistrés dans les requêtes. Lorsque l'API est reconstruite sur le serveur, un nouveau fichier JS avec des fragments d'API est également généré. Et dans les demandes, c'est écrit comme ceci:
const { UserNoNestingFragment, EthAccountNoNestingFragment, NotificationTypeNoNestingFragment, BatchPayloadNoNestingFragment, } = queryFragments; const userFragment = ` fragment user on User { ...UserNoNesting EthAccounts{ ...EthAccountNoNesting } NotificationTypes{ ...NotificationTypeNoNesting } } ${UserNoNestingFragment} ${EthAccountNoNestingFragment} ${NotificationTypeNoNestingFragment} `; const usersConnection = ` query usersConnection ( $where: UserWhereInput $orderBy: UserOrderByInput $skip: Int $after: String $before: String $first: Int $last: Int ){ objectsConnection: usersConnection ( where: $where orderBy: $orderBy skip: $skip after: $after before: $before first: $first last: $last ){ aggregate{ count } edges{ node{ ...user } } } } ${userFragment} `;
5.2 Un contexte pour tous les composants
React 16.3 introduit une
nouvelle API de contexte . Je l'ai fait pour que dans les composants enfants à n'importe quel niveau, il soit possible d'accéder à un seul contexte sans répertorier les types précédemment souhaités à partir du contexte, mais simplement en indiquant statique contextType = PrismaCmsContext et en obtenant tous les charmes via ce contexte -> (y compris le client API, le schéma , demandes, etc.).
5.3 filtres dynamiques
Je voulais aussi vraiment. GraphQL vous permet de créer des requêtes complexes avec une structure imbriquée. Je voulais aussi que les filtres soient dynamiques, formés à partir du schéma API et nous permettent de créer des conditions imbriquées. Voici ce qui s'est passé:
5.4 Générateur de site Web
Et enfin, ce qui me manquait, c'était un éditeur de site externe, c'est-à -dire un concepteur. Je voulais que le serveur n'ait qu'un minimum d'actions à effectuer, et toute la conception finale devrait être faite à l'avant (y compris la configuration du routage, la génération des sélections, etc.). C'est un sujet pour un article séparé, car entre autres, j'ai également écrit mon éditeur wysiwyg crutchy sur pure contentEditable, et il y a beaucoup de subtilités. Si je retrouve mes droits et qui sera intéressé, j'écrirai un article séparé.
Enfin, une courte vidéo de démonstration du designer en action. Toujours assez brut, mais j'aime ça.
Je vais terminer là -dessus. Je n'ai pas encore écrit grand-chose, ce que j'aimerais écrire, mais tant de choses se sont passées. Je me ferai un plaisir de commenter.
PS: tous les codes source, y compris les codes source du site lui-mĂŞme,
sont ici .