原子样式的web实现有很多,tailwind, windy, unocss 等。原子样式使用最麻烦的地方是在动态生成上,而这与工程类型、IDE皆有关系。如果原子样式项目是集成到某个项目的,事情就会变得更加复杂,实现进度和完成度都会滞后。而这,与原子样式本身的简洁特点是不符合的。
1 类似工具支持情况
1.1 快速使用
到目前为止,还没有特别适合微信原生小程序的原子样式生成工具。我们可以从现有工具的特点吸收优点进行改进。
(1) windicss ,在 CLI 模式下,一行命令安装,一行命令生成代码。
npm i -g windicss
windicss './**/*.wxml' -to windi.css
CLI 模式下使用已经是极简。其他模式下配置也不复杂。
但生成的结果不理想,没有针对微信小程序优化,编译器报告 wxss 编译错误。
(2)unocss 通过 CLI 方式在 windows powershell 下运行失败,适配还没有完全做好。
1.2 问题分析
当然,主要原因是微信内置的浏览器不是标准的WEB浏览器,各工具没有完全适配。这一点会随着标准的公开和时间的推进而越来越好。
就小程序而言,要快而小,样式无需太复杂,在手机上是全屏运行,场景更加简单。微信小程序的原子样式工具应该更加简单。
2 工具研发
2.1 特定需求
(1)app.wxss
为全局样式,默认启用。
(2)页面根元素使用 page
,而不是 body
。
(3)页面样式属性对应 class
和 hover-class
和 placeholder-class
。
2.2 建设思路
基于上一篇通过样式生成css代码的函数,生成微信小程序所需的样式代码,只需要从样式文件中提取样式名称即可。
提取样式只需解析 wxml 文件,从匹配的样式属性中提取样式名称,去重即可。
2.3 技术实现
本地文件 wxmp-atomic-css-generate.ts
import {htmltok, TokenType} from 'https://deno.land/x/htmltok@v0.0.3/mod.ts';
const RootDir = "./miniprogram"
const ConfigFileName = `${RootDir}/app.json`
interface CountMap {
[index: string]: number
}
const fullPagePath = (page: string): string => `${RootDir}/${page}.wxml`
const isClassAttr = (attrName: string): boolean => attrName == "class" || attrName == "hover-class" || attrName == "placeholder-class"
const readWxmpPages = (app: any): string[] => [...app.pages, ...app.subpackages.map((pkg: any) => pkg.pages.map((page: string) => `${pkg.root}/${page}`)).flat()]
const extraClassItem = (className: string): string[] => {
if (className == "" || className.length <= 2) {
return []
}
if (className.match(/^[\s\da-z-\\.]+$/)) {
return className.trim().split(/\s+/)
}
const result = className.trim().match(/[\w-]+/g)
if(!result) {
return []
}
return result.filter(m=> m.length > 1 && !/[A-Z]/.test(m))
}
const parseClassItem = (page: string, countMap: CountMap) => {
const xml = Deno.readTextFileSync(fullPagePath(page))
let attrName = ""
for (const token of htmltok(xml)) {
if (token.type == TokenType.ATTR_NAME) {
attrName = token.getValue()
} else if (isClassAttr(attrName) && token.type == TokenType.ATTR_VALUE) {
const items = extraClassItem(token.getValue())
items.forEach((s:string)=> {
countMap[s] = (countMap[s] || 0) + 1
})
}
}
}
const readAllClassItems = async () => {
const classNameMap: CountMap = {}
const pages = await Deno.readTextFile(ConfigFileName)
.then((data: string) => readWxmpPages(JSON.parse(data)))
.then((pages: any) => pages.map((page: string) => parseClassItem(page, classNameMap)))
return [...Object.keys(classNameMap)]
}
const classItems = await readAllClassItems()
console.log(classItems)
执行命令
deno run --allow-read ./wxmp-atomic-css-generate.ts
也可以将 wxmp-atomic-css-generate.ts 文件放到代码仓库,在小程序代码下直接执行。
3 小结
本文介绍了基于 deno 实现 CLI 模式的微信小程序原子样式代码生成方式,一个文件即可。微信小程序适配的主要工作任务是从页面文件中提取样式名称,通过正则表达式和函数组合可以提取。复杂点在于识别微信表达式,提取样式名称不遗漏。