打造自己的组件库(二)CSS工程化方案

1. css工程化方案

1.1. 目录结构设计

复制代码
src/assets/styles/
├── index.scss          # 主入口文件
├── variables.scss      # 全局CSS变量定义
├── mixins.scss        # SCSS混入
├── reset.scss         # 样式重置
└── theme/
    ├── light.scss     # 亮色主题
    └── dark.scss      # 暗色主题

1.2. CSS工程化特点

1.2.1 模块化导入

scss 复制代码
@use './theme/dark.scss';
@use './theme/light.scss';
@use './reset.scss';
@use './mixins.scss';
@use './variables.scss';

1.2.2 CSS变量系统

项目建立了完整的CSS变量系统,包括:

  • 设计令牌: 颜色、字体、间距、圆角等
  • 主题变量: 支持多主题切换
  • 组件变量: 组件级别的样式变量

1.2.3 命名规范

采用BEM命名规范,使用ms-前缀:

  • --ms-color-primary: 主色调
  • --ms-font-size-base: 基础字体大小
  • --ms-border-radius-base: 基础圆角

2. 主题切换功能实现

2.1. 核心实现原理

主题切换功能基于以下技术实现:

2.1.1 CSS变量 + 属性选择器

scss 复制代码
// 亮色主题
[data-theme="light"] {
  --ms-bg-color: #ffffff;
  --ms-text-color-primary: #303133;
  // ... 其他变量
}

// 暗色主题
[data-theme="dark"] {
  --ms-bg-color: #141414;
  --ms-text-color-primary: #e5eaf3;
  // ... 其他变量
}

2.1.2 JavaScript主题管理

typescript 复制代码
// src/utils/theme.ts
export function setTheme(theme: ThemeType): void {
  // 保存到localStorage
  localStorage.setItem(THEME_KEY, theme);
  
  // 设置HTML属性
  document.documentElement.setAttribute('data-theme', theme);
  
  // 触发主题变化事件
  window.dispatchEvent(new CustomEvent('theme-change', { detail: { theme } }));
}

2.1.3 初始化主题

typescript 复制代码
// src/main.ts
import { getCurrentTheme } from './utils/theme'

// 初始化主题
const theme = getCurrentTheme();
document.documentElement.setAttribute('data-theme', theme);

2.1.4 主题获取优先级

  1. localStorage: 用户手动设置的主题
  2. HTML属性: 当前DOM中的主题设置
  3. 系统偏好 : prefers-color-scheme媒体查询
  4. 默认值: 亮色主题

2.1.5 系统主题监听

typescript 复制代码
export function watchSystemTheme(): (() => void) | undefined {
  if (window.matchMedia) {
    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
    
    const handleChange = (e: MediaQueryListEvent) => {
      const newTheme: ThemeType = e.matches ? 'dark' : 'light';
      // 只有在没有手动设置主题时才跟随系统
      if (!localStorage.getItem(THEME_KEY)) {
        setTheme(newTheme);
      }
    };
    
    mediaQuery.addEventListener('change', handleChange);
    return () => mediaQuery.removeEventListener('change', handleChange);
  }
  return undefined;
}

2.2. 组件级主题应用

2.2.1 按钮组件主题适配

scss 复制代码
// src/components/button/Button.scss
.ms-button {
  --ms-button-font-weight: var(--ms-font-weight-primary);
  --ms-button-border-color: var(--ms-border-color);
  --ms-button-bg-color: var(--ms-fill-color-blank);
  --ms-button-text-color: var(--ms-text-color-regular);
  // ... 其他变量
  
  color: var(--ms-button-text-color);
  background-color: var(--ms-button-bg-color);
  border-color: var(--ms-button-border-color);
  transition: 0.1s;
}

2.2.2 主题切换组件

vue 复制代码
<!-- App.vue 中的主题切换 -->
<template>
  <Button @click="handleToggleTheme" :type="currentTheme === 'dark' ? 'primary' : undefined">
    <span class="theme-icon">{{ currentTheme === 'dark' ? '��' : '☀️' }}</span>
    <span class="theme-text">{{ currentTheme === 'dark' ? '切换到白天模式' : '切换到晚上模式' }}</span>
  </Button>
</template>

<script setup>
const handleToggleTheme = () => {
  const newTheme = toggleTheme();
  currentTheme.value = newTheme;
};
</script>

4. 主题色彩系统

项目采用了Element UI的色彩系统,包括:

4.1 语义化颜色
  • Primary: 主色调 (#409eff)
  • Success: 成功色 (#67c23a)
  • Warning: 警告色 (#e6a23c)
  • Danger: 危险色 (#f56c6c)
  • Info: 信息色 (#909399)
4.2 颜色变体

每个语义化颜色都有多个变体:

  • --ms-color-primary-light-3: 浅色变体
  • --ms-color-primary-light-5: 更浅色变体
  • --ms-color-primary-light-7: 最浅色变体
  • --ms-color-primary-dark-2: 深色变体

5. 过渡动画

主题切换时提供平滑的过渡效果:

scss 复制代码
.app-container {
  transition: background-color var(--ms-transition-duration);
}

.theme-switch {
  transition: all var(--ms-transition-duration);
}

3. postcss

PostCSS 是一个强大的 CSS 处理工具,可以通过插件链实现自动加前缀、现代特性降级、压缩优化等功能。

在你的项目中,PostCSS 主要负责:

  • 自动添加浏览器前缀(autoprefixer)
  • 现代 CSS 特性降级(postcss-preset-env)
  • 修复 flexbox 兼容性 bug(postcss-flexbugs-fixes)
  • 生产环境下 CSS 压缩(cssnano)

3.1. 配置文件说明

postcss.config.cjs

js 复制代码
module.exports = {
    plugins: [
        require('postcss-flexbugs-fixes'),
        require('autoprefixer'),
        require('postcss-preset-env')({
            stage: 1,
            features: {
                'nesting-rules': true
            }
        }),
        require('cssnano')({ preset: 'default' })
    ]
}

.browserslistrc

js 复制代码
>1%
last 20 versions
not dead
not ie 11
  • autoprefixer:根据 .browserslistrc 自动为 CSS 添加 -webkit-、-ms- 等前缀,保证兼容主流浏览器。

  • postcss-preset-env:让你可以直接写现代 CSS(如嵌套、逻辑属性、容器查询等),自动降级为目标浏览器支持的语法。

  • postcss-flexbugs-fixes:修复 flexbox 在部分浏览器下的已知 bug。

  • cssnano:生产环境下压缩 CSS,移除注释、空格、重复代码等,极大减小体积。

  • 指定目标浏览器范围,autoprefixer 和 postcss-preset-env 会据此决定哪些特性需要降级、哪些前缀需要添加。


3.2. Vite 与 PostCSS 的集成

  • Vite 默认会自动读取根目录下的 postcss.config.cjs 和 .browserslistrc,无需在 vite.config.ts 里重复配置。
  • 只需在 Vite 的 css.preprocessorOptions 配置 SCSS 变量等,PostCSS 相关配置自动生效。

3.3. 代码压缩与兼容性实现流程

  1. 开发时

    你可以直接使用现代 CSS 语法和特性(如嵌套、变量、容器查询等),无需手动添加前缀或考虑兼容性。

  2. 构建时(npm run build)

    • Vite 会调用 PostCSS,按 postcss.config.cjs 配置依次处理 CSS。
    • autoprefixer 根据 .browserslistrc 自动加前缀。
    • postcss-preset-env 将现代 CSS 转换为目标浏览器支持的语法。
    • cssnano 在生产环境下压缩 CSS,移除注释、空格、重复代码。
    • 最终生成的 dist/assets/index-xxxx.css 文件体积小、兼容性强。
  3. 验证

    • 你曾用 test-postcss.scss、browser-compatibility-test.scss 验证现代特性和前缀添加,构建后可见 -webkit-、-ms- 等前缀,现代特性被正确降级,且 CSS 被压缩。

3.4. 典型效果举例

开发时写法:

scss 复制代码
.delete-button {
  display: flex;
  user-select: none;
  &:hover {
    color: var(--ms-primary);
  }
}

构建后产物:

css 复制代码
.delete-button {
    display: flex;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none
}

.delete-button:hover {
    color: var(--ms-primary)
}
  • 自动加了 -webkit-、-moz- 前缀
  • 空格、注释被移除
  • 现代语法被降级
相关推荐
mldong6 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排6 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60617 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角8 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!8 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰8 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi10 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen10 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马11 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒12 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端