Il y a 7 ans -
Temps de lecture 6 minutes
Webpack : choisir SASS ou LESS ?
Le rôle de Webpack
Comme expliqué dans ce précédent article (Webpack + ES6 + Babel + Angular1.x), Webpack nous permet de modulariser le code Javascript. Ce n’est toutefois pas là son seul argument puisqu’il permet aussi de disposer de toutes les ressources statiques de notre projet (CSS, images, polices de caractères) en tant que module.
Plus besoin de task-runner comme Gulp ou Grunt, il suffit d’inclure la ressource dans un des fichiers Javascript pour que la ressource soit ajoutée au « pack » (aussi appelé bundle) généré par Webpack.
Exemple en EcmaScript 5
[js]require(‘../path/to/my/style.css’);[/js]
Exemple en EcmaScript 6
[js]import ‘./myImage.png’;[/js]
Nous allons nous intéresser à l’une de ces ressources statiques, la feuille CSS, à travers un exemple. Voyons tout d’abord une configuration simple de Webpack faite en ES6 permettant de charger les feuilles de style.
Configuration Webpack
[js]import webpack from ‘webpack’;
const DIRNAME = __dirname;
export default {
context: DIRNAME,
entry: [
DIRNAME + ‘/app.js’
],
output: {
path: DIRNAME + ‘dist’,
filename: ‘bundle.js’
},
module: {
loaders: [
{ test: /\.css$/, loaders: [‘style’, ‘css’]) }
]
}
};[/js]
./style.css
[css].my-class{
text-align: center;
}[/css]
./app.js
[js]/* … */
import ‘./style.css’;
/* … */[/js]
Le css-loader va ajouter au fichier bundle.js le contenu du ./style.css
importé et le style-loader ajoutera la balise <link rel="stylesheet">
référençant l’ensemble des CSS du pack.
Dossier output
[java]dist
|_ bundle.js[/java]
bundle.js
[js]Webpack generated code…
/*!********************************!*\
!*** ./~/css-loader!./style.css ***!
\**********************************/
/***/ function(module, exports, __webpack_require__) {
exports = module.exports = __webpack_require__(/*! ./../~/css-loader/lib/css-base.js */ 12)();
// imports
// module
exports.push([module.id, ".my-class{text-align: center;}"]);
// exports
/***/ },
Webpack generated code…[/js]
Pré-processeurs
Si l’on souhaite utiliser un pré-processeur comme SASS ou LESS les étapes seront les mêmes que pour n’importe quelle ressource, à savoir:
- Installer le loader adéquat
[js]npm install sass-loader
// ou
npm install less-loader[/js] - Ajouter le loader à la configuration de Webpack
[js]{ test: /\.scss$/, loaders: [‘style’, ‘css’, ‘sass’]}
// ou
{ test: /\.less$/, loaders: [‘style’, ‘css’, ‘less’]}[/js] - Importer les feuilles de style dans le code Javascript
[js]import ‘../path/to/styles.scss’;
// ou
import ‘./styles.less’;[/js] - Lancer Webpack afin qu’il puisse packager ces ressources
En dehors des différences inhérentes aux pré-processeurs il faut savoir qu’utiliser l’un ou l’autre avec Webpack n’est pas sans conséquence. Nous allons donc définir quelles sont ces contraintes afin de faciliter le choix entre SASS ou LESS.
Webpack et SASS
Avantage : la ré-écriture d’URL
L’intégration de SASS avec Webpack profite de la popularité du pré-processeur: le projet sur Github (https://github.com/jtangelder/sass-loader) est plus avancé que son concurrent. Néanmoins comme SASS ne fournit pas de ré-écriture d’url, un loader supplémentaire devra être installé (resolve-url-loader).
Prenons par exemple le cas suivant: nous souhaitons utiliser une image enregistrée localement comme background d’une classe CSS. Il est même possible de passer par une variable.
Répertoire du projet
[java]projet
|_ app.js
|_ index.html
|_ assets
| |_ fonts
| |_ images
| |_ image1.png
| |_ image2.png
|_ style.scss
|_ dist[/java]
./style.scss
[js]@path-to-images: ‘./assets/images/’;
.my-class{
background: url(@path-to-images + ‘image1.png’) no-repeat;
/* le loader resolve-url va transformer le chemin
* ‘./assets/images/image1.png’ en ‘/img/image1.png’
*/
}[/js]
Voici maintenant la configuration permettant au loader file-loader de copier les images png dans le répertoire du bundle Webpack (dist).
Loaders de Webpack
[js]…
module: {
loaders: [
{ test: /\.scss$/, loaders: [‘style’, ‘css’, ‘resolve-url’, ‘sass’]) },
{ test: /\.png$/, loader: "file-loader?name=img/[name].png"}
/* Tous les fichiers png seront copiés vers l’url
* ./dist/img/<nom-fichier>.png
*/
]
}
…[/js]
Dossier output
[java]dist
|_ bundle.js
|_ img
| |_ image1.png
| |_ image2.png[/java]
Les images sont désormais accessibles par le bundle au chemin /img
, néanmoins sass-loader ne peut pas remplacer les URL automatiquement. C’est donc le loader resolve-url-loader qui s’en chargera.
Inconvénient : les multiples imports
Le plus gros désavantage de SASS concerne les multiples imports : un fichier commonStyle.scss sera par exemple importé dans plusieurs autres feuilles de style.
./commonStyles.scss
[js].commonClass{
…
}[/js]
[js]@import ‘./commonStyles.scss’;
.firstClass{
.commonClass;
…
}[/js]
./style2.scss
[js]@import ‘./commonStyles.scss’;
.anotherClass{
.commonClass;
…
}[/js]
Chaque import supplémentaire entrainera une duplication du code dans le bundle généré. Dans ce cas Webpack aura généré deux fois le code de variables.scss
.
Il n’y a pas, à l’heure actuelle, de façon propre de régler ce problème :
- La première solution consiste à importer une seule fois tous les fichiers de style dans un fichier
main.scss
importé dans l’application. On perd néanmoins l’aspect isolé et indépendant des modules ES6.
[js].commonClass{
…
}[/js]
./style1.scss
[js]//Plus d’import
.firstClass{
.commonClass;
…
}[/js]
./style2.scss
[js]//Plus d’import
.anotherClass{
.commonClass;
…
}[/js]
[js]// Import all stylesheets here
@import ‘./variables.scss’;
@import ‘.commonStyles.scss/’;
@import ‘./style1.scss’;
@import ‘./style2.scss’;[/js]
app.js
[js]/* … */
import ‘./main.scss’;
/* … */[/js]
- Il est enfin possible de contourner le problème avec node-sass-import-once mais dans ce cas on n’utilise plus Webpack pour gérer les feuilles de style.
Webpack et LESS
Avantage: les multiples imports
LESS est moins utilisé que son homologue, sa communauté est de ce fait moins importante. Néanmoins ce pré-processeur corrige les inconvénients de SASS concernant la gestion des multiples imports.
Si l’on reprend l’exemple écrit en LESS, les classes de commonStyles.less
ne seront écrites qu’une seule fois dans le bundle de Webpack si l’on spécifie le mot clé « reference » lors de l’import d’une ressource externe.
[js].commonClass{
…
}
.anotherCommonClass{
…
}[/js]
[js]@import (reference) ‘./commonStyles.less’;
.firstClass{
.commonClass;
…
}[/js]
./style2.less
[js]@import (reference) ‘./commonStyles.less’;
.anotherClass{
.commonClass;
…
}[/js]
Inconvénient: la ré-écriture d’url
LESS gère la ré-écriture d’URL sans avoir besoin d’un autre loader (resolve-url de SASS). Néanmoins il subsiste à l’heure actuelle un bug qui empêche de passer une variable comme paramètre d’url.
Reprenons le précédent projet:
Configuration Webpack gérant les ressources LESS et PNG
[js]…
module: {
loaders: [
{ test: /\.scss$/, loaders: [‘style’, ‘css’, ‘less’]) },
{ test: /\.png$/, loader: "file-loader?name=img/[name].png"}
/* images disponible dans le bundle à l’url /img/[nom-image].png */
]
}
…[/js]
Répertoire du projet
[java]projet
|_ app.js
|_ index.html
|_ assets
| |_ fonts
| |_ images
| |_ image1.png
| |_ image2.png
|_ style.scss
|_ dist[/java]
Url en dur
./style.scss
[js].my-class{
background: url(‘./assets/images/image1.png’) no-repeat;
}[/js]
Classe dans le bundle de Webpack
[js].my-class{background: url(‘/img/image1.png’) no-repeat;}
/* L’url a bien été remplacée par less-loader */[/js]
Url par une variable
[js]@path-to-image1: ‘./assets/images/image1.png’;
.my-class{
background: url(@path-to-image1) no-repeat;
}[/js]
[js].my-class{background: url(‘./assets/images/image1.png’) no-repeat;}
/* L’url n’a pas été remplacée et n’est donc pas disponible */[/js]
Conclusion
Aucun des deux choix ne sera mauvais cependant LESS permet de rester dans la philosophie de Webpack sans aucun ajout tiers. SASS possède une communauté plus importante, plus active, mais sa complémentarité avec Webpack n’est pas encore parfaite.
Et vous, quel sera votre choix ?
Commentaire
3 réponses pour " Webpack : choisir SASS ou LESS ? "
Published by Melkior , Il y a 7 ans
Merci pour l’article,
Il y’a une coquille sur le bloc de code contenant
{ test: /\.scss$/, loaders: [‘style’, ‘css’, ‘resolve-url’, ‘sass’]) },
Il faut supprimer la ) après ‘sass’]
Published by Baltazar , Il y a 6 ans
Bravo, Bastien.
Article tres interessant sur un probleme souvent rencontre lorsqu’on cherche a eviter l’utilisation de Gulp
Merci
Published by Flint , Il y a 5 ans
Le problème des imports multiple de SASS peut se résoudre avec au niveau de webpack avec optimize-css-assets-webpack-plugin :
https://www.npmjs.com/package/optimize-css-assets-webpack-plugin
https://gist.github.com/phun-ky/766e5ec9f75eac61c945273a951f0c0b
Reste un autre élément pour comparer les deux, le fait que SASS ne gère pas les imports dynamiques càd les noms de variables dans les imports
https://github.com/sass/sass/issues/739