作者简介
Can,携程前端开发,目前从事小程序开发工作,对编译打包技术、小程序跨平台解决方案有浓厚兴趣。
一、概述
目前我们团队小程序是使用 Taro 跨端方案 React 框架进行开发,基于现有样式方案,在编译打包后会产生大量的样式代码冗余,在项目编译后的产物中占有较大比例。
分析了编译后的样式代码后,我们发现冗余代码主要体现在两个方面:
项目样式文件中大量使用了父子选择器作为作用域进行样式隔离,编译后出现类名大量重复冗余。如以下 SCSS 文件样式代码中,编译后 .box .item 重复冗余了三次。
// 编译前代码
.box {
.item {
.item1 {}
.item2 {}
.item3 {}
.item4 {}
}
}
// 编译后代码
.box .item .item1 {}
.box .item .item2 {}
.box .item .item3 {}
.box .item .item4 {}
样式代码中大量属性值重复冗余。如最常用的 display: flex 属性值,在项目中可能存在几百上千份重复冗余,而且为了兼容性开启了 Autoprefixer 插件后, display:flex 将会变成 display:-webkit-flex;display:-ms-flexbox;display:flex; ,使得样式文件属性值的冗余情况更为严重。
针对 Taro项目 React 框架小程序遇到的以上问题,本文将介绍一种新的样式解决方案。本方案在较少改变现有开发体验的条件下,采用 cssModules 样式方案语法要求,利用 Taro 插件的便利性给出对应的解决方案,以此对产物进行“瘦身”。最终样式文件的瘦身效果可以达到 50% - 70%,进一步缓解官方包 Size 的限制,便于业务的高速发展。
二、cssModules 简单介绍
本文样式方案学习了 cssModules 解决样式冲突的基本原理,并在此基础上改进以达到缩减样式文件 Size 的目的。因此在正式了解本方案之前,本文先用 Taro 官网中使用 cssModules 方案的例子代码作为示例,简单了解下其语法要求与原理。
2.1 语法要求
在配置开启了 cssModules 后,按照语法要求,Taro 项目中有 index.module.scss 和 index.js 两个文件,文件代码如下。cssModules 默认是开启部分自定义模式转换,只有文件名中包含 .module. 的样式文件才会经过 cssModules 转换处理。在如下 index.module.scss 样式文件中,我们正常使用了父子选择器、类选择器。但是在index.js 文件中,className 赋值不再是字符串,而是 SCSS 文件导出的 Object 的某个 Key,该 Key 为 SCSS 文件中的类选择器的命名。
import React, { Component } from 'react'
import { View, Text } from '@tarojs/components'
import styles from './index.module.scss'
export default class Index extends Component {
render() {
return (
<View className={styles.test}>
<Text className={styles.txt}>Hello world!</Text>
</View>
)
}
}
.test {
color: red;
.txt {
font-size: 36px;
}
}
2.2 原理
Taro 项目开启 cssModules 配置后,在编译打包时,会使用实现了 cssModules 规范的 css-loader 对 SCSS 等样式文件进行处理。它首先会处理原 SCSS 文件中的类选择器,将类名进行哈希处理得到新类名如 index-module__test___Bm2J6 ,生成新的样式代码输出到最终的 index.wxss,同时保存了原类名与哈希处理后的新类名的映射关系。此后它会将原 SCSS 文件 index.module.scss 编译为导出了原类名与哈希后的新类名的映射对象。JS 文件在运行时能通过该映射对象获取到哈希后的新类名,保证该文件类名不会与其他样式文件的同类名冲突,从而解决样式冲突问题。以下为编译后的代码示例, styles.test 在运行时会会变成 index-module__test___Bm2J6 。
// index.module.scss
export default ({"test":"index-module__test___Bm2J6","txt":"index-module__txt___nIysk"});
// index.wxss
.index-module__test___Bm2J6 {
color: red;
}
.index-module__test___Bm2J6 .index-module__txt___nIysk {
font-size: 36rpx;
}
三、方案原理介绍
3.1 基本原理
3.1.1 当前样式文件 size 分析
在正式介绍本文方案是如何缩减样式文件 Size 之前,本文通过以下两个正则去分别匹配打包产物中所有样式文件的两个核心组成部分 ClassName 与 PropertyValue,并进行 Size 统计分析。
注:在本文中,有如该 .txt .tit {color: #red;} CssRule代码,ClassName 指的是其中的 txt 和 tit ,PropertyValue 指的是 color:#red; 。
const classNamePattern = /(?<=\.)[A-Za-z0-9\-_]+(?=\s|{|:)/g // 匹配 ClassName 如 .txt {color: #red;}中的txt
const cssPropertyPattern = /(?<=\{)[^}]+(?=})/g // 匹配PropertyValue, 如 .txt {color: #red;}中 中括号之间的所有内容 color: #red;
下图是对整个编译打包后的小程序项目的样式文件进行组成 Size 分析。通过该图我们可以发现,我们项目打包编译后的所有的样式文件中,ClassName 占用大约有五分之一的空间,而 PropertyValue 则占用了有十分之七的空间,其余空间占比可能是如空格、伪类这种形态存在,本文暂不考虑。