preface
Due to the limitation of package size of small program and the continuous increase of business iteration, the reduction of package size has always been a pain point that troubles business development. So I started trying to reduce package size in different directions, and here I’m optimizing the size of CSS stylesheet files.
Overall, the volume of CSS style sheets in the whole project is relatively large, and some styles are repetitive, so we began to consider the way of sharing Class to achieve the purpose of reducing package size, so we began to explore and experiment from this perspective.
About atomizing CSS
Atomized CSS is a style of CSS architecture that favors small, single-purpose classes and is named after visual effects.
Atomized CSS can also be referred to as CSS utility classes. There are already CSS frameworks such as Tailwind CSS, Windi CSS, and Tachyons on the market. Some UI libraries also provide built-in CSS utility classes such as Bootstrap.
Traditional scheme
The traditional atomized CSS solution is to provide all the CSS utility classes you might need in your project up front and use them directly at project development time. This is similar to the static utility classes provided by the Bootstrap library. Therefore, the programme mainly has the following problems:
- Large CSS stylesheet files (which is obviously not desirable in applets)
- The CSS does not support dynamic generation of tool classes
Generate solutions on demand
There are Tailwind CSS and Windi CSS on the market. Tailwind CSS supports on-demand generation only after JIT mode is provided. The process for generating tool class files is as follows:
- Scanning the use of tools in the project;
- Generate tool classes on demand based on the scan of the first step;
- Generate stylesheet files;
Both Windi CSS and Tailwind JIT use pre-scanned source code to support HMR.
Finally, Windi CSS framework was selected as the atomized CSS scheme in the project, mainly for the following reasons:
- The Tailwind CSS development environment does not support JIT in reference to the implementation of other projects;
- Tailwind CSS dynamic utility classes need to be used
[]
Enveloping, which is not supported in WXSS files; - Windi CSS is perfectly compatible with Tailwind CSS and has many additional features;
- Windi CSS supports more flexible extension of dynamic style generation rules;
Windi CSS and Tailwind CSS frameworks perform well in Web projects. This article is for small program projects only. If you are a Web project, all the capabilities of the framework are a very good development experience.
Windi CSS framework introduction and use
Windi CSS official document cn.windicss.org/
By scanning HTML and CSS to generate utilities on demand, Windi CSS aims to provide a faster loading experience and faster HMR in development
Installation and introduction
For small programs, because it is based on the cross-platform framework for development, so we can choose the Webpack plug-in windicss- Webpack-plugin provided.
The VS Code plug-in Windi CSS Intellisense is also provided to support automatic prompt and improve development efficiency.
The installation
npm i windicss-webpack-plugin -D
# yarn add windicss-webpack-plugin -D
Copy the code
The introduction of
Import the plug-in into the plugins configuration item of the Webpack configuration file
const WindiCSSWebpackPlugin = require('windicss-webpack-plugin')
export default {
// ...
plugins: [
new WindiCSSWebpackPlugin()
]
}
Copy the code
Introduce windi.css in entry files or files that are loaded only once
<! --js-->
import 'windi.css'
<! --mpx-->
<style src="windi.css"></style>
Copy the code
It doesn’t have to be all in the applet, it’s included in windi.css
windi-base.css
: Initial base style;windi-components.css
: Mainly generate the Stylesheet for Shortcuts;windi-utilities.css
: all utility class stylesheets used;
You don’t really need to use the Windi-base.css style sheet in the applet, so you just need to introduce two other stylesheets that can be broken down to override the existing hierarchy with custom CSS.
<! --js-->
import 'windi-components.css'
import 'windi-utilities.css'
<! --mpx-->
<style src="windi-components.css"></style>
<style src="windi-utilities.css"></style>
Copy the code
Ability to support
Most of the features provided by Windi CSS cannot be used in small programs and are redundant due to the limitations of the syntax of the small program framework.
Available capability:
- Automatic value derivation;
- Shortcuts.
- Visual analyzer;
Capabilities not recommended or unavailable:
- Variable modifier group (unavailable) : Compiled code stylesheets do not support WXSS file formats; (Translatable)
- Instruction (not supported) : compiled production code is the same as handwritten code, and CSS preprocessors (such as SCSS, LESS) are not the same
@apply
Use together; - Attribute mode (unavailable) : Possibly due to applets template, not supported
<view>
; - Responsive design (not recommended) : This capability is not required on the mobile terminal.
- Dark mode (not recommended) : this capability is not required on mobile terminals.
- RTL (not recommended); Try to unify the writing method;
- Important prefix (not supported) : WXSS files with symbols that are not supported will be reported; (Introduction to wechat small program WXSS)
Windi configuration
Add a file named windi.config.ts to the project root directory.
Windi configuration default value: github.com/windicss/wi…
When the server starts, Windi will scan your code and extract the utility classes for use. The configuration in Windi CSS is almost identical to that in Tailwind CSS, but with additional enhancements, so you can also refer to Tailwind CSS.
The following configuration is mainly for applets.
// defineConfig is a help function with type hints that can be ignored if not needed
import { defineConfig } from 'windicss/helpers'
export default defineConfig({
prefixer: false.// Automatically compatible with platform browsers (no)
prefix: 'c-'.// Class name style prefix (to prevent style contamination)
extract: {
// Scan the file range
include: ['src/**/*.{css,html,mpx}'].// Ignore scanning folders
exclude: ['node_modules'.'.git'.'dist'],},// Shortcuts className does not need a prefix
shortcuts: {
/ / sample
'flex-center': 'flex items-center justify-center'
},
theme: {
screens: null.// Media query (not required)
animationTimingFunction: null.// Animation render function (not required)
/* Overwrite or add default values... * /
extend: {
/* Extend theme presets... * /}},plugins: [
/* Introduce extension toolclass plugins... * /].corePlugins: [
/ * core plugin Settings, supports all the core plug-in 1 at https://github.com/windicss/windicss/blob/main/src/interfaces.ts#L98. Allows you to completely disable utility classes generated by Windi by default by setting them to empty arrays [] 2. If you only want to use the listed default utility class, set it to an array and list the core plugins you want to use, such as ['margin', 'padding'] 3. If you only want to disable the specified core plug-in, set it to an object and list the core plug-in to disable, such as {float: false} */]})Copy the code
Have a problem
- Since the Official Windi documentation is relatively light on configuration items, you can learn more from the Windi source code and Tailwind website.
- On Windows, stylesheet files generated by the Windi build tool are named with
:
Is an invalid character in the Windows operating system and causes file generation failure. The current alternative scheme can passWindi CSS CLIYou can customize the output file name.
The project practice
Returning to the theme, the atomized CSS scheme is used to reduce package size. The Windi CSS framework is not designed to do this, but to provide a better development experience, so we need to experiment in small programs to verify whether this solution can achieve the purpose of reducing package size.
In the previous three rounds of experiments, the experimental process of the overall package volume is to accumulate and transform a certain amount of code in each round to demonstrate whether the package volume decreases gradually with the increase of the coverage of tool classes
rounds | The main package volume | Tool style sheet volume |
---|---|---|
The first round | A significant increase in | A significant increase in |
The second round | This is less than the previous round, but an overall increase | Slowly increase |
In the third round | Overall decrease | Slowly increase |
As a result, we achieved our goal and started to modify a small part of the business. However, when we looked at the generated stylesheet file, we found that there was still room for reduction. Due to the style generation rules of the Windi CSS framework, there were several main points:
- Background and font color generation code is more than we write directly;
/* Background color */
.bg-white {
--tw-bg-opacity: 1;
background-color: rgba(255.255.255.var(--tw-bg-opacity));
}
/* Text color */
.text-gray-500 {
--tw-text-opacity: 1;
color: rgba(107.114.128.var(--tw-text-opacity));
}
Copy the code
- Dynamic sizing Settings need to be added
rpx
Unit; (By default, is not addedrem
Units)
/* Width size set */
.w-24 {
width: 6rem;
}
Copy the code
- Spacing cannot be abbreviated;
.py-10 {
padding-top: 2.5 rem;
padding-bottom: 2.5 rem;
}
.my-12 {
margin-top: 3rem;
margin-bottom: 3rem;
}
Copy the code
The first thing to understand is why using atomized CSS can reduce package size is that we can use shorter Class utility classes and longer Style styles. Then there are the benefits of the framework:
- Windi CSS framework provides a visual analyzer that can analyze the use of all tool classes to manage and optimize the use of CSS;
- Provides dynamic tool class generation capability, do not need to manually add static tool class;
To address the above problem, you need to use the ability of the Extended utility classes provided by the Windi CSS framework to customize plug-ins for applets.
Windi CSS framework plug-in development
On the plug-in development of Windi framework, may refer to: cn.windicss.org/plugins/int…
The introduction and instructions in the document are actually quite brief, with only a few simple examples for our reference. This section focuses on adding dynamic utility classes and customizing a plug-in for a small program project. The first goal was to reduce package size, and around that goal we needed to re-customize the style generation rules for some of the utility classes. Therefore, the following utility classes need to be recustomized:
- Default units for size/spacing/layout/positioning/border;
- Extended margin and padding rules; (for example, support
.m-2_4
generatemargin: 2rpx 4rpx
) - Font/background color generation rules;
First, a quick look at the basic usage
import plugin from 'windicss/plugin'
plugin(({ addDynamic }) = > {
addDynamic('filter'.({ Utility, Style }) = > {
return Utility.handler
.handleStatic(Style('filter'))
.createProperty(['-webkit-filter'.'filter'])})})Copy the code
The Utility object
It’s important to understand what properties and methods a Utility object contains for us to use. Specific can see its source github.com/windicss/wi…
Suppose you use the.-placeholder real-gray-300 utility class
attribute | type | describe | The return value |
---|---|---|---|
raw | String | Class Specifies the name of the utility class | -placeholder-real-gray-300 |
class | String | The class tools | .-placeholder-real-gray-300 |
isNegative | Boolean | Check if it’s negative | true |
absolute | String | Absolute value of tool class | placeholder-real-gray-300 |
identifier | String | First word of tool class | placeholder |
key | String | Tool keyword | placeholder-real-gray |
center | String | Median utility class value | real-gray |
amount | String | The numerical | 300 |
body | String | Tool class body | real-gray-300 |
match | Function | Re match utility class | – |
clone | Function | Copy the utility classUtility The sample |
– |
handler | Function | Set of processing functions | – |
Utility.handler
The API provided by Utility. Handler makes it easy to process the scanned Utility classes and generate the desired style. The following apis are provided:
export type Handler = {
utility: Utility value? : string_amount: string opacity? : string |undefinedcolor? : colorCallbackhandleStatic: (map? : { [key: string]: string | string[] } | unknown, callback? : (str: string) => string |undefined
) = > Handler
handleBody: (map? : { [key: string]: string | string[] } | unknown, callback? : (str: string) => string |undefined
) = > Handler
handleNumber: (start? : number, end? : number, type? :'int' | 'float', callback? : (number: number) => string |undefined
) = > Handler
handleString: (callback: (string: string) => string | undefined) = > Handler
handleSpacing: () = > Handler
handleSquareBrackets: (callback? : (number: string) => string |undefined
) = > Handler
handleTime: (start? : number, end? : number, type? :'int' | 'float', callback? : (milliseconds: number) => string |undefined
) = > Handler
handleColor: (map? : colorObject | unknown) = > Handler
handleOpacity: (map? : DictStr | unknown) = > Handler
handleFraction: (callback? : (fraction: string) => string |undefined
) = > Handler
handleNxl: (callback? : (number: number) => string |undefined
) = > Handler
handleSize: (callback? : (size: string) => string |undefined
) = > Handler
handleVariable: (callback? : (variable: string) => string |undefined
) = > Handler
handleNegative: (callback? : (value: string) => string |undefined
) = > Handler
createProperty: (name: string | string[], callback? : (value: string) => string) = > Property | undefined
createStyle: (
selector: string,
callback: (value: string) => Property | Property[] | undefined
) = > Style | undefined
createColorValue: (opacityValue? : string |undefined
) = > string | undefined
createColorStyle: (selector: string, property: string | string[], opacityVariable? : string |undefined, wrapRGB? : boolean) = > Style | undefined
callback: (
func: (value: string) => Property | Style | Style[] | undefined
) = > Property | Style | Style[] | undefined
}
Copy the code
See github.com/windicss/wi…
Here is a simple example that deals with the default unit of length set to RPX
// set size unit
const handleGeneratorUnit = function(style, prop) {
return function({ Utility, Style }) {
return Utility.handler
.handleStatic(Style(style))
.handleSize()
.handleNumber(1.undefined.'int'.(number) = > `${number}rpx`)
.handleNegative()
.createProperty(prop)
}
}
Copy the code
The main processing operations are as follows:
- Identify the need to handle to the utility class and set the global Settings to the property name;
- Check whether the value is
size
If so, returnUtility.amount
; - Returns an integer with values ranging from 1 to infinity
rpx
The size of a unit; - Whether the processing is negative, if so, add a negative sign;
- Create properties;
Windi external to provide the creation of tool class method is very many, and very flexible, if you need to develop a complex tool class plug-in, you can refer to the source code:
- Dynamic tool class correlation
- The official plug-in
The resources
- Reimagine atomized CSS