手搓CSS解析器:实现完整选择器匹配与样式计算
引言:CSS解析的奥秘
在当今Web开发领域,CSS(层叠样式表)是构建美观用户界面的核心技术之一。浏览器如何将看似简单的CSS规则转换为屏幕上精美的视觉效果?这个看似神奇的过程背后,是一套复杂而精密的解析引擎。本文将深入探讨如何从零开始构建一个完整的CSS解析器,实现选择器匹配与样式计算的全过程。
传统前端开发中,我们通常将CSS视为一种声明式语言,只需编写规则,浏览器便会自动应用。然而,当我们需要构建自定义样式引擎、实现CSS-in-JS解决方案、开发可视化CSS编辑器,或是优化样式性能时,深入理解CSS解析机制变得至关重要。手写CSS解析器不仅能加深对Web标准的理解,还能为解决复杂样式问题提供新的思路。
第一章:CSS解析器架构设计
1.1 CSS处理流程概述
浏览器处理CSS的过程可分为多个阶段:
加载与解析:读取CSS文件,解析为结构化数据
规则匹配:将CSS规则与DOM元素进行匹配
样式计算:计算每个元素最终应用的样式值
布局计算:根据样式计算元素布局
绘制与合成:将元素绘制到屏幕上
本文将重点聚焦前三个阶段:解析、匹配和计算。
1.2 解析器整体架构
我们将构建的CSS解析器包含以下核心模块:
text
CSS解析器架构:
├── 词法分析器 (Lexer)
│ ├── 字符流处理
│ ├── Token生成
│ └── 错误恢复机制
├── 语法分析器 (Parser)
│ ├── 规则解析
│ ├── 选择器解析
│ └── 声明解析
├── 样式规则管理器
│ ├── 规则存储
│ ├── 规则索引
│ └── 规则排序
├── 选择器匹配引擎
│ ├── 选择器解析
│ ├── 元素匹配算法
│ └── 伪类/伪元素处理
└── 样式计算器
├── 层叠计算
├── 继承处理
├── 默认值填充
└── 计算值转换
1.3 数据结构设计
我们需要定义几个核心数据结构:
javascript
// CSS Token类型
const TokenType = {
IDENT: 'IDENT', // 标识符
STRING: 'STRING', // 字符串
NUMBER: 'NUMBER', // 数字
PERCENTAGE: 'PERCENTAGE', // 百分比
DIMENSION: 'DIMENSION', // 带单位的数值
COLON: 'COLON', // 冒号
SEMICOLON: 'SEMICOLON', // 分号
COMMA: 'COMMA', // 逗号
LEFT_BRACE: 'LEFT_BRACE', // 左大括号
RIGHT_BRACE: 'RIGHT_BRACE', // 右大括号
LEFT_PAREN: 'LEFT_PAREN', // 左括号
RIGHT_PAREN: 'RIGHT_PAREN', // 右括号
LEFT_BRACKET: 'LEFT_BRACKET', // 左中括号
RIGHT_BRACKET: 'RIGHT_BRACKET', // 右中括号
HASH: 'HASH', // #号
DOT: 'DOT', // 点号
AT_KEYWORD: 'AT_KEYWORD', // @规则
DELIM: 'DELIM', // 分隔符
WHITESPACE: 'WHITESPACE', // 空白字符
COMMENT: 'COMMENT', // 注释
EOF: 'EOF' // 文件结束
};
// CSS规则表示
class CSSRule {
constructor(selectors, declarations, specificity, position) {
this.selectors = selectors; // 选择器列表
this.declarations = declarations; // 声明集合
this.specificity = specificity; // 选择器优先级
this.position = position; // 规则位置(用于层叠排序)
}
}
// 样式声明
class CSSDeclaration {
constructor(property, value, important = false) {
this.property = property; // 属性名
this.value = value; // 属性值
this.important = important; // !important标记
}
}
// 选择器表示
class Selector {
constructor(selectorText, specificity) {
this.selectorText = selectorText; // 选择器文本
this.specificity = specificity; // 优先级值
this.components = []; // 选择器组件
}
}
第二章:词法分析器实现
2.1 字符流处理
词法分析器的首要任务是读取CSS源码并将其转换为一系列有意义的Token。我们需要一个字符流处理器来管理源码读取:
javascript
class CharStream {
constructor(source) {
this.source = source; // CSS源码
this.position = 0; // 当前位置
this.line = 1; // 当前行号
this.column = 0; // 当前列号
}
// 查看下一个字符但不移动指针
peek(offset = 0) {
return this.source[this.position + offset] || '';
}
// 读取下一个字符并移动指针
next() {
const char = this.peek();
if (char) {
this.position++;
if (char === '\\n') {
this.line++;
this.column = 0;
} else {
this.column++;
}
}
return char;
}
// 检查是否到达结尾
eof() {
return this.position >= this.source.length;
}
// 回溯到上一个位置
rewind(amount = 1) {
this.position = Math.max(0, this.position – amount);
// 注意:回溯时行号列号计算简化处理,实际实现需要更精确
}
}
2.2 Token生成器
基于字符流,我们可以构建Token生成器:
javascript
class CSSLexer {
constructor(source) {
this.stream = new CharStream(source);
this.currentToken = null;
}
// 获取下一个Token
nextToken() {
this.skipWhitespaceAndComments();
if (this.stream.eof()) {
return this.createToken(TokenType.EOF, '');
}
const char = this.stream.peek();
// 标识符或关键字
if (this.isIdentStart(char)) {
return this.scanIdent();
}
// 数字
if (this.isDigit(char) || (char === '.' && this.isDigit(this.stream.peek(1)))) {
return this.scanNumeric();
}
// 字符串
if (char === '"' || char === "'") {
return this.scanString();
}
// Hash值(如#fff)
if (char === '#') {
return this.scanHash();
}
// 符号处理
switch (char) {
case '{':
this.stream.next();
return this.createToken(TokenType.LEFT_BRACE, '{');
case '}':
this.stream.next();
return this.createToken(TokenType.RIGHT_BRACE, '}');
case ':':
this.stream.next();
return this.createToken(TokenType.COLON, ':');
case ';':
this.stream.next();
return this.createToken(TokenType.SEMICOLON, ';');
case ',':
this.stream.next();
return this.createToken(TokenType.COMMA, ',');
case '(':
this.stream.next();
return this.createToken(TokenType.LEFT_PAREN, '(');
case ')':
this.stream.next();
return this.createToken(TokenType.RIGHT_PAREN, ')');
case '[':
this.stream.next();
return this.createToken(TokenType.LEFT_BRACKET, '[');
case ']':
this.stream.next();
return this.createToken(TokenType.RIGHT_BRACKET, ']');
case '.':
this.stream.next();
return this.createToken(TokenType.DOT, '.');
case '@':
return this.scanAtKeyword();
}
// 默认作为分隔符处理
this.stream.next();
return this.createToken(TokenType.DELIM, char);
}
// 扫描标识符
scanIdent() {
let value = '';
// 读取标识符起始字符
value += this.stream.next();
// 读取标识符其余部分
while (!this.stream.eof()) {
const char = this.stream.peek();
if (this.isIdentChar(char)) {
value += this.stream.next();
} else {
break;
}
}
// 检查是否为CSS关键字
if (this.isCSSKeyword(value)) {
return this.createToken(this.getKeywordTokenType(value), value);
}
return this.createToken(TokenType.IDENT, value);
}
// 扫描数字
scanNumeric() {
let value = '';
let type = TokenType.NUMBER;
// 读取数字部分
while (!this.stream.eof() && this.isDigit(this.stream.peek())) {
value += this.stream.next();
}
// 检查小数点
if (this.stream.peek() === '.' && this.isDigit(this.stream.peek(1))) {
value += this.stream.next(); // 小数点
while (!this.stream.eof() && this.isDigit(this.stream.peek())) {
value += this.stream.next();
}
}
// 检查单位
if (!this.stream.eof()) {
const nextChar = this.stream.peek();
if (this.isIdentStart(nextChar)) {
const unitStart = this.stream.position;
const unit = this.scanIdent().value;
// 百分比特殊处理
if (unit === '%') {
type = TokenType.PERCENTAGE;
value += unit;
} else {
type = TokenType.DIMENSION;
value += unit;
}
}
}
return this.createToken(type, value);
}
// 辅助方法
isDigit(char) {
return /[0-9]/.test(char);
}
isIdentStart(char) {
return /[a-zA-Z_]/.test(char) || char.charCodeAt(0) > 127;
}
isIdentChar(char) {
return this.isIdentStart(char) || /[0-9-]/.test(char);
}
skipWhitespaceAndComments() {
while (!this.stream.eof()) {
const char = this.stream.peek();
// 跳过空白字符
if (/\\s/.test(char)) {
this.stream.next();
continue;
}
// 处理注释
if (char === '/' && this.stream.peek(1) === '*') {
this.skipComment();
continue;
}
break;
}
}
skipComment() {
// 跳过 /*
this.stream.next();
this.stream.next();
// 直到遇到 */
while (!this.stream.eof()) {
if (this.stream.peek() === '*' && this.stream.peek(1) === '/') {
this.stream.next();
this.stream.next();
break;
}
this.stream.next();
}
}
createToken(type, value) {
return {
type,
value,
line: this.stream.line,
column: this.stream.column
};
}
}
2.3 词法分析优化
实际应用中,我们需要对词法分析器进行优化:
javascript
class OptimizedCSSLexer extends CSSLexer {
constructor(source) {
super(source);
this.tokenCache = []; // Token缓存
this.cacheIndex = 0; // 缓存索引
this.lookaheadTokens = []; // 预读Token
}
// 带缓存的Token获取
nextToken() {
if (this.cacheIndex < this.tokenCache.length) {
return this.tokenCache[this.cacheIndex++];
}
const token = super.nextToken();
this.tokenCache.push(token);
this.cacheIndex++;
return token;
}
// 查看未来第n个Token
peekToken(n = 0) {
while (this.lookaheadTokens.length <= n) {
const token = super.nextToken();
this.lookaheadTokens.push(token);
}
return this.lookaheadTokens[n];
}
// 消耗一个Token
consumeToken() {
if (this.lookaheadTokens.length > 0) {
const token = this.lookaheadTokens.shift();
this.tokenCache.push(token);
this.cacheIndex++;
return token;
}
return this.nextToken();
}
// 回溯到指定位置
rewindTo(position) {
this.cacheIndex = Math.max(0, Math.min(position, this.tokenCache.length));
}
}
第三章:语法分析器构建
3.1 CSS语法规则定义
CSS语法可以形式化表示为:
text
stylesheet : [ CDO | CDC | S | statement ]*;
statement : ruleset | at-rule;
ruleset : selector? '{' S* declaration? [ ';' S* declaration? ]* '}' S*;
selector : simple_selector [ combinator simple_selector ]*;
simple_selector : element_name? [ HASH | class | attrib | pseudo ]* S*;
declaration : property S* ':' S* value;
property : IDENT;
value : [ any | block | ATKEYWORD S* ]+;
3.2 语法分析器实现
语法分析器将Token流转换为抽象语法树(AST):
javascript
class CSSParser {
constructor(lexer) {
this.lexer = lexer;
this.currentToken = null;
this.rules = [];
this.errors = [];
}
// 解析整个样式表
parseStylesheet() {
const rules = [];
while (!this.isTokenType(TokenType.EOF)) {
this.consumeWhitespace();
if (this.isTokenType(TokenType.AT_KEYWORD)) {
const atRule = this.parseAtRule();
if (atRule) rules.push(atRule);
} else if (this.isTokenType(TokenType.IDENT) || this.isTokenType(TokenType.HASH) ||
this.isTokenType(TokenType.DOT) || this.isTokenType(TokenType.LEFT_BRACKET) ||
this.isTokenType(TokenType.COLON) || this.isTokenType(TokenType.DELIM)) {
const rule = this.parseRule();
if (rule) rules.push(rule);
} else if (!this.isTokenType(TokenType.EOF)) {
this.reportError(`Unexpected token: ${this.currentToken.value}`);
this.consumeToken();
}
}
return rules;
}
// 解析CSS规则
parseRule() {
try {
// 解析选择器
const selectors = this.parseSelectors();
// 期望左大括号
this.expectToken(TokenType.LEFT_BRACE);
// 解析声明块
const declarations = this.parseDeclarationBlock();
// 期望右大括号
this.expectToken(TokenType.RIGHT_BRACE);
// 计算选择器优先级
const specificity = this.calculateSpecificity(selectors);
return new CSSRule(
selectors,
declarations,
specificity,
{ line: this.currentToken.line, column: this.currentToken.column }
);
} catch (error) {
this.reportError(error.message);
this.recoverFromError();
return null;
}
}
// 解析选择器列表
parseSelectors() {
const selectors = [];
let currentSelector = '';
while (!this.isTokenType(TokenType.LEFT_BRACE) && !this.isTokenType(TokenType.EOF)) {
const token = this.consumeToken();
if (token.type === TokenType.COMMA) {
if (currentSelector.trim()) {
selectors.push(this.parseSelector(currentSelector));
currentSelector = '';
}
} else {
currentSelector += token.value;
}
}
// 处理最后一个选择器
if (currentSelector.trim()) {
selectors.push(this.parseSelector(currentSelector));
}
return selectors;
}
// 解析单个选择器
parseSelector(selectorText) {
// 移除首尾空白
selectorText = selectorText.trim();
// 解析选择器组件
const components = this.parseSelectorComponents(selectorText);
// 计算优先级
const specificity = this.calculateSelectorSpecificity(components);
return new Selector(selectorText, specificity, components);
}
// 解析选择器组件
parseSelectorComponents(selectorText) {
const components = [];
let buffer = '';
let inAttribute = false;
let inPseudo = false;
let parenDepth = 0;
for (let i = 0; i < selectorText.length; i++) {
const char = selectorText[i];
if (char === '[' && !inPseudo) {
inAttribute = true;
buffer += char;
} else if (char === ']' && inAttribute) {
inAttribute = false;
buffer += char;
components.push({ type: 'attribute', value: buffer });
buffer = '';
} else if (char === ':' && !inAttribute) {
if (buffer) {
components.push({ type: 'element', value: buffer });
buffer = '';
}
inPseudo = true;
buffer += char;
} else if (char === '(' && inPseudo) {
parenDepth++;
buffer += char;
} else if (char === ')' && inPseudo) {
parenDepth–;
buffer += char;
if (parenDepth === 0) {
components.push({ type: 'pseudo', value: buffer });
buffer = '';
inPseudo = false;
}
} else if ((char === ' ' || char === '>' || char === '+' || char === '~') &&
!inAttribute && !inPseudo && parenDepth === 0) {
if (buffer) {
components.push({ type: 'element', value: buffer });
buffer = '';
}
components.push({ type: 'combinator', value: char });
} else if (char === '.' && !inAttribute && !inPseudo) {
if (buffer) {
components.push({ type: 'element', value: buffer });
buffer = '';
}
buffer = char;
} else if (char === '#' && !inAttribute && !inPseudo) {
if (buffer) {
components.push({ type: 'element', value: buffer });
buffer = '';
}
buffer = char;
} else {
buffer += char;
}
}
// 处理最后一部分
if (buffer) {
if (buffer.startsWith('.')) {
components.push({ type: 'class', value: buffer });
} else if (buffer.startsWith('#')) {
components.push({ type: 'id', value: buffer });
} else if (!inAttribute && !inPseudo) {
components.push({ type: 'element', value: buffer });
}
}
return components;
}
// 解析声明块
parseDeclarationBlock() {
const declarations = [];
this.consumeWhitespace();
while (!this.isTokenType(TokenType.RIGHT_BRACE) && !this.isTokenType(TokenType.EOF)) {
const declaration = this.parseDeclaration();
if (declaration) {
declarations.push(declaration);
}
// 跳过可选的分号
if (this.isTokenType(TokenType.SEMICOLON)) {
this.consumeToken();
}
this.consumeWhitespace();
}
return declarations;
}
// 解析单个声明
parseDeclaration() {
// 期望属性名
if (!this.isTokenType(TokenType.IDENT)) {
this.reportError(`Expected property name, got ${this.currentToken.type}`);
return null;
}
const property = this.consumeToken().value;
this.consumeWhitespace();
// 期望冒号
this.expectToken(TokenType.COLON);
this.consumeWhitespace();
// 解析属性值
const valueTokens = [];
let important = false;
// 收集值Token,直到遇到分号或右大括号
while (!this.isTokenType(TokenType.SEMICOLON) &&
!this.isTokenType(TokenType.RIGHT_BRACE) &&
!this.isTokenType(TokenType.EOF)) {
const token = this.consumeToken();
// 检查!important
if (token.type === TokenType.DELIM && token.value === '!') {
const nextToken = this.peekToken();
if (nextToken && nextToken.type === TokenType.IDENT &&
nextToken.value.toLowerCase() === 'important') {
this.consumeToken(); // 消耗'important'
important = true;
break;
}
}
valueTokens.push(token);
}
// 组合值Token
const value = valueTokens.map(t => t.value).join('').trim();
return new CSSDeclaration(property, value, important);
}
// 计算选择器优先级
calculateSelectorSpecificity(components) {
let a = 0; // ID选择器
let b = 0; // 类选择器、属性选择器、伪类
let c = 0; // 类型选择器、伪元素
for (const component of components) {
switch (component.type) {
case 'id':
a++;
break;
case 'class':
case 'attribute':
case 'pseudo':
// 检查是否为伪类(:hover等)而不是伪元素(::before)
if (component.type === 'pseudo' && component.value.startsWith('::')) {
c++;
} else if (component.type === 'pseudo') {
b++;
} else {
b++;
}
break;
case 'element':
// 忽略通用选择器(*)
if (component.value !== '*') {
c++;
}
break;
}
}
return (a << 16) | (b << 8) | c;
}
// 辅助方法
consumeToken() {
if (!this.currentToken) {
this.currentToken = this.lexer.nextToken();
}
const token = this.currentToken;
this.currentToken = this.lexer.nextToken();
return token;
}
peekToken() {
if (!this.currentToken) {
this.currentToken = this.lexer.nextToken();
}
return this.currentToken;
}
isTokenType(type) {
const token = this.peekToken();
return token.type === type;
}
expectToken(type) {
const token = this.consumeToken();
if (token.type !== type) {
throw new Error(`Expected ${type}, got ${token.type}`);
}
return token;
}
consumeWhitespace() {
while (this.isTokenType(TokenType.WHITESPACE)) {
this.consumeToken();
}
}
reportError(message) {
this.errors.push({
message,
line: this.currentToken ? this.currentToken.line : 0,
column: this.currentToken ? this.currentToken.column : 0
});
}
recoverFromError() {
// 跳过直到找到右大括号或分号
while (!this.isTokenType(TokenType.RIGHT_BRACE) &&
!this.isTokenType(TokenType.SEMICOLON) &&
!this.isTokenType(TokenType.EOF)) {
this.consumeToken();
}
}
}
3.3 语法树优化
为了提高解析效率,我们可以对语法树进行优化:
javascript
class OptimizedCSSParser extends CSSParser {
constructor(lexer) {
super(lexer);
this.selectorCache = new Map(); // 选择器解析缓存
this.declarationCache = new Map(); // 声明解析缓存
}
// 缓存选择器解析结果
parseSelector(selectorText) {
if (this.selectorCache.has(selectorText)) {
return this.selectorCache.get(selectorText);
}
const selector = super.parseSelector(selectorText);
this.selectorCache.set(selectorText, selector);
return selector;
}
// 优化声明解析
parseDeclaration() {
const startPos = this.lexer.stream.position;
const declaration = super.parseDeclaration();
if (declaration) {
const cacheKey = `${declaration.property}:${declaration.value}:${declaration.important}`;
this.declarationCache.set(cacheKey, declaration);
}
return declaration;
}
}
第四章:选择器匹配引擎
4.1 选择器匹配算法
选择器匹配是CSS引擎的核心功能。我们需要实现多种选择器类型的匹配逻辑:
javascript
class SelectorMatcher {
constructor() {
this.matchers = {
// 元素选择器匹配
element: (element, selector) => {
const tagName = element.tagName.toLowerCase();
const selectorName = selector.value.toLowerCase();
return selectorName === '*' || tagName === selectorName;
},
// 类选择器匹配
class: (element, selector) => {
const className = selector.value.substring(1); // 去掉开头的.
return element.classList.contains(className);
},
// ID选择器匹配
id: (element, selector) => {
const idValue = selector.value.substring(1); // 去掉开头的#
return element.id === idValue;
},
// 属性选择器匹配
attribute: (element, selector) => {
const attrSelector = selector.value;
// 简化的属性选择器解析
// 支持 [attr], [attr=value], [attr~=value], [attr|=value], [attr^=value], [attr$=value], [attr*=value]
// 提取属性名和操作符
const match = attrSelector.match(/\\[([^\\]=~|\\^\\$\\*]+)(?:([~|^$*]?=)["']?([^"'\\]]+)["']?)?\\]/);
if (!match) return false;
const [, attrName, operator, expectedValue] = match;
const actualValue = element.getAttribute(attrName);
if (actualValue === null) return false;
if (!operator) {
// [attr] 形式:只要属性存在
return true;
}
switch (operator) {
case '=':
return actualValue === expectedValue;
case '~=':
return actualValue.split(/\\s+/).includes(expectedValue);
case '|=':
return actualValue === expectedValue ||
actualValue.startsWith(expectedValue + '-');
case '^=':
return actualValue.startsWith(expectedValue);
case '$=':
return actualValue.endsWith(expectedValue);
case '*=':
return actualValue.includes(expectedValue);
default:
return false;
}
},
// 伪类匹配
pseudo: (element, selector, context) => {
const pseudoValue = selector.value.toLowerCase();
// 结构伪类
if (pseudoValue.startsWith(':nth-child')) {
return this.matchNthChild(element, pseudoValue);
} else if (pseudoValue.startsWith(':nth-of-type')) {
return this.matchNthOfType(element, pseudoValue);
} else if (pseudoValue === ':first-child') {
return this.matchFirstChild(element);
} else if (pseudoValue === ':last-child') {
return this.matchLastChild(element);
} else if (pseudoValue === ':only-child') {
return this.matchOnlyChild(element);
}
// 状态伪类
if (pseudoValue === ':hover') {
return context.isHovered && context.isHovered(element);
} else if (pseudoValue === ':focus') {
return context.isFocused && context.isFocused(element);
} else if (pseudoValue === ':checked') {
return element.checked === true;
} else if (pseudoValue === ':disabled') {
return element.disabled === true;
} else if (pseudoValue === ':enabled') {
return !element.disabled;
}
// 默认返回false,不支持所有伪类
return false;
}
};
}
// 匹配第n个子元素
matchNthChild(element, pseudoValue) {
const match = pseudoValue.match(/:nth-child\\(([^)]+)\\)/);
if (!match) return false;
const formula = match[1].trim();
const parent = element.parentElement;
if (!parent) return false;
const children = Array.from(parent.children);
const index = children.indexOf(element) + 1; // nth-child是从1开始的
return this.evaluateNthFormula(formula, index);
}
// 匹配第n个类型元素
matchNthOfType(element, pseudoValue) {
const match = pseudoValue.match(/:nth-of-type\\(([^)]+)\\)/);
if (!match) return false;
const formula = match[1].trim();
const parent = element.parentElement;
if (!parent) return false;
const siblings = Array.from(parent.children)
.filter(child => child.tagName === element.tagName);
const index = siblings.indexOf(element) + 1;
return this.evaluateNthFormula(formula, index);
}
// 评估nth公式
evaluateNthFormula(formula, index) {
// 处理特殊关键字
if (formula === 'odd') {
return index % 2 === 1;
} else if (formula === 'even') {
return index % 2 === 0;
}
// 处理An+B格式
const match = formula.match(/^(-?\\d*)?n([+-]\\d+)?$/);
if (match) {
const [, aStr, bStr] = match;
const a = aStr === '' ? 1 : aStr === '-' ? -1 : parseInt(aStr || 0);
const b = bStr ? parseInt(bStr) : 0;
if (a === 0) {
return index === b;
}
// 检查是否满足 index = a * n + b
const n = (index – b) / a;
return n >= 0 && Number.isInteger(n);
}
// 处理单个数字
const singleNumber = parseInt(formula);
if (!isNaN(singleNumber)) {
return index === singleNumber;
}
return false;
}
// 匹配第一个子元素
matchFirstChild(element) {
const parent = element.parentElement;
if (!parent) return false;
return parent.firstElementChild === element;
}
// 匹配最后一个子元素
matchLastChild(element) {
const parent = element.parentElement;
if (!parent) return false;
return parent.lastElementChild === element;
}
// 匹配唯一子元素
matchOnlyChild(element) {
const parent = element.parentElement;
if (!parent) return false;
return parent.children.length === 1 && parent.firstElementChild === element;
}
// 匹配组合选择器
matchCombinedSelector(element, selector, context) {
const components = selector.components;
let currentElement = element;
let componentIndex = components.length – 1;
while (componentIndex >= 0 && currentElement) {
const component = components[componentIndex];
if (component.type === 'combinator') {
// 处理组合符
switch (component.value) {
case ' ':
// 后代选择器:移动到父元素
currentElement = currentElement.parentElement;
componentIndex–;
break;
case '>':
// 子选择器:检查父元素
const parent = currentElement.parentElement;
if (!parent) return false;
// 检查父元素是否匹配前一个组件
componentIndex–;
if (componentIndex >= 0) {
const prevComponent = components[componentIndex];
if (!this.matchComponent(parent, prevComponent, context)) {
return false;
}
currentElement = parent;
componentIndex–;
}
break;
case '+':
// 相邻兄弟选择器
const prevSibling = currentElement.previousElementSibling;
if (!prevSibling) return false;
componentIndex–;
if (componentIndex >= 0) {
const prevComponent = components[componentIndex];
if (!this.matchComponent(prevSibling, prevComponent, context)) {
return false;
}
currentElement = prevSibling;
componentIndex–;
}
break;
case '~':
// 通用兄弟选择器
let found = false;
let sibling = currentElement.previousElementSibling;
componentIndex–;
if (componentIndex < 0) return false;
const targetComponent = components[componentIndex];
while (sibling) {
if (this.matchComponent(sibling, targetComponent, context)) {
found = true;
currentElement = sibling;
break;
}
sibling = sibling.previousElementSibling;
}
if (!found) return false;
componentIndex–;
break;
}
} else {
// 检查当前元素是否匹配组件
if (!this.matchComponent(currentElement, component, context)) {
return false;
}
componentIndex–;
}
}
return componentIndex < 0;
}
// 匹配单个组件
matchComponent(element, component, context) {
const matcher = this.matchers[component.type];
if (!matcher) {
console.warn(`No matcher for component type: ${component.type}`);
return false;
}
return matcher(element, component, context);
}
// 检查元素是否匹配选择器
matchesSelector(element, selector, context = {}) {
return this.matchCombinedSelector(element, selector, context);
}
}
4.2 规则索引与快速匹配
为了提高匹配效率,我们需要建立规则索引:
javascript
class RuleIndex {
constructor() {
// 按选择器类型索引
this.idIndex = new Map(); // #id选择器
this.classIndex = new Map(); // .class选择器
this.tagIndex = new Map(); // tag选择器
this.attributeIndex = new Map(); // [attr]选择器
this.universalRules = []; // *选择器
this.otherRules = []; // 其他选择器
}
// 添加规则到索引
addRule(rule) {
for (const selector of rule.selectors) {
this.indexSelector(selector, rule);
}
}
// 索引选择器
indexSelector(selector, rule) {
const components = selector.components;
// 从右向左查找关键组件(最右侧的简单选择器)
for (let i = components.length – 1; i >= 0; i–) {
const component = components[i];
if (component.type === 'id') {
// ID选择器
const id = component.value.substring(1); // 去掉#
if (!this.idIndex.has(id)) {
this.idIndex.set(id, []);
}
this.idIndex.get(id).push({ selector, rule });
return;
} else if (component.type === 'class') {
// 类选择器
const className = component.value.substring(1); // 去掉.
if (!this.classIndex.has(className)) {
this.classIndex.set(className, []);
}
this.classIndex.get(className).push({ selector, rule });
return;
} else if (component.type === 'element' && component.value !== '*') {
// 元素选择器(非通配符)
const tagName = component.value.toLowerCase();
if (!this.tagIndex.has(tagName)) {
this.tagIndex.set(tagName, []);
}
this.tagIndex.get(tagName).push({ selector, rule });
return;
} else if (component.type === 'attribute') {
// 属性选择器
const attrMatch = component.value.match(/\\[([^\\]=~|\\^\\$\\*]+)/);
if (attrMatch) {
const attrName = attrMatch[1];
if (!this.attributeIndex.has(attrName)) {
this.attributeIndex.set(attrName, []);
}
this.attributeIndex.get(attrName).push({ selector, rule });
return;
}
} else if (component.type === 'element' && component.value === '*') {
// 通配符选择器
this.universalRules.push({ selector, rule });
return;
}
}
// 没有找到关键组件,放入其他规则
this.otherRules.push({ selector, rule });
}
// 查找匹配元素的规则
findMatchingRules(element, matcher, context = {}) {
const matchingRules = [];
// 检查ID索引
if (element.id) {
const idRules = this.idIndex.get(element.id);
if (idRules) {
this.testRules(idRules, element, matcher, context, matchingRules);
}
}
// 检查类索引
if (element.classList && element.classList.length > 0) {
for (const className of element.classList) {
const classRules = this.classIndex.get(className);
if (classRules) {
this.testRules(classRules, element, matcher, context, matchingRules);
}
}
}
// 检查标签索引
const tagName = element.tagName.toLowerCase();
const tagRules = this.tagIndex.get(tagName);
if (tagRules) {
this.testRules(tagRules, element, matcher, context, matchingRules);
}
// 检查属性索引
if (element.attributes) {
for (const attr of element.attributes) {
const attrRules = this.attributeIndex.get(attr.name);
if (attrRules) {
this.testRules(attrRules, element, matcher, context, matchingRules);
}
}
}
// 检查通配符规则
this.testRules(this.universalRules, element, matcher, context, matchingRules);
// 检查其他规则
this.testRules(this.otherRules, element, matcher, context, matchingRules);
return matchingRules;
}
// 测试规则是否匹配
testRules(ruleEntries, element, matcher, context, matchingRules) {
for (const entry of ruleEntries) {
if (matcher.matchesSelector(element, entry.selector, context)) {
if (!matchingRules.includes(entry.rule)) {
matchingRules.push(entry.rule);
}
}
}
}
}
第五章:样式计算引擎
5.1 层叠与优先级计算
CSS的核心特性之一是层叠(Cascade),它决定了当多个规则应用于同一元素时,哪个规则最终生效:
javascript
class StyleCascade {
constructor() {
this.defaultStyles = this.createDefaultStyles();
this.userAgentStyles = this.createUserAgentStyles();
this.inheritedProperties = this.getInheritedProperties();
}
// 计算元素的最终样式
computeElementStyles(element, matchingRules, importantDeclarations = []) {
// 收集所有适用的声明
const declarations = this.collectDeclarations(
element,
matchingRules,
importantDeclarations
);
// 按优先级排序
this.sortDeclarationsBySpecificity(declarations);
// 应用层叠
const computedStyles = this.applyCascade(declarations);
// 处理继承
this.applyInheritance(element, computedStyles);
// 应用默认值
this.applyDefaults(computedStyles);
return computedStyles;
}
// 收集所有声明
collectDeclarations(element, matchingRules, importantDeclarations) {
const declarations = [];
// 1. 用户代理样式(最低优先级)
for (const rule of this.userAgentStyles) {
if (this.elementMatchesRule(element, rule)) {
declarations.push(…rule.declarations.map(d => ({
…d,
source: 'user-agent',
specificity: { a: 0, b: 0, c: 0 }
})));
}
}
// 2. 用户样式(普通)
for (const rule of matchingRules) {
declarations.push(…rule.declarations.map(d => ({
…d,
source: 'author',
specificity: rule.specificity,
rulePosition: rule.position
})));
}
// 3. 重要声明(作者)
for (const declaration of importantDeclarations) {
declarations.push({
…declaration,
source: 'author',
important: true,
specificity: { a: 0, b: 0, c: 0 } // 内联样式无选择器
});
}
return declarations;
}
// 按优先级排序声明
sortDeclarationsBySpecificity(declarations) {
declarations.sort((a, b) => {
// 1. 重要性比较
if (a.important && !b.important) return 1;
if (!a.important && b.important) return -1;
// 2. 来源比较
const sourceOrder = { 'user-agent': 0, 'author': 1 };
if (sourceOrder[a.source] !== sourceOrder[b.source]) {
return sourceOrder[b.source] – sourceOrder[a.source];
}
// 3. 选择器优先级比较
if (a.specificity !== b.specificity) {
return this.compareSpecificity(a.specificity, b.specificity);
}
// 4. 位置比较(后出现的覆盖先出现的)
if (a.rulePosition && b.rulePosition) {
if (a.rulePosition.line !== b.rulePosition.line) {
return a.rulePosition.line – b.rulePosition.line;
}
return a.rulePosition.column – b.rulePosition.column;
}
return 0;
});
}
// 比较选择器优先级
compareSpecificity(specA, specB) {
// 假设specificity是一个包含a, b, c三个数字的对象
if (specA.a !== specB.a) {
return specA.a – specB.a;
}
if (specA.b !== specB.b) {
return specA.b – specB.b;
}
return specA.c – specB.c;
}
// 应用层叠规则
applyCascade(declarations) {
const computedStyles = {};
const seenProperties = new Set();
// 从高优先级到低优先级遍历
for (let i = declarations.length – 1; i >= 0; i–) {
const declaration = declarations[i];
const property = declaration.property;
// 如果属性尚未设置,则应用当前声明
if (!seenProperties.has(property)) {
computedStyles[property] = declaration.value;
seenProperties.add(property);
}
}
return computedStyles;
}
// 应用继承
applyInheritance(element, computedStyles) {
const parent = element.parentElement;
if (!parent) return;
const parentStyles = parent.computedStyles || {};
for (const property of this.inheritedProperties) {
if (!(property in computedStyles) && property in parentStyles) {
computedStyles[property] = parentStyles[property];
}
}
}
// 应用默认值
applyDefaults(computedStyles) {
for (const [property, defaultValue] of Object.entries(this.defaultStyles)) {
if (!(property in computedStyles)) {
computedStyles[property] = defaultValue;
}
}
}
// 创建默认样式表
createDefaultStyles() {
return {
'display': 'inline',
'color': 'black',
'font-size': 'medium',
'font-family': 'serif',
'background-color': 'transparent',
'border': 'none',
'margin': '0',
'padding': '0',
'width': 'auto',
'height': 'auto'
// 更多默认值…
};
}
// 创建用户代理样式表
createUserAgentStyles() {
// 简化的用户代理样式
return [
{
selectors: [new Selector('html', { a: 0, b: 0, c: 1 })],
declarations: [
new CSSDeclaration('display', 'block'),
new CSSDeclaration('font-size', '16px')
]
},
{
selectors: [new Selector('body', { a: 0, b: 0, c: 1 })],
declarations: [
new CSSDeclaration('margin', '8px'),
new CSSDeclaration('line-height', '1.2')
]
},
{
selectors: [new Selector('div', { a: 0, b: 0, c: 1 })],
declarations: [
new CSSDeclaration('display', 'block')
]
},
{
selectors: [new Selector('span', { a: 0, b: 0, c: 1 })],
declarations: [
new CSSDeclaration('display', 'inline')
]
},
{
selectors: [new Selector('h1', { a: 0, b: 0, c: 1 })],
declarations: [
new CSSDeclaration('display', 'block'),
new CSSDeclaration('font-size', '2em'),
new CSSDeclaration('font-weight', 'bold'),
new CSSDeclaration('margin', '0.67em 0')
]
}
// 更多用户代理规则…
];
}
// 获取可继承属性列表
getInheritedProperties() {
return [
'color',
'font-family',
'font-size',
'font-style',
'font-weight',
'line-height',
'text-align',
'visibility'
// 更多可继承属性…
];
}
// 检查元素是否匹配规则
elementMatchesRule(element, rule) {
// 简化实现,实际应使用选择器匹配引擎
for (const selector of rule.selectors) {
if (selector.selectorText === element.tagName.toLowerCase()) {
return true;
}
}
return false;
}
}
5.2 值计算与转换
CSS值计算涉及单位转换、相对值计算等复杂操作:
javascript
class CSSValueCalculator {
constructor() {
this.unitConverters = {
// 长度单位转换(px为基准)
'px': value => value,
'em': (value, context) => value * context.fontSize,
'rem': (value, context) => value * context.rootFontSize,
'pt': value => value * 1.33333, // 1pt = 1.333px
'pc': value => value * 16, // 1pc = 16px
'in': value => value * 96, // 1in = 96px
'cm': value => value * 37.8, // 1cm ≈ 37.8px
'mm': value => value * 3.78, // 1mm ≈ 3.78px
'%': (value, context, property) => {
// 百分比根据属性不同有不同的基准
switch (property) {
case 'width':
return value * context.parentWidth / 100;
case 'height':
return value * context.parentHeight / 100;
case 'font-size':
return value * context.parentFontSize / 100;
default:
return value;
}
}
};
this.colorParsers = {
'hex': this.parseHexColor.bind(this),
'rgb': this.parseRgbColor.bind(this),
'rgba': this.parseRgbaColor.bind(this),
'hsl': this.parseHslColor.bind(this),
'hsla': this.parseHslaColor.bind(this),
'named': this.parseNamedColor.bind(this)
};
}
// 计算CSS值
computeValue(value, property, context = {}) {
if (value === 'inherit') {
return context.inheritedValue || '';
}
if (value === 'initial') {
return this.getInitialValue(property);
}
if (value === 'unset') {
return this.isInheritedProperty(property) ? 'inherit' : 'initial';
}
// 尝试解析为带单位的数值
const unitMatch = value.match(/^([-+]?[0-9]*\\.?[0-9]+)([a-z%]*)$/);
if (unitMatch) {
const [, numStr, unit] = unitMatch;
const numValue = parseFloat(numStr);
if (unit in this.unitConverters) {
const converter = this.unitConverters[unit];
return converter(numValue, context, property);
}
return numValue; // 无单位数值
}
// 尝试解析颜色
const color = this.parseColor(value);
if (color) {
return color;
}
// 其他值(字符串、关键字等)
return value;
}
// 解析颜色值
parseColor(colorStr) {
colorStr = colorStr.trim().toLowerCase();
// 十六进制颜色
if (colorStr.startsWith('#')) {
return this.colorParsers.hex(colorStr);
}
// rgb/rgba颜色
if (colorStr.startsWith('rgb')) {
const isRgba = colorStr.startsWith('rgba');
return isRgba ?
this.colorParsers.rgba(colorStr) :
this.colorParsers.rgb(colorStr);
}
// hsl/hsla颜色
if (colorStr.startsWith('hsl')) {
const isHsla = colorStr.startsWith('hsla');
return isHsla ?
this.colorParsers.hsla(colorStr) :
this.colorParsers.hsl(colorStr);
}
// 颜色名称
return this.colorParsers.named(colorStr);
}
// 解析十六进制颜色
parseHexColor(hex) {
// 移除#号
hex = hex.substring(1);
let r, g, b, a = 1;
if (hex.length === 3) {
// #RGB格式
r = parseInt(hex[0] + hex[0], 16);
g = parseInt(hex[1] + hex[1], 16);
b = parseInt(hex[2] + hex[2], 16);
} else if (hex.length === 4) {
// #RGBA格式
r = parseInt(hex[0] + hex[0], 16);
g = parseInt(hex[1] + hex[1], 16);
b = parseInt(hex[2] + hex[2], 16);
a = parseInt(hex[3] + hex[3], 16) / 255;
} else if (hex.length === 6) {
// #RRGGBB格式
r = parseInt(hex.substring(0, 2), 16);
g = parseInt(hex.substring(2, 4), 16);
b = parseInt(hex.substring(4, 6), 16);
} else if (hex.length === 8) {
// #RRGGBBAA格式
r = parseInt(hex.substring(0, 2), 16);
g = parseInt(hex.substring(2, 4), 16);
b = parseInt(hex.substring(4, 6), 16);
a = parseInt(hex.substring(6, 8), 16) / 255;
} else {
return null;
}
return `rgba(${r}, ${g}, ${b}, ${a})`;
}
// 解析rgb颜色
parseRgbColor(rgbStr) {
const match = rgbStr.match(/rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)/);
if (!match) return null;
const [, r, g, b] = match;
return `rgb(${r}, ${g}, ${b})`;
}
// 解析命名颜色
parseNamedColor(colorName) {
const namedColors = {
'black': '#000000',
'white': '#ffffff',
'red': '#ff0000',
'green': '#008000',
'blue': '#0000ff',
'yellow': '#ffff00',
'cyan': '#00ffff',
'magenta': '#ff00ff',
'silver': '#c0c0c0',
'gray': '#808080',
'maroon': '#800000',
'olive': '#808000',
'purple': '#800080',
'teal': '#008080',
'navy': '#000080',
// 更多颜色…
};
return namedColors[colorName] || null;
}
// 获取属性初始值
getInitialValue(property) {
const initialValues = {
'display': 'inline',
'position': 'static',
'float': 'none',
'clear': 'none',
'width': 'auto',
'height': 'auto',
'margin': '0',
'padding': '0',
'border': 'none',
'background': 'none',
'color': 'black',
'font-size': 'medium',
'font-weight': 'normal',
'text-align': 'start',
'vertical-align': 'baseline'
// 更多初始值…
};
return initialValues[property] || '';
}
// 检查属性是否可继承
isInheritedProperty(property) {
const inheritedProperties = [
'color',
'font-family',
'font-size',
'font-style',
'font-weight',
'line-height',
'text-align',
'visibility'
];
return inheritedProperties.includes(property);
}
}
第六章:集成与优化
6.1 完整CSS引擎集成
将各个模块组合成完整的CSS引擎:
javascript
class CSSEngine {
constructor() {
this.lexer = null;
this.parser = null;
this.ruleIndex = new RuleIndex();
this.selectorMatcher = new SelectorMatcher();
this.styleCascade = new StyleCascade();
this.valueCalculator = new CSSValueCalculator();
this.stylesheet = null;
this.computedStyles = new WeakMap(); // 缓存计算后的样式
}
// 加载和解析CSS
loadCSS(cssText) {
this.lexer = new OptimizedCSSLexer(cssText);
this.parser = new OptimizedCSSParser(this.lexer);
this.stylesheet = this.parser.parseStylesheet();
// 构建规则索引
for (const rule of this.stylesheet) {
this.ruleIndex.addRule(rule);
}
return this.stylesheet;
}
// 计算元素的样式
computeStyles(element, pseudoState = {}) {
// 检查缓存
if (this.computedStyles.has(element)) {
return this.computedStyles.get(element);
}
// 查找匹配的规则
const matchingRules = this.ruleIndex.findMatchingRules(
element,
this.selectorMatcher,
pseudoState
);
// 收集内联样式
const inlineStyles = this.parseInlineStyles(element);
// 计算最终样式
const computedStyles = this.styleCascade.computeElementStyles(
element,
matchingRules,
inlineStyles
);
// 计算值(单位转换等)
const context = this.createComputationContext(element);
const finalStyles = {};
for (const [property, value] of Object.entries(computedStyles)) {
finalStyles[property] = this.valueCalculator.computeValue(
value,
property,
context
);
}
// 缓存结果
this.computedStyles.set(element, finalStyles);
return finalStyles;
}
// 解析内联样式
parseInlineStyles(element) {
const styleAttr = element.getAttribute('style');
if (!styleAttr) return [];
const declarations = [];
const parts = styleAttr.split(';');
for (const part of parts) {
const trimmed = part.trim();
if (!trimmed) continue;
const colonIndex = trimmed.indexOf(':');
if (colonIndex === -1) continue;
const property = trimmed.substring(0, colonIndex).trim();
let value = trimmed.substring(colonIndex + 1).trim();
let important = false;
// 检查!important
const importantIndex = value.toLowerCase().indexOf('!important');
if (importantIndex !== -1) {
value = value.substring(0, importantIndex).trim();
important = true;
}
declarations.push(new CSSDeclaration(property, value, important));
}
return declarations;
}
// 创建计算上下文
createComputationContext(element) {
const parent = element.parentElement;
const parentStyles = parent ? this.computeStyles(parent) : {};
return {
fontSize: this.parseFontSize(parentStyles['font-size'] || '16px'),
rootFontSize: 16, // 假设根字体大小为16px
parentWidth: this.parseLength(parentStyles['width'] || 'auto'),
parentHeight: this.parseLength(parentStyles['height'] || 'auto'),
parentFontSize: this.parseFontSize(parentStyles['font-size'] || '16px')
};
}
// 解析字体大小
parseFontSize(fontSizeStr) {
const match = fontSizeStr.match(/^([-+]?[0-9]*\\.?[0-9]+)([a-z%]*)$/);
if (!match) return 16;
const [, numStr, unit] = match;
const numValue = parseFloat(numStr);
if (unit === 'px') return numValue;
if (unit === 'em') return numValue * 16; // 简化处理
if (unit === 'rem') return numValue * 16;
if (unit === '%') return numValue * 16 / 100;
return 16; // 默认值
}
// 解析长度值
parseLength(lengthStr) {
if (lengthStr === 'auto') return null;
const match = lengthStr.match(/^([-+]?[0-9]*\\.?[0-9]+)([a-z%]*)$/);
if (!match) return 0;
const [, numStr] = match;
return parseFloat(numStr);
}
// 强制重新计算元素样式
recomputeStyles(element) {
this.computedStyles.delete(element);
return this.computeStyles(element);
}
// 获取元素的计算样式(类似window.getComputedStyle)
getComputedStyle(element, pseudoElt = null) {
const pseudoState = pseudoElt ? this.getPseudoState(pseudoElt) : {};
return this.computeStyles(element, pseudoState);
}
// 获取伪元素状态
getPseudoState(pseudoElt) {
// 简化实现
const state = {};
if (pseudoElt === ':hover') {
state.isHovered = () => true;
} else if (pseudoElt === ':focus') {
state.isFocused = () => true;
}
return state;
}
}
6.2 性能优化策略
CSS引擎的性能至关重要,特别是在处理大型DOM和复杂样式表时:
javascript
class OptimizedCSSEngine extends CSSEngine {
constructor() {
super();
this.styleCache = new LRUCache(1000); // LRU缓存
this.dependencyGraph = new Map(); // 样式依赖图
this.invalidationTracker = new InvalidationTracker();
}
// 带缓存的样式计算
computeStyles(element, pseudoState = {}) {
// 生成缓存键
const cacheKey = this.generateCacheKey(element, pseudoState);
// 检查缓存
if (this.styleCache.has(cacheKey)) {
return this.styleCache.get(cacheKey);
}
// 计算样式
const styles = super.computeStyles(element, pseudoState);
// 更新缓存
this.styleCache.set(cacheKey, styles);
// 跟踪依赖
this.trackDependencies(element, styles);
return styles;
}
// 生成缓存键
generateCacheKey(element, pseudoState) {
const parts = [
element.tagName,
element.id,
Array.from(element.classList).join('.'),
JSON.stringify(Array.from(element.attributes).map(attr =>
`${attr.name}=${attr.value}`
).sort())
];
// 添加伪状态
if (pseudoState) {
parts.push(JSON.stringify(pseudoState));
}
// 添加上下文信息
const parent = element.parentElement;
if (parent) {
const parentKey = this.generateCacheKey(parent, {});
parts.push(`parent:${parentKey}`);
}
return parts.join('|');
}
// 跟踪样式依赖
trackDependencies(element, styles) {
const dependencies = new Set();
// 跟踪继承依赖
for (const property of this.styleCascade.inheritedProperties) {
if (property in styles) {
const parent = element.parentElement;
if (parent) {
dependencies.add(`parent:${property}`);
}
}
}
// 跟踪相对单位依赖
for (const [property, value] of Object.entries(styles)) {
if (typeof value === 'string') {
if (value.includes('em') || value.includes('rem') || value.includes('%')) {
dependencies.add(`relative:${property}`);
}
}
}
this.dependencyGraph.set(element, dependencies);
}
// 处理样式失效
invalidateStyles(element, changedProperty = null) {
// 标记元素样式失效
this.computedStyles.delete(element);
this.styleCache.delete(this.generateCacheKey(element, {}));
// 传播失效到依赖项
this.propagateInvalidation(element, changedProperty);
}
// 传播失效
propagateInvalidation(element, changedProperty) {
// 查找依赖此元素样式的子元素
for (const [depElement, dependencies] of this.dependencyGraph.entries()) {
if (depElement === element) continue;
let shouldInvalidate = false;
// 检查继承依赖
if (changedProperty &&
this.styleCascade.inheritedProperties.includes(changedProperty)) {
if (dependencies.has(`parent:${changedProperty}`)) {
shouldInvalidate = true;
}
}
// 检查相对单位依赖
if (changedProperty && (changedProperty === 'font-size' ||
changedProperty === 'width' || changedProperty === 'height')) {
if (dependencies.has(`relative:${changedProperty}`)) {
shouldInvalidate = true;
}
}
if (shouldInvalidate) {
this.invalidateStyles(depElement);
}
}
}
// 批量样式计算
computeStylesBatch(elements) {
// 按DOM顺序排序以减少重排
const sortedElements = this.sortByDOMOrder(elements);
const results = new Map();
for (const element of sortedElements) {
results.set(element, this.computeStyles(element));
}
return results;
}
// 按DOM顺序排序
sortByDOMOrder(elements) {
return Array.from(elements).sort((a, b) => {
if (a.contains(b)) return 1;
if (b.contains(a)) return -1;
return 0;
});
}
}
// LRU缓存实现
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) return undefined;
const value = this.cache.get(key);
// 刷新键的使用顺序
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
set(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
// 删除最久未使用的
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
has(key) {
return this.cache.has(key);
}
delete(key) {
return this.cache.delete(key);
}
}
第七章:测试与验证
7.1 测试框架
为确保CSS解析器的正确性,需要建立全面的测试套件:
javascript
class CSSParserTestSuite {
constructor() {
this.tests = [];
this.results = [];
}
// 添加测试用例
addTest(name, cssInput, expectedOutput, testFunction) {
this.tests.push({
name,
cssInput,
expectedOutput,
testFunction: testFunction || this.defaultTestFunction
});
}
// 默认测试函数
defaultTestFunction(cssInput, expectedOutput) {
const lexer = new CSSLexer(cssInput);
const parser = new CSSParser(lexer);
const result = parser.parseStylesheet();
// 简化比较,实际应更详细
return JSON.stringify(result) === JSON.stringify(expectedOutput);
}
// 运行所有测试
runAllTests() {
console.log('开始运行CSS解析器测试…');
for (const test of this.tests) {
try {
const startTime = performance.now();
const passed = test.testFunction(test.cssInput, test.expectedOutput);
const endTime = performance.now();
this.results.push({
name: test.name,
passed,
duration: endTime – startTime
});
console.log(`${test.name}: ${passed ? '✓' : '✗'} (${(endTime – startTime).toFixed(2)}ms)`);
} catch (error) {
console.log(`${test.name}: ✗ (错误: ${error.message})`);
this.results.push({
name: test.name,
passed: false,
error: error.message
});
}
}
this.printSummary();
}
// 打印测试摘要
printSummary() {
const total = this.results.length;
const passed = this.results.filter(r => r.passed).length;
const failed = total – passed;
console.log('\\n测试摘要:');
console.log(`总计: ${total}, 通过: ${passed}, 失败: ${failed}`);
if (failed > 0) {
console.log('\\n失败测试:');
for (const result of this.results.filter(r => !r.passed)) {
console.log(` – ${result.name}: ${result.error || '未知错误'}`);
}
}
}
// 创建标准测试套件
static createStandardTestSuite() {
const suite = new CSSParserTestSuite();
// 基础选择器测试
suite.addTest(
'简单元素选择器',
'div { color: red; }',
[{
selectors: [{ selectorText: 'div', specificity: { a: 0, b: 0, c: 1 } }],
declarations: [{ property: 'color', value: 'red', important: false }]
}]
);
// 类选择器测试
suite.addTest(
'类选择器',
'.container { width: 100%; }',
[{
selectors: [{ selectorText: '.container', specificity: { a: 0, b: 1, c: 0 } }],
declarations: [{ property: 'width', value: '100%', important: false }]
}]
);
// ID选择器测试
suite.addTest(
'ID选择器',
'#main { margin: 0 auto; }',
[{
selectors: [{ selectorText: '#main', specificity: { a: 1, b: 0, c: 0 } }],
declarations: [{ property: 'margin', value: '0 auto', important: false }]
}]
);
// 多个声明测试
suite.addTest(
'多个声明',
'p { color: blue; font-size: 14px; line-height: 1.5; }',
[{
selectors: [{ selectorText: 'p', specificity: { a: 0, b: 0, c: 1 } }],
declarations: [
{ property: 'color', value: 'blue', important: false },
{ property: 'font-size', value: '14px', important: false },
{ property: 'line-height', value: '1.5', important: false }
]
}]
);
// 多个选择器测试
suite.addTest(
'多个选择器',
'h1, h2, h3 { font-weight: bold; }',
[{
selectors: [
{ selectorText: 'h1', specificity: { a: 0, b: 0, c: 1 } },
{ selectorText: 'h2', specificity: { a: 0, b: 0, c: 1 } },
{ selectorText: 'h3', specificity: { a: 0, b: 0, c: 1 } }
],
declarations: [{ property: 'font-weight', value: 'bold', important: false }]
}]
);
// !important测试
suite.addTest(
'重要声明',
'.warning { color: red !important; }',
[{
selectors: [{ selectorText: '.warning', specificity: { a: 0, b: 1, c: 0 } }],
declarations: [{ property: 'color', value: 'red', important: true }]
}]
);
// 属性选择器测试
suite.addTest(
'属性选择器',
'[data-test] { border: 1px solid #ccc; }',
[{
selectors: [{ selectorText: '[data-test]', specificity: { a: 0, b: 1, c: 0 } }],
declarations: [{ property: 'border', value: '1px solid #ccc', important: false }]
}]
);
// 伪类测试
suite.addTest(
'伪类选择器',
'a:hover { text-decoration: underline; }',
[{
selectors: [{ selectorText: 'a:hover', specificity: { a: 0, b: 1, c: 1 } }],
declarations: [{ property: 'text-decoration', value: 'underline', important: false }]
}]
);
// 复杂选择器测试
suite.addTest(
'复杂选择器',
'div.container > p:first-child { margin-top: 0; }',
[{
selectors: [{ selectorText: 'div.container > p:first-child', specificity: { a: 0, b: 2, c: 2 } }],
declarations: [{ property: 'margin-top', value: '0', important: false }]
}]
);
// 媒体查询测试(简化)
suite.addTest(
'媒体查询',
'@media (max-width: 600px) { .sidebar { display: none; } }',
[]
);
return suite;
}
}
// 性能测试工具
class CSSPerformanceBenchmark {
constructor() {
this.testCases = [];
}
// 添加性能测试
addTestCase(name, setupFunction, testFunction) {
this.testCases.push({ name, setupFunction, testFunction });
}
// 运行性能测试
runBenchmark(iterations = 100) {
console.log('开始性能基准测试…\\n');
const results = [];
for (const testCase of this.testCases) {
console.log(`测试: ${testCase.name}`);
// 准备测试
const testData = testCase.setupFunction();
// 预热
for (let i = 0; i < 10; i++) {
testCase.testFunction(testData);
}
// 正式测试
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
testCase.testFunction(testData);
}
const endTime = performance.now();
const avgTime = (endTime – startTime) / iterations;
results.push({ name: testCase.name, avgTime });
console.log(` 平均时间: ${avgTime.toFixed(3)}ms\\n`);
}
// 排序结果
results.sort((a, b) => a.avgTime – b.avgTime);
console.log('性能排名:');
results.forEach((result, index) => {
console.log(`${index + 1}. ${result.name}: ${result.avgTime.toFixed(3)}ms`);
});
return results;
}
// 创建标准性能测试套件
static createStandardBenchmark() {
const benchmark = new CSSPerformanceBenchmark();
// 大型样式表解析测试
benchmark.addTestCase(
'大型样式表解析',
() => {
// 生成大型CSS
let css = '';
for (let i = 0; i < 1000; i++) {
css += `.class-${i} { color: #${Math.floor(Math.random()*16777215).toString(16)}; }\\n`;
}
return css;
},
(css) => {
const lexer = new CSSLexer(css);
const parser = new CSSParser(lexer);
parser.parseStylesheet();
}
);
// 复杂选择器匹配测试
benchmark.addTestCase(
'复杂选择器匹配',
() => {
// 创建测试元素和规则
const engine = new CSSEngine();
const css = `
div.container > ul.list li.item:nth-child(odd) a[href^="https"]:hover {
color: red;
font-weight: bold;
}
#main .sidebar ~ section.content article:first-of-type h2 {
margin-top: 0;
}
`;
engine.loadCSS(css);
// 创建测试元素
const div = { tagName: 'DIV', classList: ['container'], id: '' };
const ul = { tagName: 'UL', classList: ['list'], parentElement: div };
const li = { tagName: 'LI', classList: ['item'], parentElement: ul };
const a = {
tagName: 'A',
parentElement: li,
getAttribute: (attr) => attr === 'href' ? 'https://example.com' : null
};
return { engine, element: a };
},
({ engine, element }) => {
engine.computeStyles(element, { isHovered: () => true });
}
);
// 样式计算性能测试
benchmark.addTestCase(
'批量样式计算',
() => {
const engine = new CSSEngine();
const css = `
* { box-sizing: border-box; }
div { display: block; }
.box { width: 100px; height: 100px; margin: 10px; }
.red { background-color: red; }
.blue { background-color: blue; }
.large { font-size: 20px; }
.small { font-size: 12px; }
`;
engine.loadCSS(css);
// 创建多个测试元素
const elements = [];
for (let i = 0; i < 100; i++) {
const element = {
tagName: 'DIV',
classList: ['box', i % 2 ? 'red' : 'blue', i % 3 ? 'large' : 'small'],
parentElement: i > 0 ? elements[i-1] : null,
getAttribute: () => null
};
elements.push(element);
}
return { engine, elements };
},
({ engine, elements }) => {
for (const element of elements) {
engine.computeStyles(element);
}
}
);
return benchmark;
}
}
7.2 浏览器兼容性测试
为确保解析器行为与主流浏览器一致,需要进行兼容性测试:
javascript
class BrowserCompatibilityTester {
constructor() {
this.features = [
'css-variables',
'css-grid',
'flexbox',
'css-transitions',
'css-animations',
'css-calc',
'media-queries',
'pseudo-elements',
'pseudo-classes',
'attribute-selectors'
];
}
// 测试特定CSS功能
testFeature(featureName, testCSS) {
const testResults = {
feature: featureName,
supported: false,
details: {}
};
try {
const lexer = new CSSLexer(testCSS);
const parser = new CSSParser(lexer);
const rules = parser.parseStylesheet();
testResults.supported = rules.length > 0;
testResults.details.rulesParsed = rules.length;
testResults.details.errors = parser.errors;
if (testResults.supported) {
console.log(`✓ ${featureName}: 支持`);
} else {
console.log(`✗ ${featureName}: 不支持或解析失败`);
}
} catch (error) {
testResults.supported = false;
testResults.details.error = error.message;
console.log(`✗ ${featureName}: 错误 – ${error.message}`);
}
return testResults;
}
// 运行所有兼容性测试
runAllCompatibilityTests() {
console.log('开始浏览器兼容性测试…\\n');
const results = {};
// CSS变量测试
results.cssVariables = this.testFeature('css-variables', `
:root {
–main-color: #06c;
–accent-color: #006;
}
.component {
color: var(–main-color);
background-color: var(–accent-color);
}
`);
// CSS Grid测试
results.cssGrid = this.testFeature('css-grid', `
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 10px;
}
`);
// Flexbox测试
results.flexbox = this.testFeature('flexbox', `
.container {
display: flex;
justify-content: center;
align-items: center;
}
`);
// 过渡效果测试
results.cssTransitions = this.testFeature('css-transitions', `
.button {
transition: all 0.3s ease-in-out;
}
.button:hover {
transform: scale(1.1);
}
`);
// calc()函数测试
results.cssCalc = this.testFeature('css-calc', `
.element {
width: calc(100% – 20px);
height: calc(50vh + 10px);
}
`);
// 媒体查询测试
results.mediaQueries = this.testFeature('media-queries', `
@media (min-width: 768px) {
.container {
max-width: 750px;
}
}
@media (min-width: 992px) {
.container {
max-width: 970px;
}
}
`);
// 伪元素测试
results.pseudoElements = this.testFeature('pseudo-elements', `
p::first-line {
font-weight: bold;
}
p::before {
content: "→ ";
}
`);
// 复杂伪类测试
results.pseudoClasses = this.testFeature('pseudo-classes', `
li:nth-child(3n+1) {
color: red;
}
input:not(:checked) + label {
opacity: 0.5;
}
`);
// 属性选择器测试
results.attributeSelectors = this.testFeature('attribute-selectors', `
input[type="text"] {
border: 1px solid #ccc;
}
a[href^="https"] {
color: green;
}
img[src$=".jpg"] {
border: 2px solid #999;
}
`);
return results;
}
}
第八章:实际应用与扩展
8.1 构建CSS-in-JS解决方案
基于我们的CSS引擎,可以构建一个简单的CSS-in-JS库:
javascript
class StyledComponents {
constructor() {
this.engine = new OptimizedCSSEngine();
this.styleSheet = '';
this.componentStyles = new Map();
this.styleElement = null;
}
// 创建样式组件
createStyledComponent(tagName, styles) {
const componentId = `sc-${Math.random().toString(36).substr(2, 9)}`;
const cssRules = this.generateCSS(componentId, styles);
// 添加到样式表
this.styleSheet += cssRules;
this.updateStyleElement();
// 返回组件工厂函数
return (props = {}, …children) => {
const element = {
tagName: tagName.toUpperCase(),
classList: [componentId],
attributes: [],
parentElement: null,
children: children || [],
getAttribute: (name) => {
const attr = this.attributes.find(a => a.name === name);
return attr ? attr.value : null;
}
};
// 处理props
if (props.className) {
element.classList.push(props.className);
}
// 动态样式
if (props.style) {
element.attributes.push({ name: 'style', value: this.objectToCSS(props.style) });
}
return element;
};
}
// 生成CSS规则
generateCSS(componentId, styles) {
const selector = `.${componentId}`;
let css = `${selector} {\\n`;
for (const [property, value] of Object.entries(styles)) {
const cssProperty = this.camelToKebab(property);
css += ` ${cssProperty}: ${value};\\n`;
}
css += '}\\n';
return css;
}
// 驼峰命名转短横线命名
camelToKebab(str) {
return str.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
}
// 对象转CSS字符串
objectToCSS(styleObj) {
return Object.entries(styleObj)
.map(([key, value]) => `${this.camelToKebab(key)}: ${value}`)
.join('; ');
}
// 更新样式元素
updateStyleElement() {
if (!this.styleElement) {
this.styleElement = document.createElement('style');
document.head.appendChild(this.styleElement);
}
this.styleElement.textContent = this.styleSheet;
this.engine.loadCSS(this.styleSheet);
}
// 创建关键帧动画
keyframes(keyframesObj) {
const animationName = `animation-${Math.random().toString(36).substr(2, 9)}`;
let css = `@keyframes ${animationName} {\\n`;
for (const [keyframe, styles] of Object.entries(keyframesObj)) {
css += ` ${keyframe} {\\n`;
for (const [property, value] of Object.entries(styles)) {
const cssProperty = this.camelToKebab(property);
css += ` ${cssProperty}: ${value};\\n`;
}
css += ' }\\n';
}
css += '}\\n';
this.styleSheet += css;
this.updateStyleElement();
return animationName;
}
// 创建全局样式
createGlobalStyle(styles) {
const css = `* {\\n${this.objectToCSS(styles).replace(/;/g, ';\\n')}\\n}\\n`;
this.styleSheet += css;
this.updateStyleElement();
}
}
// 使用示例
const styled = new StyledComponents();
// 创建样式组件
const Button = styled.createStyledComponent('button', {
backgroundColor: '#007bff',
color: 'white',
padding: '10px 20px',
border: 'none',
borderRadius: '4px',
fontSize: '16px',
cursor: 'pointer',
transition: 'background-color 0.3s ease',
'&:hover': {
backgroundColor: '#0056b3'
},
'&:active': {
backgroundColor: '#004085'
}
});
// 创建动画
const spinAnimation = styled.keyframes({
'0%': { transform: 'rotate(0deg)' },
'100%': { transform: 'rotate(360deg)' }
});
// 创建带动画的组件
const Spinner = styled.createStyledComponent('div', {
width: '40px',
height: '40px',
border: '4px solid #f3f3f3',
borderTop: '4px solid #3498db',
borderRadius: '50%',
animation: `${spinAnimation} 1s linear infinite`
});
// 设置全局样式
styled.createGlobalStyle({
margin: 0,
padding: 0,
boxSizing: 'border-box',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
});
8.2 可视化CSS编辑器
利用我们的CSS引擎,可以构建一个可视化CSS编辑器:
javascript
class VisualCSSEditor {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.engine = new OptimizedCSSEngine();
this.selectedElement = null;
this.stylesheet = '';
this.initUI();
}
// 初始化用户界面
initUI() {
// 创建编辑器布局
this.container.innerHTML = `
<div class="css-editor">
<div class="editor-top">
<div class="element-selector">
<h3>选择元素</h3>
<div class="element-tree" id="elementTree"></div>
</div>
<div class="css-editor-panel">
<h3>CSS编辑器</h3>
<textarea id="cssEditor" rows="20" cols="50"></textarea>
<div class="editor-buttons">
<button id="applyCSS">应用CSS</button>
<button id="resetCSS">重置</button>
</div>
</div>
</div>
<div class="editor-bottom">
<div class="style-inspector">
<h3>样式检查器</h3>
<div id="styleProperties"></div>
</div>
<div class="preview-area">
<h3>预览</h3>
<div id="previewContainer" contenteditable="true">
<h1>可视化CSS编辑器</h1>
<p>编辑此区域的内容和样式</p>
<button class="demo-button">示例按钮</button>
<div class="demo-box">
<p>这是一个示例框</p>
</div>
</div>
</div>
</div>
</div>
`;
// 绑定事件
this.bindEvents();
// 初始化元素树
this.buildElementTree();
// 初始化默认CSS
this.loadDefaultCSS();
}
// 绑定事件
bindEvents() {
// CSS编辑器应用按钮
document.getElementById('applyCSS').addEventListener('click', () => {
this.applyCSS();
});
// 重置按钮
document.getElementById('resetCSS').addEventListener('click', () => {
this.resetCSS();
});
// 预览区域点击事件(用于选择元素)
document.getElementById('previewContainer').addEventListener('click', (e) => {
this.selectElement(e.target);
});
// 实时CSS编辑(可选)
document.getElementById('cssEditor').addEventListener('input', () => {
// 可以添加防抖处理
// this.applyCSS();
});
}
// 构建元素树
buildElementTree() {
const previewContainer = document.getElementById('previewContainer');
const elementTree = document.getElementById('elementTree');
const buildTree = (element, parentNode) => {
const node = document.createElement('div');
node.className = 'tree-node';
node.textContent = element.tagName.toLowerCase();
node.dataset.elementId = this.getElementId(element);
node.addEventListener('click', (e) => {
e.stopPropagation();
this.selectElement(element);
});
parentNode.appendChild(node);
// 递归处理子元素
if (element.children.length > 0) {
const childrenContainer = document.createElement('div');
childrenContainer.className = 'tree-children';
node.appendChild(childrenContainer);
for (const child of element.children) {
buildTree(child, childrenContainer);
}
}
};
elementTree.innerHTML = '';
buildTree(previewContainer, elementTree);
}
// 选择元素
selectElement(element) {
// 清除之前的选择
if (this.selectedElement) {
this.selectedElement.classList.remove('selected');
}
// 设置新选择
this.selectedElement = element;
element.classList.add('selected');
// 更新元素树选择
this.updateElementTreeSelection();
// 显示元素样式
this.inspectElementStyles();
// 更新CSS编辑器
this.updateCSSEditor();
}
// 更新元素树选择
updateElementTreeSelection() {
const elementId = this.getElementId(this.selectedElement);
const treeNodes = document.querySelectorAll('.tree-node');
treeNodes.forEach(node => {
node.classList.remove('selected');
if (node.dataset.elementId === elementId) {
node.classList.add('selected');
// 确保节点可见
node.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
});
}
// 检查元素样式
inspectElementStyles() {
if (!this.selectedElement) return;
const computedStyles = this.engine.getComputedStyle(this.selectedElement);
const propertiesContainer = document.getElementById('styleProperties');
propertiesContainer.innerHTML = '<table class="style-table"><thead><tr><th>属性</th><th>值</th><th>来源</th></tr></thead><tbody></tbody></table>';
const tbody = propertiesContainer.querySelector('tbody');
// 按属性名排序
const sortedProperties = Object.keys(computedStyles).sort();
for (const property of sortedProperties) {
const value = computedStyles[property];
const row = document.createElement('tr');
row.innerHTML = `
<td><code>${property}</code></td>
<td><code>${value}</code></td>
<td>计算值</td>
`;
tbody.appendChild(row);
}
}
// 更新CSS编辑器
updateCSSEditor() {
if (!this.selectedElement) return;
const editor = document.getElementById('cssEditor');
const selector = this.generateSelector(this.selectedElement);
// 查找现有的样式规则
const existingRule = this.findRuleForElement(this.selectedElement);
if (existingRule) {
editor.value = existingRule;
} else {
editor.value = `${selector} {\\n \\n}`;
// 将光标定位在大括号内
editor.focus();
editor.setSelectionRange(selector.length + 3, selector.length + 3);
}
}
// 生成选择器
generateSelector(element) {
let selector = element.tagName.toLowerCase();
if (element.id) {
selector = `#${element.id}`;
} else if (element.classList.length > 0) {
// 使用第一个类
selector = `.${element.classList[0]}`;
}
return selector;
}
// 查找元素的现有规则
findRuleForElement(element) {
// 简化实现
const selector = this.generateSelector(element);
// 在样式表中查找
const lines = this.stylesheet.split('\\n');
let inRule = false;
let ruleStart = -1;
let ruleSelector = '';
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.endsWith('{') && !line.includes('@')) {
inRule = true;
ruleStart = i;
ruleSelector = line.substring(0, line.length – 1).trim();
} else if (line === '}' && inRule) {
inRule = false;
// 检查选择器是否匹配
if (this.selectorMatchesElement(ruleSelector, element)) {
// 提取整个规则
const ruleLines = lines.slice(ruleStart, i + 1);
return ruleLines.join('\\n');
}
}
}
return null;
}
// 检查选择器是否匹配元素
selectorMatchesElement(selector, element) {
// 简化实现
try {
return element.matches(selector);
} catch (e) {
return false;
}
}
// 应用CSS
applyCSS() {
const editor = document.getElementById('cssEditor');
const css = editor.value;
// 添加到样式表
this.stylesheet += '\\n' + css;
// 更新引擎
this.engine.loadCSS(this.stylesheet);
// 重新计算样式
this.inspectElementStyles();
// 更新预览
this.updatePreview();
}
// 重置CSS
resetCSS() {
this.stylesheet = '';
this.engine = new OptimizedCSSEngine();
this.loadDefaultCSS();
this.inspectElementStyles();
this.updatePreview();
// 清空编辑器
document.getElementById('cssEditor').value = '';
}
// 更新预览
updatePreview() {
// 重新计算所有元素的样式
const previewContainer = document.getElementById('previewContainer');
this.updateElementStyles(previewContainer);
}
// 递归更新元素样式
updateElementStyles(element) {
// 计算样式
const styles = this.engine.getComputedStyle(element);
// 应用内联样式
let styleString = '';
for (const [property, value] of Object.entries(styles)) {
const cssProperty = property.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`);
styleString += `${cssProperty}: ${value}; `;
}
element.style.cssText = styleString;
// 递归处理子元素
for (const child of element.children) {
this.updateElementStyles(child);
}
}
// 加载默认CSS
loadDefaultCSS() {
const defaultCSS = `
#previewContainer {
font-family: Arial, sans-serif;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #f9f9f9;
}
#previewContainer h1 {
color: #333;
border-bottom: 2px solid #007bff;
padding-bottom: 10px;
}
#previewContainer p {
line-height: 1.6;
color: #666;
}
.demo-button {
background-color: #28a745;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.demo-button:hover {
background-color: #218838;
}
.demo-box {
border: 1px solid #ddd;
padding: 15px;
margin-top: 20px;
border-radius: 4px;
background-color: white;
}
.selected {
outline: 2px dashed #007bff;
outline-offset: 2px;
}
`;
this.stylesheet = defaultCSS;
this.engine.loadCSS(defaultCSS);
}
// 获取元素唯一ID
getElementId(element) {
if (!element._cssEditorId) {
element._cssEditorId = `el-${Math.random().toString(36).substr(2, 9)}`;
}
return element._cssEditorId;
}
}
第九章:未来发展与优化方向
9.1 支持CSS新特性
随着CSS标准不断发展,解析器需要持续更新以支持新特性:
javascript
class ModernCSSParser extends CSSParser {
constructor(lexer) {
super(lexer);
this.supportedFeatures = {
'css-grid': true,
'css-variables': true,
'css-contain': true,
'css-subgrid': false, // 尚未支持
'css-container-queries': false // 尚未支持
};
}
// 解析CSS自定义属性
parseCustomProperty(declaration) {
if (declaration.property.startsWith('–')) {
return {
type: 'custom-property',
name: declaration.property,
value: declaration.value
};
}
return null;
}
// 解析CSS Grid属性
parseGridValue(value) {
// 简化的Grid解析
const gridValues = {
'grid-template-columns': this.parseGridTemplate(value),
'grid-template-rows': this.parseGridTemplate(value),
'grid-area': this.parseGridArea(value)
};
return gridValues;
}
parseGridTemplate(value) {
// 解析如 "1fr 1fr minmax(100px, 1fr)" 的值
const parts = value.split(' ');
return parts.map(part => {
if (part.startsWith('minmax(')) {
return this.parseMinMax(part);
} else if (part.startsWith('repeat(')) {
return this.parseRepeat(part);
}
return part;
});
}
parseMinMax(minmaxStr) {
const match = minmaxStr.match(/minmax\\(([^,]+),\\s*([^)]+)\\)/);
if (match) {
return {
type: 'minmax',
min: match[1].trim(),
max: match[2].trim()
};
}
return minmaxStr;
}
// 解析容器查询
parseContainerQuery(atRule) {
if (atRule.name === 'container') {
return {
type: 'container-query',
condition: atRule.condition,
rules: atRule.rules
};
}
return null;
}
}
9.2 性能优化策略
进一步优化解析器性能:
javascript
class HighPerformanceCSSEngine extends OptimizedCSSEngine {
constructor() {
super();
this.workerPool = new WorkerPool(4); // 4个工作线程
this.incrementalParser = new IncrementalParser();
this.selectiveRecalc = new SelectiveRecalcEngine();
}
// 并行解析CSS
async parseCSSInParallel(cssText) {
// 将CSS分割为多个块
const chunks = this.splitCSSIntoChunks(cssText);
// 并行解析每个块
const promises = chunks.map(chunk =>
this.workerPool.enqueue(() => this.parseChunk(chunk))
);
const results = await Promise.all(promises);
// 合并结果
return this.mergeParseResults(results);
}
// 增量解析
parseCSSIncrementally(cssText, callback) {
return this.incrementalParser.parse(cssText, (progress, rules) => {
// 逐步添加规则到索引
for (const rule of rules) {
this.ruleIndex.addRule(rule);
}
callback(progress, rules);
});
}
// 选择性重新计算
recomputeStylesSelectively(element, changedProperties) {
return this.selectiveRecalc.recompute(
element,
changedProperties,
this.computeStyles.bind(this)
);
}
}
// 工作线程池
class WorkerPool {
constructor(size) {
this.size = size;
this.queue = [];
this.workers = Array.from({ length: size }, () => ({
busy: false,
worker: this.createWorker()
}));
}
createWorker() {
// 创建Web Worker或使用模拟
return {
execute: (task) => new Promise(resolve => {
// 模拟异步执行
setTimeout(() => resolve(task()), Math.random() * 10);
})
};
}
enqueue(task) {
return new Promise((resolve) => {
this.queue.push({ task, resolve });
this.processQueue();
});
}
processQueue() {
const availableWorker = this.workers.find(w => !w.busy);
if (!availableWorker || this.queue.length === 0) return;
const { task, resolve } = this.queue.shift();
availableWorker.busy = true;
availableWorker.worker.execute(task).then(result => {
availableWorker.busy = false;
resolve(result);
this.processQueue();
});
}
}
// 增量解析器
class IncrementalParser {
constructor() {
this.buffer = '';
this.partialRules = [];
}
parse(cssText, callback) {
this.buffer += cssText;
let progress = 0;
// 按规则边界分割
const rules = [];
let ruleStart = 0;
let braceDepth = 0;
let inComment = false;
for (let i = 0; i < this.buffer.length; i++) {
// 跳过注释
if (this.buffer.substr(i, 2) === '/*') {
inComment = true;
i++;
continue;
}
if (inComment && this.buffer.substr(i, 2) === '*/') {
inComment = false;
i++;
continue;
}
if (inComment) continue;
// 跟踪大括号深度
if (this.buffer[i] === '{') {
braceDepth++;
} else if (this.buffer[i] === '}') {
braceDepth–;
// 找到完整规则
if (braceDepth === 0) {
const ruleText = this.buffer.substring(ruleStart, i + 1);
rules.push(ruleText);
ruleStart = i + 1;
// 更新进度
progress = (i + 1) / this.buffer.length;
callback(progress, this.parseRules([ruleText]));
}
}
}
// 保留未完成的规则
this.buffer = this.buffer.substring(ruleStart);
return rules;
}
parseRules(ruleTexts) {
// 使用主解析器解析规则
return ruleTexts.map(text => {
const lexer = new CSSLexer(text);
const parser = new CSSParser(lexer);
return parser.parseRule();
}).filter(rule => rule !== null);
}
}
// 选择性重新计算引擎
class SelectiveRecalcEngine {
constructor() {
this.dependencyMap = new Map();
}
recompute(element, changedProperties, computeFunction) {
// 检查哪些属性需要重新计算
const propertiesToRecalc = this.getDependentProperties(
element,
changedProperties
);
if (propertiesToRecalc.length === 0) {
// 没有依赖,无需重新计算
return null;
}
// 重新计算
return computeFunction(element);
}
getDependentProperties(element, changedProperties) {
const dependencies = new Set();
// 获取元素的样式依赖
const elementDeps = this.dependencyMap.get(element) || new Set();
for (const changedProp of changedProperties) {
// 查找依赖此属性的属性
for (const dep of elementDeps) {
if (dep.dependsOn === changedProp) {
dependencies.add(dep.property);
}
}
}
return Array.from(dependencies);
}
trackDependencies(element, property, value) {
if (!this.dependencyMap.has(element)) {
this.dependencyMap.set(element, new Set());
}
const deps = this.dependencyMap.get(element);
// 分析值中的依赖
if (typeof value === 'string') {
// 检查相对单位
if (value.includes('em') || value.includes('rem')) {
deps.add({
property,
dependsOn: 'font-size',
type: 'relative-unit'
});
}
if (value.includes('%')) {
deps.add({
property,
dependsOn: 'parent-size',
type: 'percentage'
});
}
// 检查CSS变量
const varMatch = value.match(/var\\(–([^)]+)\\)/);
if (varMatch) {
deps.add({
property,
dependsOn: `var-${varMatch[1]}`,
type: 'css-variable'
});
}
}
}
}
结语
通过本文的详细阐述,我们完整地构建了一个功能齐全的CSS解析器,实现了从词法分析、语法解析到选择器匹配和样式计算的全过程。这个解析器不仅能够处理基本的CSS规则,还支持复杂的选择器匹配、层叠计算、继承处理和性能优化。
手写CSS解析器的意义不仅在于技术实现本身,更重要的是通过这个过程,我们能够:
深入理解浏览器工作原理:CSS解析是浏览器渲染引擎的核心部分,理解这一过程有助于我们编写更高效的CSS代码。
掌握编译原理实践:CSS解析器是一个典型的编译器前端实现,涉及词法分析、语法分析等编译原理核心概念。
构建自定义样式解决方案:基于自定义解析器,我们可以构建CSS-in-JS、可视化样式编辑器、样式优化工具等高级应用。
性能优化基础:理解样式计算过程有助于我们发现和解决Web应用中的样式性能瓶颈。
跨平台样式引擎:可以基于此解析器构建跨平台的样式引擎,用于移动端、桌面端或游戏UI系统。
虽然本文实现的CSS解析器已经相当完整,但在生产环境中使用还需要考虑更多因素,如完整的CSS规范支持、错误恢复机制、内存管理优化等。此外,随着CSS标准的不断发展,解析器也需要持续更新以支持新特性。
希望本文能够为读者提供深入的CSS解析器实现指导,并激发更多关于Web技术底层实现的探索兴趣。通过理解这些基础技术,我们能够更好地驾驭现代Web开发,构建更高效、更可靠的Web应用。
附录
A. CSS解析器完整源码结构
text
css-parser/
├── src/
│ ├── lexer/
│ │ ├── CharStream.js
│ │ ├── CSSLexer.js
│ │ └── OptimizedCSSLexer.js
│ ├── parser/
│ │ ├── CSSParser.js
│ │ ├── OptimizedCSSParser.js
│ │ └── ModernCSSParser.js
│ ├── selector/
│ │ ├── SelectorMatcher.js
│ │ ├── RuleIndex.js
│ │ └── SpecificityCalculator.js
│ ├── cascade/
│ │ ├── StyleCascade.js
│ │ ├── CSSValueCalculator.js
│ │ └── InheritanceManager.js
│ ├── engine/
│ │ ├── CSSEngine.js
│ │ ├── OptimizedCSSEngine.js
│ │ └── HighPerformanceCSSEngine.js
│ └── utils/
│ ├── LRUCache.js
│ ├── WorkerPool.js
│ └── IncrementalParser.js
├── test/
│ ├── CSSParserTestSuite.js
│ ├── CSSPerformanceBenchmark.js
│ └── BrowserCompatibilityTester.js
├── examples/
│ ├── styled-components.js
│ ├── visual-css-editor.js
│ └── css-optimizer.js
└── docs/
├── API.md
├── ARCHITECTURE.md
└── PERFORMANCE.md
B. 推荐阅读与学习资源
CSS规范文档
-
CSS Syntax Module Level 3
-
CSS Selectors Level 4
-
CSS Cascading and Inheritance Level 4
浏览器源码
-
WebKit/Blink源码中的CSS解析部分
-
Firefox Servo引擎的样式系统
相关工具和库
-
PostCSS:现代CSS处理器
-
CSSOM:CSS对象模型操作库
-
Styled-components:CSS-in-JS实现
进阶主题
-
样式隔离与Shadow DOM
-
CSS Houdini:浏览器扩展API
-
响应式设计原理与实现
通过不断学习和实践,我们可以将CSS解析器的理解应用到实际项目中,提升Web应用的性能和用户体验。





