menu
首页文章相册工具关于
search
...

单一数据源颜色管理

2025/11/13
eye-

背景与问题

在微信小程序中,颜色定义通常需要在多个地方使用:

  • CSS 文件:用于样式定义
  • JavaScript 文件:用于组件属性、动态样式等

传统的做法是在两个地方分别定义:

// _variables.scss
$Primary: #228891;
$Gray100: #f7f7f7;
// colors.js
export const COLORS = {
  Primary: "#228891",
  Gray100: "#f7f7f7",
}

存在的问题:

  1. ❌ 重复定义:同一个颜色值需要在多个地方维护
  2. ❌ 容易不一致:修改时可能遗漏某个地方
  3. ❌ 维护成本高:新增或修改颜色需要同步多个文件
  4. ❌ 容易出错:手动同步容易产生错误

解决方案

采用 Single Source of Truth (SSOT) 原则,使用单一数据源 + 构建时代码生成的方式:

单一数据源 (colors.js)
    ↓
构建时自动生成
    ↓
┌─────────────────────┬─────────────────────┐
│ _colors-generated.scss │ colors-generated.js │
│ (SCSS 变量)          │ (JS 常量)            │
└─────────────────────┴─────────────────────┘

技术实现

1. 单一数据源:colors.js

所有颜色定义在一个 JavaScript 文件中,支持注释和分组:

/**
 * Color definitions - single source of truth
 * This file is used to generate SCSS variables and JS constants via build/generateColors.js
 */

// Base colors
const white = "#fff"
const black = "#000"

// Primary colors
const Primary = "#228891"
const Primary100 = "#eff6fa"
const Primary200 = "#c1dbe7"

// Composite colors (using variables for clarity)
// These are calculated from base colors above, making the relationship clear
const Outline = "rgba(255, 255, 255, 0.08)" // rgba(white, 0.08)
const Overlay1 = "rgba(0, 0, 0, 0.1)" // rgba(black, 0.1)

// Export all colors as a flat object for the generator
module.exports = {
  Primary,
  black,
  white,
  Primary100,
  Primary200,
  Outline,
  Overlay1,
  // ... more colors
}

优势:

  • ✅ 可以添加注释说明颜色的用途和来源
  • ✅ 可以使用变量和表达式(通过注释说明关系)
  • ✅ 可以分组组织,更易维护
  • ✅ 复合颜色可以清晰标注来源(如 rgba(white, 0.08))

2. 代码生成器:generateColors.js

构建时脚本,读取 colors.js 并生成两个文件:

/**
 * Generate color files at build time
 * Generate SCSS variable file and JS constants file from colors.js
 */
const path = require("path")

const colorsJsPath = path.resolve(__dirname, "../src/style/colors.js")
const scssOutputPath = path.resolve(
  __dirname,
  "../src/style/_colors-generated.scss"
)
const jsOutputPath = path.resolve(__dirname, "../src/style/colors-generated.js")

function generateColorFiles() {
  // Read JS file (use require to execute JS code)
  // Clear require cache to ensure we always get the latest content
  delete require.cache[require.resolve(colorsJsPath)]
  const colors = require(colorsJsPath)

  // Generate SCSS variable file
  let scssContent =
    "// Auto-generated file by build/generateColors.js, do not edit manually\n"
  scssContent += "// Color definitions are in src/style/colors.js\n\n"

  Object.entries(colors).forEach(([key, value]) => {
    scssContent += `$${key}: ${value};\n`
  })

  // Generate JS constants file
  let jsContent = "/**\n"
  jsContent +=
    " * Auto-generated file by build/generateColors.js, do not edit manually\n"
  jsContent += " * Color definitions are in src/style/colors.js\n"
  jsContent += " */\n\n"
  jsContent += "export const COLORS = {\n"

  Object.entries(colors).forEach(([key, value]) => {
    jsContent += `  ${key}: "${value}",\n`
  })

  jsContent += "};\n"

  // Write files
  const fs = require("fs")
  fs.writeFileSync(scssOutputPath, scssContent, "utf8")
  fs.writeFileSync(jsOutputPath, jsContent, "utf8")

  console.log("✅ Color files generated successfully:")
  console.log(`   - ${scssOutputPath}`)
  console.log(`   - ${jsOutputPath}`)
}

// Execute if this script is run directly
if (require.main === module) {
  generateColorFiles()
}

module.exports = generateColorFiles

关键点:

  • 清除 require 缓存,确保每次获取最新内容
  • 支持直接运行:node generateColors.js
  • 支持模块导入:供 Webpack 插件使用

3. Webpack 插件:ColorGeneratorPlugin.js

集成到构建流程,实现自动化:

/**
 * Webpack plugin: Auto-generate SCSS and JS color files from colors.js
 */
const fs = require("fs")
const path = require("path")
const generateColorFiles = require("./generateColors")

class ColorGeneratorPlugin {
  apply(compiler) {
    // Generate color files before compilation starts
    compiler.hooks.beforeRun.tap("ColorGeneratorPlugin", () => {
      generateColorFiles()
    })

    // Watch colors.js file changes
    compiler.hooks.afterCompile.tap("ColorGeneratorPlugin", (compilation) => {
      const colorsJsPath = path.resolve(__dirname, "../src/style/colors.js")
      if (fs.existsSync(colorsJsPath)) {
        compilation.fileDependencies.add(colorsJsPath)
      }
    })

    // Regenerate when colors.js changes
    compiler.hooks.invalid.tap("ColorGeneratorPlugin", (fileName) => {
      if (fileName && fileName.includes("colors.js")) {
        generateColorFiles()
      }
    })
  }
}

module.exports = ColorGeneratorPlugin

Webpack Hooks 说明:

  1. beforeRun:编译开始前生成颜色文件,确保文件存在
  2. afterCompile:将 colors.js 添加到文件依赖,让 Webpack 监听变化
  3. invalid:当 colors.js 变化时,立即重新生成颜色文件

工作流程:

用户修改 colors.js
    ↓
Webpack 检测到变化(通过 afterCompile 添加的依赖)
    ↓
触发 invalid hook
    ↓
重新生成颜色文件
    ↓
Webpack 开始重新编译

4. 集成到项目

在 webpack.config.js 中配置:

const ColorGeneratorPlugin = require("./build/ColorGeneratorPlugin")

module.exports = {
  configureWebpack(config) {
    const plugins = [
      new ColorGeneratorPlugin(), // 添加插件
      // ... other plugins
    ]
    return { plugins }
  },
}

使用方式

在 SCSS 中使用

// _variables.scss
@import "./_colors-generated";

// 直接使用
.my-class {
  color: $Primary;
  background: $Gray100;
  border: 1px solid $Border;
}

在 JavaScript/模板中使用

// 1. 导入颜色常量
import { COLORS } from "@style/colors-generated"

// 2. 在组件中使用
createPage({
  data() {
    return {
      primaryColor: COLORS.Primary,
    }
  },
})
<!-- 3. 在模板中使用 -->
<Custom-Com color="{{primaryColor}}" />

优势总结

优势说明
✅ 单一数据源所有颜色在一个地方定义,避免重复
✅ 自动同步修改 colors.js 后自动生成所有格式
✅ 类型安全减少手动同步导致的错误
✅ 易于维护新增颜色只需在一个地方添加
✅ 支持注释JS 文件支持注释,说明颜色用途
✅ 构建时生成不增加运行时开销
✅ 热更新支持开发时修改 colors.js 自动重新生成

注意事项

  1. ⚠️ 不要手动修改生成的文件(_colors-generated.scss 和 colors-generated.js)
  2. ✅ 所有颜色修改都在 colors.js 中进行
  3. ✅ 生成的文件已加入 .gitignore,不会提交到仓库
  4. ✅ 修改 colors.js 后,Webpack 会自动重新生成文件

手动生成

如果需要手动生成颜色文件,可以运行:

npm run generate:colors

项目结构

project/
├── src/
│   └── style/
│       ├── colors.js                    # 单一数据源(手动维护)
│       ├── _colors-generated.scss       # 自动生成(不要手动修改)
│       ├── colors-generated.js          # 自动生成(不要手动修改)
│       └── _variables.scss              # 引入生成的文件
├── build/
│   ├── generateColors.js                # 代码生成器
│   └── ColorGeneratorPlugin.js          # Webpack 插件
└── webpack.config.js                        # 配置插件

总结

通过单一数据源 + 构建时代码生成的方案,我们实现了:

  • 🎯 消除重复:颜色定义只在一个地方
  • 🚀 自动化:修改后自动同步到所有格式
  • 📝 可维护:支持注释和分组,代码更清晰
  • 🔄 实时更新:开发时支持热更新

这是一个典型的 DRY (Don't Repeat Yourself) 原则的实践,通过构建工具自动化解决了多格式同步的问题。


相关文件:

  • 数据源:colors.js
  • 生成器:build/generateColors.js
  • Webpack 插件:build/ColorGeneratorPlugin.js
打磨与发布python 和 JavaScript 的不同
目录
  • 背景与问题
  • 解决方案
  • 技术实现
  • 1. 单一数据源:`colors.js`
  • 2. 代码生成器:`generateColors.js`
  • 3. Webpack 插件:`ColorGeneratorPlugin.js`
  • 4. 集成到项目
  • 使用方式
  • 在 SCSS 中使用
  • 在 JavaScript/模板中使用
  • 优势总结
  • 注意事项
  • 手动生成
  • 项目结构
  • 总结