Skip to content

CSSTree 课程计划

课程概述

本课程将带你从零开始实现一个 CSS 解析器,理解 CSS 解析的原理。

课程目标

  • 理解 CSS 解析的原理
  • 掌握 AST 的构建和遍历
  • 学习 CSS 代码生成
  • 能够构建自己的 CSS 工具

课程结构

8 节课,每节 20-40 分钟,总时长约 90-120 分钟。


第 1 课:CSS 基础

目标

  • 了解 CSS 的语法
  • 理解 CSS 的结构
  • 掌握 CSS 的选择器

内容

  1. CSS 语法

    • 规则集
    • 选择器
    • 声明
  2. CSS 结构

    • 样式表
    • 规则
    • 声明块
  3. CSS 选择器

    • 元素选择器
    • 类选择器
    • ID 选择器
    • 属性选择器

实践步骤

bash
# 1. 创建项目
mkdir csstree-course
cd csstree-course

# 2. 初始化项目
npm init -y

预期输出

✓ 项目初始化完成

总结

  • ✅ 理解了 CSS 语法
  • ✅ 掌握了 CSS 结构
  • ✅ 了解了 CSS 选择器

第 2 课:词法分析

目标

  • 理解词法分析的原理
  • 实现 CSS 词法分析器
  • 学习标记类型

内容

  1. 词法分析

    • 什么是词法分析
    • 标记类型
    • 词法分析算法
  2. 实现词法分析器

    • 识别标记
    • 处理空白
    • 处理注释
  3. 测试词法分析器

    • 单元测试
    • 边界测试

实践步骤

bash
# 1. 创建词法分析器
cat > src/tokenizer.js << 'EOF'
export function tokenize(source) {
  const tokens = []
  let pos = 0
  
  while (pos < source.length) {
    const char = source[pos]
    
    // 跳过空白
    if (/\s/.test(char)) {
      pos++
      continue
    }
    
    // 识别标记
    // ... 标记识别逻辑
  }
  
  return tokens
}
EOF

# 2. 创建测试
cat > test/tokenizer.test.js << 'EOF'
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { tokenize } from '../src/tokenizer.js'

describe('词法分析器测试', () => {
  it('应该解析 CSS', () => {
    const tokens = tokenize('body { color: red; }')
    assert.ok(Array.isArray(tokens))
  })
})
EOF

# 3. 运行测试
npm test

预期输出

✓ 应该解析 CSS

总结

  • ✅ 实现了词法分析器
  • ✅ 理解了标记类型
  • ✅ 掌握了词法分析算法

第 3 课:语法分析

目标

  • 理解语法分析的原理
  • 实现 CSS 语法分析器
  • 学习 AST 构建

内容

  1. 语法分析

    • 什么是语法分析
    • AST 结构
    • 语法分析算法
  2. 实现语法分析器

    • 构建节点
    • 构建规则
    • 构建声明
  3. 测试语法分析器

    • 单元测试
    • 复杂测试

实践步骤

bash
# 1. 创建语法分析器
cat > src/parser.js << 'EOF'
export function parse(source) {
  const tokens = tokenize(source)
  const ast = {
    type: 'Stylesheet',
    children: []
  }
  
  // 构建逻辑
  
  return ast
}
EOF

# 2. 创建测试
cat > test/parser.test.js << 'EOF'
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { parse } from '../src/parser.js'

describe('语法分析器测试', () => {
  it('应该解析 CSS', () => {
    const ast = parse('body { color: red; }')
    assert.strictEqual(ast.type, 'Stylesheet')
  })
})
EOF

# 3. 运行测试
npm test

预期输出

✓ 应该解析 CSS

总结

  • ✅ 实现了语法分析器
  • ✅ 理解了 AST 结构
  • ✅ 掌握了语法分析算法

第 4 课:AST 遍历

目标

  • 理解 AST 遍历的原理
  • 实现 AST 遍历器
  • 学习节点访问

内容

  1. AST 遍历

    • 什么是遍历
    • 遍历策略
    • 访问者模式
  2. 实现遍历器

    • 深度优先遍历
    • 广度优先遍历
    • 节点访问
  3. 测试遍历器

    • 单元测试
    • 复杂测试

实践步骤

bash
# 1. 创建遍历器
cat > src/traverse.js << 'EOF'
export function traverse(ast, visitor) {
  function visit(node) {
    if (!node) return
    
    // 访问当前节点
    if (visitor.enter) {
      visitor.enter(node)
    }
    
    // 遍历子节点
    if (node.children) {
      for (const child of node.children) {
        visit(child)
      }
    }
    
    // 离开节点
    if (visitor.leave) {
      visitor.leave(node)
    }
  }
  
  visit(ast)
}
EOF

# 2. 创建测试
cat > test/traverse.test.js << 'EOF'
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { traverse } from '../src/traverse.js'

describe('遍历器测试', () => {
  it('应该遍历 AST', () => {
    const ast = { type: 'Stylesheet', children: [] }
    let visited = false
    
    traverse(ast, {
      enter(node) {
        if (node.type === 'Stylesheet') {
          visited = true
        }
      }
    })
    
    assert.strictEqual(visited, true)
  })
})
EOF

# 3. 运行测试
npm test

预期输出

✓ 应该遍历 AST

总结

  • ✅ 实现了 AST 遍历器
  • ✅ 理解了遍历策略
  • ✅ 掌握了访问者模式

第 5 课:代码生成

目标

  • 理解代码生成的原理
  • 实现 CSS 代码生成器
  • 学习代码格式化

内容

  1. 代码生成

    • 什么是代码生成
    • 生成算法
    • 格式化选项
  2. 实现代码生成器

    • 节点到代码
    • 格式化
    • 压缩
  3. 测试代码生成器

    • 单元测试
    • 格式化测试

实践步骤

bash
# 1. 创建代码生成器
cat > src/generator.js << 'EOF'
export function generate(ast, options = {}) {
  const { minify = false } = options
  let code = ''
  
  function visit(node) {
    switch (node.type) {
      case 'Stylesheet':
        code += node.children.map(visit).join('')
        break
      case 'Rule':
        code += node.selector + ' {'
        code += node.children.map(visit).join('')
        code += '}'
        break
      // ... 其他节点类型
    }
  }
  
  visit(ast)
  
  if (minify) {
    code = code.replace(/\s+/g, ' ').trim()
  }
  
  return code
}
EOF

# 2. 创建测试
cat > test/generator.test.js << 'EOF'
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { generate } from '../src/generator.js'

describe('代码生成器测试', () => {
  it('应该生成 CSS', () => {
    const ast = { type: 'Stylesheet', children: [] }
    const code = generate(ast)
    assert.ok(typeof code === 'string')
  })
})
EOF

# 3. 运行测试
npm test

预期输出

✓ 应该生成 CSS

总结

  • ✅ 实现了代码生成器
  • ✅ 理解了代码生成算法
  • ✅ 掌握了代码格式化

第 6 课:CSS 转换

目标

  • 理解 CSS 转换的原理
  • 实现 CSS 转换器
  • 学习常见转换

内容

  1. CSS 转换

    • 什么是转换
    • 转换类型
    • 转换应用
  2. 实现转换器

    • 添加前缀
    • 压缩 CSS
    • 优化选择器
  3. 测试转换器

    • 单元测试
    • 转换测试

实践步骤

bash
# 1. 创建转换器
cat > src/transform.js << 'EOF'
export function transform(ast, plugins = []) {
  for (const plugin of plugins) {
    plugin(ast)
  }
  return ast
}

export const plugins = {
  addPrefix(ast) {
    // 添加浏览器前缀
  },
  
  minify(ast) {
    // 压缩 CSS
  }
}
EOF

# 2. 创建测试
cat > test/transform.test.js << 'EOF'
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { transform } from '../src/transform.js'

describe('转换器测试', () => {
  it('应该转换 AST', () => {
    const ast = { type: 'Stylesheet', children: [] }
    const result = transform(ast, [])
    assert.ok(result)
  })
})
EOF

# 3. 运行测试
npm test

预期输出

✓ 应该转换 AST

总结

  • ✅ 实现了 CSS 转换器
  • ✅ 理解了转换类型
  • ✅ 掌握了常见转换

第 7 课:CSS 优化

目标

  • 理解 CSS 优化的原理
  • 实现 CSS 优化器
  • 学习优化策略

内容

  1. CSS 优化

    • 什么是优化
    • 优化策略
    • 优化效果
  2. 实现优化器

    • 合并规则
    • 移除重复
    • 简化选择器
  3. 测试优化器

    • 单元测试
    • 优化测试

实践步骤

bash
# 1. 创建优化器
cat > src/optimize.js << 'EOF'
export function optimize(ast) {
  // 合并相同选择器的规则
  // 移除重复的声明
  // 简化选择器
  
  return ast
}
EOF

# 2. 创建测试
cat > test/optimize.test.js << 'EOF'
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { optimize } from '../src/optimize.js'

describe('优化器测试', () => {
  it('应该优化 AST', () => {
    const ast = { type: 'Stylesheet', children: [] }
    const result = optimize(ast)
    assert.ok(result)
  })
})
EOF

# 3. 运行测试
npm test

预期输出

✓ 应该优化 AST

总结

  • ✅ 实现了 CSS 优化器
  • ✅ 理解了优化策略
  • ✅ 掌握了优化效果

第 8 课:总结与扩展

目标

  • 总结所有课程
  • 回顾关键概念
  • 提供扩展建议

内容

  1. 课程总结

    • 回顾所有功能
    • 总结关键概念
    • 展示完整代码
  2. 扩展建议

    • 添加更多功能
    • 优化性能
    • 支持更多特性
  3. 下一步

    • 学习 CSSTree 源码
    • 参与开源项目
    • 构建自己的工具

总结

通过本课程,你学会了:

  1. ✅ CSS 基础
  2. ✅ 词法分析
  3. ✅ 语法分析
  4. ✅ AST 遍历
  5. ✅ 代码生成
  6. ✅ CSS 转换
  7. ✅ CSS 优化

下一步

  • 学习 CSSTree 源码
  • 添加更多插件
  • 参与开源项目

参考资源

架构师AI杜公众号二维码

扫描二维码关注"架构师AI杜"公众号,获取更多技术内容和最新动态