原子樣式的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/[email protected]/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