CSS est l'élément évolutif le plus lent dans le domaine du front-end. En raison de la popularité rapide de l'ES2015 / 2016 et du développement rapide d'outils tels que Babel / Webpack, CSS a été loin derrière et est progressivement devenu un point douloureux pour l'ingénierie de projets à grande échelle. C'est également devenu un problème qui doit être résolu avant que le front-end ne se dirige vers une modularisation complète.
Il existe de nombreuses solutions modulaires CSS, mais il existe deux catégories principales. La première consiste à abandonner complètement le CSS et à utiliser JS ou JSON pour écrire des styles. Radium , jsxstyle , réagissent de style appartiennent à cette catégorie. L'avantage est qu'il peut fournir à CSS la même modularité puissante que JS; l'inconvénient est qu'il ne peut pas utiliser le préprocesseur (ou postprocesseur) CSS mature Sass / Less / PostCSS, :hover
et le :active
traitement de pseudo-classe est compliqué. L'autre consiste à toujours utiliser CSS, mais à utiliser JS pour gérer les dépendances de style, qui représentent les modules CSS . Les modules CSS peuvent maximiser la combinaison de l'écologie CSS existante et des capacités de modularisation JS, et l'API est concise pour un coût d'apprentissage presque nul. Des JS et CSS séparés sont toujours compilés lors de leur publication. Cela ne dépend pas de React, tant que vous utilisez Webpack, vous pouvez l'utiliser dans Vue / Angular / jQuery. C'est la meilleure solution modulaire CSS à mon avis. Récemment, il a été largement utilisé dans les projets, et les détails et les idées dans la pratique sont partagés ci-dessous.
Quels problèmes la modularisation CSS a-t-elle rencontrés?
La modularité CSS est importante pour résoudre deux problèmes: l'importation et l'exportation de styles CSS. Importez à la demande de manière flexible pour réutiliser le code; lors de l'exportation, vous devez être en mesure de masquer la portée interne pour éviter la pollution mondiale. Sass / Less / PostCSS et d'autres ont tenté de résoudre le problème de la faible capacité de programmation CSS. En conséquence, ils ont fait un excellent travail, mais cela n'a pas résolu le problème le plus important de la modularité. L'ingénieur Facebook Vjeux a d' abord soulevé une série de problèmes liés aux CSS rencontrés dans le développement de React. Avec mon opinion personnelle, le résumé est le suivant:
- Global pollution
CSS utilise un mécanisme de sélection global pour définir les styles, ce qui a l'avantage de faciliter la réécriture des styles. L'inconvénient est que tous les styles sont globalement efficaces et que les styles peuvent être écrasés par des erreurs, ce qui entraîne des tableaux de comptage de poids de sélecteurs très laids!important
, même intégrés!important
et complexes , qui augmentent la probabilité de faire des erreurs et le coût d'utilisation. Le Shadow DOM dans le standard Web Components permet de résoudre complètement ce problème, mais son approche est un peu extrême: le style est complètement localisé, ce qui rend impossible la réécriture externe du style et perd de sa flexibilité. - Confusion de dénomination
En raison du problème de la pollution globale, afin d'éviter les conflits de style lors du développement collaboratif à plusieurs personnes, les sélecteurs deviennent de plus en plus complexes et il est facile de former différents styles de dénomination, ce qui est difficile à unifier. Avec plus de styles, la dénomination deviendra plus confuse. - Gestion des dépendances incomplète Les
composants doivent être indépendants les uns des autres. Lorsqu'un composant est introduit, seuls les styles CSS dont il a besoin doivent être introduits. Mais la pratique actuelle est d'introduire son CSS en plus de JS, et il est difficile pour Saas / Less de compiler un CSS séparé pour chaque composant, et l'introduction de CSS pour tous les modules cause du gaspillage. La modularité de JS est très mature, c'est une bonne solution si JS peut gérer les dépendances CSS. Webpackcss-loader
fournit cette fonctionnalité. - Impossible de partager les variables. Les
composants complexes doivent utiliser JS et CSS pour traiter les styles ensemble, ce qui entraînera la redondance de certaines variables dans JS et CSS. Sass / PostCSS / CSS, etc. ne permettent pas de partager des variables entre JS et CSS. - Compression de code incomplète
En raison de l'incertitude du réseau mobile, la compression CSS a maintenant atteint un niveau anormal. De nombreux outils de compression convertissent «16px» en «1pc» afin d'économiser un octet. Mais je ne peux rien faire avec des noms de classe très longs, et la force n'est pas utilisée sur la lame.
Le problème ci-dessus ne peut pas être résolu si seul CSS lui-même est utilisé. Si CSS est géré via JS, il est très facile à résoudre. Par conséquent, la solution donnée par Vjuex est complètement CSS en JS , mais cela équivaut à abandonner complètement CSS. Lors de l'écriture de CSS dans la syntaxe Object, on estime que tous les amis que je viens de voir ont été choqués. Jusqu'à l'apparition des modules CSS.
Schéma modulaire des modules CSS
Les modules CSS utilisent ICSS pour résoudre les deux problèmes d'importation et d'exportation de style. Correspondent aux :import
et :export
deux nouveaux pseudo-classes respectivement.
: import ("chemin / vers / dep.css") { localAlias: keyFromDep; / * ... * / } : export { ExportKey: ExportValue; / * ... * / }
Mais la programmation en utilisant directement ces deux mots-clés est trop lourde. Ils sont rarement utilisés directement dans les projets réels. Ce dont nous avons besoin est la capacité de gérer CSS avec JS. Après la combinaison avec Webpack css-loader
, vous pouvez définir des styles dans CSS et les importer dans JS.
Activer les modules CSS
// webpack.config.js css? modules & localIdentName = [nom] __ [local] - [hash: base64: 5]
L'ajout consiste modules
à l'activer, localIdentName
c'est-à-dire à définir la règle de dénomination du style généré.
/ * components / Button.css * / .normal {/ * tous les styles liés à normal * /} .disabled {/ * tous les styles liés à désactivé * /}
/ * composants / Button.js * / importer des styles depuis './Button.css'; console.log (styles); buttonElem.outerHTML = `<button class = $ {styles.normal}> Soumettre </button>`
Le HTML généré est
<button class = "button - normal-abc53"> Soumettre </button>
Notez button--normal-abc53
que les modules CSS suivent le localIdentName
nom de classe généré automatiquement. Parmi eux se abc53
trouve le code de séquence généré selon l'algorithme donné. Après une telle obfuscation, le nom de la classe est fondamentalement unique, ce qui réduit considérablement la probabilité de couverture de style dans le projet. Dans le même temps, modifiez les règles dans l'environnement de production pour générer un nom de classe plus court, ce qui peut améliorer le taux de compression du CSS.
Le résultat imprimé par la console dans l'exemple ci-dessus est:
Objet { normal: 'bouton - normal-abc53', désactivé: 'bouton - désactivé-def884', }
Les modules CSS gèrent tous les noms de classe en CSS et utilisent des objets pour enregistrer la correspondance entre la classe d'origine et la classe obfusquée.
Grâce à ces processus simples, les modules CSS ont atteint les points suivants:
- Tous les styles sont locaux, ce qui résout les conflits de noms et les problèmes de pollution globale
- La configuration flexible des règles de génération de nom de classe peut être utilisée pour compresser les noms de classe
- Il suffit de se référer au JS du composant pour obtenir tous les JS et CSS du composant
- Toujours CSS, coût d'apprentissage presque nul
Style par défaut partiel
Après avoir utilisé des modules CSS, cela équivaut à en ajouter un à chaque nom de classe :local
pour obtenir la localisation du style. Si vous souhaitez passer en mode global, utilisez celui correspondant :global
.
.normal { color: green; } / * Ce qui précède est équivalent à ce qui suit * / : local (.normal) { color: green; } / * Définit le style global * / : global (.btn) { color: red; } / * Définissez plusieurs styles globaux * / : global { .link { color: green; } .box { color: yellow; } }
Composez pour combiner les styles
Pour la réutilisation de style, les modules CSS fournissent uniquement le seul moyen de traiter: composes
combinaison
/ * components / Button.css * / .base {/ * tous les styles courants * /} .normal { compose: base; / * normal autres styles * / } .disabled { compose: base; / * désactivé les autres styles * / }
importer des styles depuis './Button.css'; buttonElem.outerHTML = `<button class = $ {styles.normal}> Soumettre </button>`
Le HTML généré devient
<button class = "button - base-daf62 button - normal-abc53"> Soumettre </button>
En raison .normal
de la composition au milieu .base
, normal deviendra deux classes après la compilation.
Les compositions peuvent également combiner des styles dans des fichiers externes.
/ * settings.css * / .primary-color { color: # f40; } / * components / Button.css * / .base {/ * Tous les styles courants * /} .primary { compose: base; compose: primaire- couleur de './ settings.css'; / * principaux autres styles * / }
Pour la plupart des projets, composes
Sass / Less / PostCSS n'est plus nécessaire. Mais si vous souhaitez l'utiliser, car il composes
ne s'agit pas d'une syntaxe CSS standard, une erreur sera signalée lors de la compilation. Vous ne pouvez utiliser que la propre syntaxe du préprocesseur pour la réutilisation du style.
compétences de dénomination de classe
La convention de dénomination des modules CSS est étendue à partir de BEM. BEM divise les noms de style en 3 niveaux, à savoir:
- Bloc: nom de module correspondant, tel que Dialog
- Élément: correspondant au nom du nœud Bouton de confirmation dans le module
- Modificateur: correspondant à l'état du nœud, tel que désactivé, mettez en surbrillance
En résumé, BEM a finalement obtenu le nom de la classe dialog__confirm-button--highlight
. Le double symbole __
et --
est utilisé pour le distinguer du séparateur entre les mots du bloc. Bien que cela puisse paraître étrange, BEM a été adopté par de nombreux grands projets et équipes. Nous reconnaissons également cette méthode de dénomination dans la pratique.
Le nom du fichier CSS dans les modules CSS correspond exactement au nom du bloc, il vous suffit donc de prendre en compte l'élément et le modificateur. La façon dont BEM correspond aux modules CSS est:
/ * .dialog.css * / .ConfirmButton - désactivé { }
Vous ne pouvez pas non plus suivre la convention de dénomination complète et utiliser camelCase pour associer Block et Modifier:
/ * .dialog.css * / .disabledConfirmButton { }
Comment réaliser le partage de variables CSS et JS
Remarque: Il n'y a pas de concept de variables dans les modules CSS. Les variables CSS ici font référence à des variables dans Sass.
Les :export
mots - clés mentionnés ci-dessus peuvent générer des variables en CSS vers JS. Ce qui suit montre comment lire les variables Sass dans JS:
/ * config.scss * / $ couleur-primaire: # f40; : export { primaryColor: $ primary-color; }
/ * app.js * / style d'importation de 'config.scss'; // 会 输出 # F40 console.log (style.primaryColor);
Conseils sur les modules CSS
Les modules CSS soustraient les CSS existants. Dans un souci de simplicité et de contrôle , l'auteur suggère de suivre les principes suivants:
- N'utilisez pas de sélecteurs, utilisez uniquement les noms de classe pour définir les styles
- Ne pas mettre en cascade plusieurs classes, n'utiliser qu'une seule classe pour définir tous les styles
- Tous les styles sont
composes
combinés pour obtenir une réutilisation - Non imbriqué
Les deux principes ci-dessus équivalent à affaiblir la partie la plus flexible du style, ce qui est difficile à accepter pour les nouveaux utilisateurs. Le premier n'est pas difficile à pratiquer, mais le second, si l'état du module est trop élevé, le nombre de classes augmentera de façon exponentielle.
Sachez que ce qui précède est appelé une recommandation car les modules CSS ne vous obligent pas à le faire. Cela semble contradictoire: comme la plupart des projets CSS ont un héritage historique profond, trop de restrictions entraînent une augmentation des coûts de migration et des coûts de coopération avec des parties externes. Il doit y avoir des compromis lors de l'utilisation initiale. Heureusement, les modules CSS le font très bien:
Et si j'utilise plusieurs classes pour un élément?
Pas de problème, le style fonctionne toujours.
Comment puis-je utiliser une classe du même nom dans un fichier de style?
Pas de problème, bien que ces classes portant le même nom puissent être compilées avec des codes aléatoires, elles ont toujours le même nom.
Que faire si j'utilise des pseudo-classes, des sélecteurs de balises, etc. dans le fichier de style?
Pas de problème, tous ces sélecteurs ne seront pas convertis et apparaîtront intacts dans le css compilé. En d'autres termes, les modules CSS ne transformeront que les styles liés au nom de la classe et au nom du sélecteur d'identifiant.
Mais notez que les trois «si» ci-dessus ne devraient pas se produire autant que possible.
Modules CSS combinés à la pratique React
En className
utilisation css directement au class
nom.
/ * dialog.css * / .root {} .confirm {} .disabledConfirm {}
importer des noms de classe à partir de 'noms de classe' importer des styles depuis './dialog.css'; exporter la classe par défaut La boîte de dialogue étend React.Component { render () { const cx = classNames ({ [styles.confirm]:! this.state.disabled, [styles.disabledConfirm]: this.state.disabled }); return <div className = {styles.root}> <a className={cx}> Confirmer </a> ... </div> } }
Notez qu'en général, le nom de classe correspondant au nœud le plus à l'extérieur du composant est root
. La classnames bibliothèque est utilisée ici pour manipuler les noms de classe.
Si vous ne voulez pas taper fréquemment styles.**
, vous pouvez essayer react-css-modules , qui utilise des fonctions de haut niveau pour éviter les entrées répétées styles.**
.
Modules CSS combinés aux pratiques de projet historiques
Une bonne solution technique est non seulement puissante et cool, mais aussi capable de migrer en douceur des projets existants. Les modules CSS sont très flexibles à ce stade.
Comment remplacer les styles locaux à l'extérieur
Lorsqu'un nom de classe confus est généré, le conflit de dénomination peut être résolu, mais comme le nom de classe final ne peut pas être prédit, il ne peut pas être écrasé par un sélecteur général. Notre pratique actuelle de projet consiste à ajouter des data-role
attributs aux nœuds clés du composant , puis à remplacer le style via le sélecteur d'attribut.
Tel que
// dialog.js renvoie <div className = {styles.root} data-role = 'dialog-root'> <a className={styles.disabledConfirm} data-role='dialog-confirm-btn'> Confirmer </ a > ... </div>
// dialog.css [data-role = "dialog-root"] { // style de remplacement }
Étant donné que les modules CSS ne transforment que les sélecteurs de classe, il n'est pas nécessaire d'ajouter des sélecteurs d'attributs ici :global
.
Comment coexister avec des styles globaux
Le projet frontal introduira inévitablement normalize.css ou d'autres fichiers css globaux. L'utilisation de Webpack permet aux styles globaux et aux styles locaux des modules CSS de coexister harmonieusement. Voici le code de configuration du webpack utilisé dans notre projet:
module: { loaders: [{ test: /\.jsx?$/, loader: 'babel' }, { test: /\.scss$/, exclude: path.resolve (__ dirname, 'src / styles'), loader : 'style! css? modules & localIdentName = [nom] __ [local]! sass? sourceMap = true' }, { test: /\.scss$/, incluez: path.resolve (__ dirname, 'src / styles'), chargeur : 'style! css! sass? sourceMap = true' }] }
/ * src / app.js * / import './ styles / app.scss'; import Component from './ view / Component ' / * src / views / Component.js * / // Les styles suivants sont liés aux composants import ' ./Component.scss ';
La structure des répertoires est la suivante:
src
├── app.js
├── styles
│ ├── app.scss
│ └── normalize.scss
└── views
├── Component.js
└── Component.scss
De cette manière, tous les styles globaux peuvent y src/styles/app.scss
être importés. Tous les autres src/views
styles inclus dans le catalogue sont partiels.
Pour résumer
Les modules CSS sont une bonne solution aux problèmes de modularisation rencontrés actuellement par CSS. Il peut être utilisé en conjonction avec Sass / Less / PostCSS, etc., et peut tirer pleinement parti de l'accumulation de technologie existante. Dans le même temps, il peut être associé de manière flexible au style global, ce qui est pratique pour la migration progressive vers les modules CSS dans le projet. L'implémentation des modules CSS est également légère et peut être migrée à faible coût une fois que des solutions standard seront disponibles à l'avenir. Si vous rencontrez des problèmes similaires dans votre produit, cela vaut la peine d'essayer.