天天看點

【原子樣式實踐】第8篇 更新微信小程式原子樣式工具的實時生成能力-20ms響應

前文的示例教學中給出了全量生成微信小程式原子樣式的過程,但生成時間在100-300ms之間,延遲200ms執行,頁面有較為明顯的抖動。為了增強工具的實用性,本文探讨如何達到10ms以内的頁面級響應。

1 工作原理

1.1 全量生成和部分生成

初次生成微信小程式原子樣式時必須使用全量生成,建立緩存資料。後續生成時基于緩存資料,可以使用部分生成,達到頁面級響應。

為了友善描述,主檔案分為三個函數。

(1)fullBuild 為全量生成函數,僅在初始化完成後執行。

(2)partiallyUpdate 為部分生成函數,在監測檔案變化後執行,複用了全量函數生成的頁面樣式緩存結果。

(3)匿名函數為主函數,直接執行。

編碼采用無狀态函數、箭頭函數、Promise風格,主檔案中除了三個輸入參數(配置檔案、自定義配置參數、系統檔案事件參數)、傳回值和錯誤值外,沒有其他參數,沒有流程圖也可以友善的了解邏輯。

import "https://deno.land/x/[email protected]/mod.ts";
import "https://deno.land/[email protected]/fs/mod.ts";
import {printError, log, timing} from "./util.ts"
import {OptionalRunningConfig, WxRunningConfig} from "./data.config.ts";
import * as wx from "./mod.wx.ts";

const fullBuild = (config: WxRunningConfig): Promise<number> => {
    log("[task] start auto generation after started");
    const time = timing()
    return Promise.all([
        wx.parseGlobalStyleNames(config),
        wx.parseMiniProgramPages(config).then(wx.batchPromise(wx.parsePageClassNames, config)),
        wx.parseComponentPages(config).then(wx.batchPromise(wx.parseComponentClassNames, config)),
    ]).then(wx.mergeTargetClassNames(config))
        .then(wx.generateContent(config))
        .then(wx.saveContent(config))
        .then(wx.finishAndPrintCostTime(config, time))
        .then(() => log("service ready, Press Ctrl-C to exit"))
        .catch(printError(time))
}

const partiallyUpdate = (config: WxRunningConfig, fileEvents: string[]): Promise<number> => {
    const time = timing()
    return wx.generateClassNamesFromFileEvents(config, fileEvents)
        .then(wx.generateContent(config))
        .then(wx.saveContent(config))
        .then(wx.finishAndPrintCostTime(config, time))
        .catch(printError(time))
}

(function () {
    log("==========================================================");
    log("   wxmp-atomic-css: wechat mini program atomic css kit");
    log("==========================================================");
    log("starting wxmp-atomic-css");

    const sigIntHandler = () => {
        log("wxmp-atomic-css service closed");
        Deno.exit();
    };
    Deno.addSignalListener("SIGINT", sigIntHandler);

    wx.readRunningConfig("data/config.json", {
        // debugOption: {showPageClassNames: true, showPageTaskBegin: true, showPageTaskResult: true}
    } as OptionalRunningConfig)
        .then(wx.ensureWorkDir)
        .then(wx.printRunningConfig)
        .then((config: WxRunningConfig) => fullBuild(config)
            .then(() => wx.watchMiniProgramPageChange(config, partiallyUpdate)))
        .catch((e: unknown) => log(e))
})()      

匿名主函數的流程為:(1)列印啟動日志;(2)注冊中斷函數;(3)讀取配置,檢查小程式目錄,列印配置,全量生成樣式檔案,監測小程式目錄生成樣式檔案。

fullBuild 全量生成函數的流程為:(1)讀取全局樣式,讀取元件頁面樣式,讀取并計算主包、分包頁面需要生成的樣式,(2)合并計算需要生成的樣式;(3)生成樣式内容;(4)儲存内容到檔案;(5)緩存結果,列印耗時。

partiallyUpdate 部分生成函數的流程為:(1)根據事件更新的頁面需要生成的樣式,合并緩存獲得全部需要生成的樣式;(2)生成樣式内容;(3)儲存内容到檔案;(4)緩存結果,列印耗時。

1.2 主要參數定義

1.2.1 運作參數定義
export interface WxRunningConfig {

    /**
     * working dir, default value is ".", should detect if contains correct contents
     */
    workDir: string

    /**
     * temp data, which store global rules and themes
     */
    tempData: TempData

    /**
     * mini program file structure
     */
    fileStructure: FileStructure

    /**
     * mini program file extension
     */
    fileExtension: FileExtension

    /**
     * css options
     */
    cssOption: CssOption

    /**
     * debug options, each default value is false
     */
    debugOption: DebugOption

    /**
     * data file or uri
     */
    dataOption: DataOption

    /**
     * watch options
     */
    watchOption: WatchOption

    /**
     * process options, e.g. promise
     */
    processOption: ProcessOption
}


/**
 * debug option
 */
export interface DebugOption {
    /**
     * print configuration
     */
    printConfigInfo: boolean
    /**
     * print rules
     */
    printRule: boolean
    /**
     * print themes
     */
    printThemes: boolean
    /**
     * show page files' class names
     */
    showPageClassNames: boolean
    /**
     * show page class attributes
     */
    showPageClassAttribute: boolean
    /**
     * show css files' style names
     */
    showCssStyleNames: boolean
    /**
     * show the beginning of page task
     */
    showPageTaskBegin: boolean
    /**
     * show the result of page task
     */
    showPageTaskResult: boolean
    /**
     * show style task result
     */
    showStyleTaskResult: boolean
    /**
     * show batch task step
     */
    showTaskStep: boolean
    /**
     * show the generated content
     */
    showFileContent: boolean
}

/**
 * watch option
 */
export interface WatchOption {
    /**
     * refresh duration after the file changes
     */
    delay: number;
    /**
     * extensions of files which could update global css file
     */
    fileTypes: string[];
    /**
     * refresh order count
     */
    refreshCount: number;
}

/**
 * process option
 */
export interface ProcessOption {
    /**
     * promise concurrent limit
     */
    promiseLimit: number;
}      
1.2.2 樣式規則定義
/**
 * atomic style rule
 */
export interface AtomicStyleRule {
    /**
     * package name to manage rules
     */
    package: string
    /**
     * describe the rule, if syntax is not normal
     */
    desc?: string
    /**
     * class name syntax, could use vars, [U]-unit, [C]-color, [N]-number,
     * [A]-alpha number, with decimal point as prefix is the true value
     */
    syntax: string
    /**
     * classes name which in global css file and compose to the new style
     */
    compose?: string[]
    /**
     * style declaration for class names, required if compose is undefined or empty
     */
    expr?: string,
    /**
     * dependencies of style names
     */
    dependencies?: string[],
    /**
     * units includes by expr, effected on running
     */
    units?: string[],
    /**
     * colors includes by expr, effected on running
     */
    colors?: string[],
    /**
     * regEx for dynamic expr, which includes [, effected on running
     */
    syntaxRegex?: RegExp
}      

1.3 并發處理

為了更充分的使用計算機的性能,這裡使用了并發處理:

(1)使用 Promise.all 發起了全局樣式檔案、元件檔案、頁面檔案解析任務。

(2)對于元件檔案和頁面檔案,在頁面檔案較多的情況下,分别執行了并發(5個,預設值)處理。

可以将處理時間從 1秒減少到200ms左右(個人計算機實測)。

1.4 複用處理 

樣式檔案目前采用全局生成政策,将各個頁面的解析結果進行緩存處理。

緩存儲存在 WxRunningSetting 的 tempData 屬性中,結構如下。

/**
 * temp data
 */
interface TempData {
    /**
     * effected rule setting cache
     */
    ruleSetting?: StyleRuleSetting;
    /**
     * effected theme map cache
     */
    themeMap?: ThemeMap;
    /**
     * page class names dictionary
     */
    pageClassNameMap: { [index: string]: string[] };
    /**
     * global style names
     */
    globalClassNames: string[]
    /**
     * global style names
     */
    tempGlobalClassNames: string[]
}      

 在編寫單個檔案時,伴随編寫進行編譯,延時200ms(預設值)進行編譯,編譯可以在3-20ms(個人計算機實測)内完成。

2 運作結果

本文示例中,本地執行分為四個部分,日志如下:

❯ deno run --allow-read --allow-write --watch worker.ts D:\xxx\yyy\miniprogram\
Watcher Process started.
2022-10-23 11:02:10.529 ==========================================================
2022-10-23 11:02:10.531    wxmp-atomic-css: wechat mini program atomic css kit
2022-10-23 11:02:10.531 ==========================================================
2022-10-23 11:02:10.531 starting wxmp-atomic-css
2022-10-23 11:02:10.534 [task] working directory found for app.wxss at D:\repo\ryl-wxmp\miniprogram\
2022-10-23 11:02:10.542 [task] read 15 themes
2022-10-23 11:02:10.545 [task] read 144 rules
2022-10-23 11:02:10.545 [task] start auto generation after started
2022-10-23 11:02:10.576 [task] parse global styles names, found [1] in [font.wxss]
2022-10-23 11:02:10.578 [task] parse global styles names, found [6] in [app.wxss]
2022-10-23 11:02:10.580 [task] read wechat mini program pages from config file, found [34] pages
2022-10-23 11:02:10.580 [task] [parsePageClassNames] begin 34 tasks
2022-10-23 11:02:10.680 [task] [parsePageClassNames] finish 34 tasks
2022-10-23 11:02:10.688 [task] [parseComponentClassNames] begin 11 tasks
2022-10-23 11:02:10.709 [task] [parseComponentClassNames] finish 11 tasks
2022-10-23 11:02:10.710 [data] total found [7] global style names
2022-10-23 11:02:10.710 [data] total found [166] class names from pages
2022-10-23 11:02:10.710 [data] total found [100] class names from components
2022-10-23 11:02:10.712 [data] [4] class names to remove, [tip-blue,tip-green,tip-red,tip-yellow]
2022-10-23 11:02:10.712 [data] new task for generate [190] class names = [-a,active-bg,active-text-day,active-text-week,ai-center,ai-end,ai-start,bg-black,bg-black-a50,bg-blue-1,bg-blue-6,bg-gray-1,bg-gray-2,bg-gray-3,bg-gray-4,bg-gray-5,bg-gray-7,bg-green-7,bg-orange-3-a10,bg-orange-6,bg-orange-6-a30,b
g-primary,bg-primary-a10,bg-red-3-a10,bg-red-7,bg-red-7-a10,bg-white,border,border-2,border-black,border-bottom,border-gray-10,border-green-6,border-orange-6,border-orange-6-a30,border-primary,border-red-6,border-top,border-transparent,c1,c2,c3,currency,day,delay-1000,disable-text,duration-1000,ease-in,
flex-cc,flex-col,flex-col-r,flex-lc,flex-row,g-,gap-10,gap-20,gap-32,gap-4,grid-2c,grid-3c,grid-5c,grid-7c,h-120,h-150,h-20,h-36,h-64,h-80,h-96,jc-around,jc-between,jc-center,jc-end,jc-start,m-32,mb-10,mb-20,mb-32,mh-120,mh-150,mh-200,ml-10,ml-20,ml-32,ml-4,mr-20,mr-32,mr-4,mt-10,mt-20,mt-32,mt-4,mx-20,
mx-32,my-10,my-20,normal-bg,normal-text,number,opacity-60,opacity-70,p-10,p-20,p-32,pb-10,pb-20,pb-32,pb-4,pl-10,pl-20,pl-32,pos-abs,pos-rel,pos-tr,pr-20,pr-32,pr-4,pt-10,pt-20,pt-32,px-10,px-20,px-32,px-37,px-4,px-80,py-10,py-20,py-4,py-90,rotate-180,round,round-10,round-10m,round-20,round-36,round-4,r
ound-8,round-bl-10,round-br-10,round-tl-10,round-tr-10,shadow,text-20,text-24,text-28,text-30,text-32,text-36,text-48,text-black,text-blue-6,text-bold,text-break,text-center,text-gray-6,text-gray-7,text-gray-9,text-green-6,text-line-p150,text-normal,text-orange-6,text-primary,text-red-6,text-right,text-
white,theme,themes,type,userinfo,userinfo-avatar,userinfo-nickname,w-120,w-96,w-full,wh-144,wh-256,wh-36,wh-48,wh-64,wh-72,wh-96,wh-full,wh-screen,wrap,z-,z-1,z-2,z-3,z-4]
2022-10-23 11:02:10.713 [data] new task to create [190] class names
2022-10-23 11:02:10.730 [warnings] 21 class names not matched, -a,active-bg,active-text-day,active-text-week,currency,day,disable-text,g-,normal-bg,normal-text,number,pos-tr,round-10m,theme,themes,type,userinfo,userinfo-avatar,userinfo-nickname,wrap,z-
2022-10-23 11:02:10.731 [data] new task to create [29] unit vars, [0,1,10,12,120,144,150,2,20,200,24,256,28,3,30,32,36,37,4,48,6,64,72,8,80,90,96,d5,p100]
2022-10-23 11:02:10.731 [data] new task to create [26] color vars, [black-1,black-1-50,blue-1,blue-6,gray-1,gray-10,gray-2,gray-3,gray-4,gray-5,gray-5-a05,gray-6,gray-7,gray-9,green-6,green-7,orange-3-10,orange-6,orange-6-30,primary-1,primary-1-10,red-3-10,red-6,red-7,red-7-10,white-1]
2022-10-23 11:02:10.731 [task] begin to write output file
2022-10-23 11:02:10.734 [task] save 1756 chars to var.wxss
2022-10-23 11:02:10.736 [task] save 10754 chars to mini.wxss
2022-10-23 11:02:10.736 [data] job done, cost 191 ms, result = 0
2022-10-23 11:02:10.736 service ready, Press Ctrl-C to exit



2022-10-23 11:02:17.339 [file changed] *D:\xxx\yyy\miniprogram\pages\index\course.wxml
2022-10-23 11:02:17.341 [task] page [D:\xxx\yyy\miniprogram\pages\index\course.wxml] - [wh-screen,z-1,flex-col,py-20,c1,mh-200,text-32,text-black,px-32,px-37,flex-row,ai-center,jc-center,h-96,wh-64,text-30,text-orange-6,mx-20,my-20,shadow,round-20,gap-32,duration-1000,delay-1000,ease-in,my-10,text
-36,text-bold,text-center,wh-144,text-primary,bg-gray-2,round-10,text-gray-7,wh-256,h-20,mt-32,bg-orange-3-a10,jc-around,wh-72,h-36,bg-red-3-a10,safe-bottom]
2022-10-23 11:02:17.342 [data] new task to create [193] class names
2022-10-23 11:02:17.346 [warnings] 23 class names not matched, -a,active-bg,active-text-day,active-text-week,currency,day,disable-text,ff-n,g-,normal-bg,normal-text,number,pop-full,pos-tr,round-10m,theme,themes,type,userinfo,userinfo-avatar,userinfo-nickname,wrap,z-
2022-10-23 11:02:17.347 [data] new task to create [29] unit vars, [0,1,10,12,120,144,150,2,20,200,24,256,28,3,30,32,36,37,4,48,6,64,72,8,80,90,96,d5,p100]
2022-10-23 11:02:17.347 [data] new task to create [26] color vars, [black-1,black-1-50,blue-1,blue-6,gray-1,gray-10,gray-2,gray-3,gray-4,gray-5,gray-5-a05,gray-6,gray-7,gray-9,green-6,green-7,orange-3-10,orange-6,orange-6-30,primary-1,primary-1-10,red-3-10,red-6,red-7,red-7-10,white-1]
2022-10-23 11:02:17.347 [task] begin to write output file
2022-10-23 11:02:17.349 [task] save 1756 chars to var.wxss
2022-10-23 11:02:17.350 [task] save 10819 chars to mini.wxss
2022-10-23 11:02:17.350 [data] job done, cost 11 ms, result = 0
2022-10-23 11:02:17.351 [task] wxmp-atomic-css refresh 1x



2022-10-23 11:02:24.870 [file changed] *D:\xxx\yyy\miniprogram\pages\index\course.wxml
2022-10-23 11:02:24.873 [task] page [D:\xxx\yyy\miniprogram\pages\index\course.wxml] - [wh-screen,z-1,flex-col,py-20,c1,mh-200,text-32,text-black,px-32,px-37,flex-row,ai-center,jc-center,h-96,wh-64,text-30,text-orange-6,text-36,mx-20,my-20,shadow,round-20,gap-32,duration-1000,delay-1000,ease-in,my
-10,text-bold,text-center,wh-144,text-primary,bg-gray-2,round-10,text-gray-7,wh-256,h-20,mt-32,bg-orange-3-a10,jc-around,wh-72,h-36,bg-red-3-a10,safe-bottom]
2022-10-23 11:02:24.874 [data] job terminated, cost 3 ms { code: 1, msg: "page class names already generated" }
2022-10-23 11:02:24.876 [task] wxmp-atomic-css refresh 2x



2022-10-23 11:02:41.979 [file changed] *D:\xxx\yyy\miniprogram\pages\index\course.wxml
2022-10-23 11:02:41.985 [task] page [D:\xxx\yyy\miniprogram\pages\index\course.wxml] - [wh-screen,z-1,flex-col,py-20,c1,mh-200,text-32,text-black,px-32,px-37,flex-row,ai-center,jc-center,h-96,wh-64,text-30,text-orange-6,text-36,text-red-5,mx-20,my-20,shadow,round-20,gap-32,duration-1000,delay-1000
,ease-in,my-10,text-bold,text-center,wh-144,text-primary,bg-gray-2,round-10,text-gray-7,wh-256,h-20,mt-32,bg-orange-3-a10,jc-around,wh-72,h-36,bg-red-3-a10,safe-bottom]
2022-10-23 11:02:41.986 [data] new task to create [194] class names
2022-10-23 11:02:41.990 [warnings] 23 class names not matched, -a,active-bg,active-text-day,active-text-week,currency,day,disable-text,ff-n,g-,normal-bg,normal-text,number,pop-full,pos-tr,round-10m,theme,themes,type,userinfo,userinfo-avatar,userinfo-nickname,wrap,z-
2022-10-23 11:02:41.991 [data] new task to create [29] unit vars, [0,1,10,12,120,144,150,2,20,200,24,256,28,3,30,32,36,37,4,48,6,64,72,8,80,90,96,d5,p100]
2022-10-23 11:02:41.991 [data] new task to create [27] color vars, [black-1,black-1-50,blue-1,blue-6,gray-1,gray-10,gray-2,gray-3,gray-4,gray-5,gray-5-a05,gray-6,gray-7,gray-9,green-6,green-7,orange-3-10,orange-6,orange-6-30,primary-1,primary-1-10,red-3-10,red-5,red-6,red-7,red-7-10,white-1]
2022-10-23 11:02:41.991 [task] begin to write output file
2022-10-23 11:02:41.994 [task] save 1791 chars to var.wxss
2022-10-23 11:02:41.996 [task] save 10865 chars to mini.wxss
2022-10-23 11:02:41.996 [data] job done, cost 17 ms, result = 0
2022-10-23 11:02:41.996 [task] wxmp-atomic-css refresh 3x      

為了友善閱讀,上面的日志人為添加空白行進行了分割。

(1)啟動後的全量生成,編譯耗時 191 ms。

(2)增加了幾個樣式,編譯耗時 11 ms。

(3)編寫了一些代碼,不涉及到樣式,編譯耗時 3 ms。

(4)編寫了一些複雜代碼,新增了一個樣式,編譯耗時 17 ms。

3 源碼

​​foxgst/wxmp-atomic-css: atomic css for wechat mini program (github.com)​​

4 小結

繼續閱讀