It just so happened that recently I have to learn new tools. The next such tool was the
webpack . The tool is interesting, but after moving with Google Closure * it became a mystery to me why the
webpack does not
squeeze class names, as Google Closure Stylesheets does. During the day, on the knee, I wrote a plugin that didn’t implement this functionality quite well. A more detailed description is below.
And so we start with the TK. This is done, firstly, for myself, and secondly, for those who have not yet understood what is happening, but somehow got on this page. Personally, I like to write large and
beautiful long class names, which are immediately clear what is happening.
For example:
.header { position: fixed; top: 0; ... } .header a { display: block; float: right; ... } .sidebar { float:right; max-width: 30%; ... } .sidebar a { font-size: 16px; .... }
But you can also reduce the
header to
h , a
sidebar to
s , thus saving quite a few bytes not only in CSS, but also in JS files, since most likely your JS will contain selectors by class names.
')
However, the readability of the code will suffer from such a reduction, and, as a result, the speed of development. Therefore, it is necessary to create a tool for carrying out this replacement automatically.
A little explanation of how this works in Closure
Google Closure consists of several tools, one of which is Google Closure Stylesheets, which is both a preprocessor and a postprocessor for style sheets.
As a preprocessor, it is similar to all its fellows, but most of all it is similar to SCSS / SASS.
As a postprocessor, it parses the class names by creating a replacement dictionary and replaces all the class names with their short notation.
For example, the code above will be:
.a { position: fixed; top: 0; ... } .aa { display: block; float: right; ... } .b { float:right; max-width: 30%; ... } .ba { font-size: 16px; .... }
A dictionary of replacements will be:
{ "header": "a", "sidebar": "b" }
In fact, there is much more functionality there, but the article is not about that. There is also Closure Templates, a good template engine in which all class names should be noted with a special directive, which would then apply a dictionary of substitutions to templates.
For example:
{namespace test} /** * Test template * */ {template .test} <div class="{css header}">Header<a href="#">Home</a><a href="#">About</a><header> ... <div class="{css sidebar}">Sidebar<header>
Also, we should not forget that we have JS in which we also need to “refine” the names of all CSS classes:
var header = goog.dom.getElementByClass(goog.getCssName('header')); var sidebar = goog.dom.getElementByClass(goog.getCssName('sidebar'));
And only when we fix all the sources and send them to compile, along with the replacement dictionary, then everything will work.
The main disadvantage of this method is that the dictionary is compiled using CSS, i.e. if you have a class that is used only to fetch a DOM element from JS, then it may not get into the dictionary (or it may, but I’ll say that this article is not a review of Closure Tools).
Back to plugin
It’s not very convenient to scatter functions everywhere, so I decided to set the class names by the mask
___ <% className%> __ .
Thus, styles will come to mind:
.___header__ { position: fixed; top: 0; ... } .___header__ a { display: block; float: right; ... } .___sidebar__ { float:right; max-width: 30%; ... } .___sidebar__ a { font-size: 16px; .... }
And work with DOM in JS, using jQuery as an example:
var header = $('.___header__'); var sidebar = $('.___sidebar__');
On the example of React:
function Header(props) { return ( <div className="___header__"> {props.children} </div> ); }
On the example of Backbone:
module.exports = Backbone.View.extend({ tagName: 'div', className: '___header__' });
UPD:For Angular, the
example is fat.
Immediately make a reservation, that the design type:
var genClassName = function(v) { return '___' + v + '__'; } module.exports = Backbone.View.extend({ tagName: 'div', className: genClassName('header') });
will not work. As well as styles:
[class*="bold"] { font-weight:bolder; }
The first steps
Installing the package:
npm install --save cssrename-webpack-plugin
And a little refining
webpack.config.js :
const CssRenameWebpackPlugin = require('cssrename-webpack-plugin'); ... module.exports = { ... plugins: [ CssRenameWebpackPluginConfig, ... ] };
During the build process, a line will appear:
Profit: 355
Which will report how many bytes have been saved.
Disadvantages and Solutions
But if in the
animal world of JS there are a huge number of libraries that cannot be covered with a single parser, then CSS is much more humane in this issue, and parsing it (CSS) is much simpler.
In its simplest form, this will be one regular expression. Therefore, why not add a similar loader that will save us from adding underscores at least to CSS.
npm install --save cssrename-loader
The next
webpack.config.js mutations:
module.exports = { module: { loaders: [ { test: /\.css$/, loader: "style-loader!css-loader!cssrename-loader" } ] } };
What happened with the test projectUpdate:I didn’t want to write about it, but apparently I should add. Further, only styles, JS and templates winnings are considered so easy not to count. For Google Closure Stylesheets:
Website / File | Original volume / zip | Received volume / zip | Percentage Savings (zip) |
---|
acss.io bundle.488facb7.css | 13.8KB / 4.4KB | 13.3KB / 4.0KB | ≈9% |
getbem.com/introduction getbem.com.1.0.0.css | 13.3KB / 3.3KB | 12KB / 3.1KB | ≈6% |
Bootstrap | 121.2KB / 18.7KB | 96.9KB / 16.7KB | ≈10% |
habrahabr.ru global_main.css | 212.3KB / 30.6KB | 155.2KB / 26.9KB | ≈13% |
ProofWhat "did not fit through" in Google Closure Stylesheets:
For Atomic, we had to make two replacements for regular expressions:
\\\(((?:(?!\\\)).)*?)\\\) => --$1-- \\. => --
For BEM "not climbed":
@supports (display: -moz-box) { [class*=LineClamp] { display: block } } @-webkit-keyframes bounce { 0% { -webkit-transform: translateY(-100%); transform: translateY(-100%); -webkit-filter: blur(5px); filter: blur(5px) } 100%, 40% { -webkit-transform: translateY(0); transform: translateY(0) } 60% { -webkit-transform: translateY(-10%); transform: translateY(-10%) } } @keyframes bounce { 0% { -webkit-transform: translateY(-100%); transform: translateY(-100%); -webkit-filter: blur(5px); filter: blur(5px) } 100%, 40% { -webkit-transform: translateY(0); transform: translateY(0) } 60% { -webkit-transform: translateY(-10%); transform: translateY(-10%) } }
From Bootstrap "not climbed":
border-top: 4px solid \9; @media all and (transform-3d),(-webkit-transform-3d) @-ms-viewport { width: device-width }
From Habr "not climbed":
@charset "UTF-8"; @-moz-document url-prefix() { .search-field__select { text-indent: .01px; text-overflow: '' } }