天天看點

【原子樣式實踐】第5篇 為微信小程式生成原子樣式

原子樣式的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      

3 小結