解釋器模式可以透過簡單語言來執行常見任務。
一份樂譜記載著一段旋律,譜上的音符代表著音高,休止符代表著暫停,音節的長短則由不同的符號來表示。樂譜上的符號就像是一種音樂的語言。演奏家通過解讀樂譜,將這些靜態的符號轉化為動人的旋律,就像解釋器藉由解讀特定的語言來執行任務一樣。
在 Tailwind CSS 中,你可以使用官方提供的樣式名稱來套用樣式,也可以透過自訂的樣式名稱來客製化樣式。客製化樣式的建立方式非常簡單,只要將樣式或屬性用中括號包起來,系統就會依據這些自訂名稱產生對應的 CSS 。例如, w-[32rem]
會產生 .w\-\[32rem\] { width: 32rem; }
。
我們參考 Tailwind CSS 的客製化語法實作一個樣式名稱解釋器。使用者可以使用 屬性-[值]
的格式來指定樣式,只要樣式名稱符合規則,解釋器就會產生對應的 CSS。
Expression
是基本的介面,包含一個 interpret
方法,讓具體表達式定義節點的解析方法。
interface Expression {
interpret(): string | undefined;
}
PropertyExpression
類別負責處理樣式名稱中的屬性縮寫(如 h
、w
),將縮寫轉換為完整的屬性名稱。
class PropertyExpression implements Expression {
private propertyMap: Record<string, string> = {
h: "height",
w: "width",
bg: "background-color",
};
constructor(private propertyAlias: string) {}
interpret() {
return this.propertyMap[this.propertyAlias];
}
}
ValueExpression
類別負責處理樣式名稱中的值(如 [12px]
),interpret
方法會移除中括號,並返回其中的值。
class ValueExpression implements Expression {
constructor(private value: string) {}
interpret() {
if (/\[[a-z0-9]+\]/.test(this.value)) {
return this.value.slice(1, -1);
}
}
}
ClassNameExpression
類別負責解析完整的樣式名稱,將名稱轉換成對應的 CSS 語法。
class ClassNameExpression implements Expression {
constructor(private value: string) {}
interpret() {
const [propAlias, value] = this.value.split("-");
if (!propAlias || !value) return;
const propExp = new PropertyExpression(propAlias);
const valueExp = new ValueExpression(value);
const interpretedProp = propExp.interpret();
const interpretedValue = valueExp.interpret();
if (interpretedProp && interpretedValue) {
const escapedClassName = this.escapeClassName(this.value);
return `.${escapedClassName} {\n ${interpretedProp}: ${interpretedValue};\n}`;
}
}
private escapeClassName(className: string): string {
return className.replace(/[^a-z0-9]/g, (match) => `\\${match}`);
}
}
ClassNameParser
負責用表達式解析字串,將樣式名稱轉換為 CSS。
class ClassNameParser {
parse(value: string) {
const classNames = value.trim().split(/\s+/);
return classNames
.map((className) => new ClassNameExpression(className).interpret())
.filter(Boolean)
.join("\n");
}
}
定義一個解釋器並傳入樣式名稱來查看生成的結果。
class ClassNameParserTestDrive {
static main() {
const parser = new ClassNameParser();
const className = "w-[12px] h-[12px] bg-[pink]";
const result = parser.parse(className);
console.log("CSS generated from custom class names:\n");
console.log(result);
}
}
ClassNameParserTestDrive.main();
執行結果:
CSS generated from custom class names:
.w\-\[12px\] {
width: 12px;
}
.h\-\[12px\] {
height: 12px;
}
.bg\-\[pink\] {
background-color: pink;
}
在某些情況下,我們會將常見的任務整理成一個簡單的語言,並透過這個語言來執行命令。解釋器模式可以讀取簡單語言,依據預先定義的規則解讀並執行相應的操作。這種做法可以減少重複性任務,使程式碼變得更有系統。此外,我們還能輕鬆地修改或增加文法規則來新增功能。解釋器模式適合用於特定領域的小型語言,面對文法複雜的大型語言,使用編譯器或轉譯器等技術會更為合適。