阿里云主机折上折
  • 微信号
Current Site:Index > CSS-in-JS vs preprocessors vs vanilla CSS: Which one makes you lose more hair?

CSS-in-JS vs preprocessors vs vanilla CSS: Which one makes you lose more hair?

Author:Chuan Chen 阅读数:51706人阅读 分类: 前端综合

The debate between CSS-in-JS, preprocessors, and vanilla CSS has never ceased, with each approach having its advocates and detractors. The choice depends on project scale, team habits, and tech stack, but their differences go far beyond syntax. Below, we dissect the pros and cons of these approaches from various angles to see which one is more likely to drive you crazy.

CSS-in-JS: The Magic of Dynamic Styles

Representative libraries like styled-components and Emotion write styles directly into JavaScript files. This approach is particularly popular in the React ecosystem because it allows for dynamic style generation and naturally supports componentization.

// styled-components example
import styled from 'styled-components';

const Button = styled.button`
  background: ${props => props.primary ? 'palevioletred' : 'white'};
  color: ${props => props.primary ? 'white' : 'palevioletred'};
  font-size: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

function App() {
  return (
    <div>
      <Button>Normal</Button>
      <Button primary>Primary</Button>
    </div>
  );
}

Pros:

  • Co-location of styles and components, avoiding global pollution
  • Supports dynamic styles (e.g., theme switching)
  • Automatic vendor prefixing and CSS minification

Pain Points:

  • Runtime performance overhead (especially in SSR scenarios)
  • Debugging difficulties (generated class names like sc-bdVaJa are hashed)
  • Steep learning curve (must master library-specific APIs)

Preprocessors: Syntactic Sugar

Preprocessors like Sass/Less/Stylus extend CSS functionality with features like variables, nesting, and mixins. They require a compilation step but output traditional CSS.

// Sass example
$primary-color: #ff4757;
$border-radius: 4px;

@mixin flex-center {
  display: flex;
  justify-content: center;
  align-items: center;
}

.button {
  @include flex-center;
  background: lighten($primary-color, 10%);
  border-radius: $border-radius;
  &:hover {
    background: darken($primary-color, 5%);
  }
}

Advantages:

  • Mature tooling (e.g., Webpack's sass-loader)
  • Clear compiled output
  • Community积累了大量的mixin库(如Compass)

Limitations:

  • Still requires manual scoping
  • Limited dynamic capabilities (cannot modify variables at runtime)
  • Nesting abuse can lead to CSS specificity nightmares
/* Compiled garbage code example */
.parent .child .grandchild button:hover {}

Vanilla CSS: Back to Basics

With the advent of CSS custom properties (variables) and modern selectors, native CSS has significantly improved:

/* Modern CSS example */
:root {
  --primary: #ff4757;
  --radius: 4px;
}

.button {
  --bg-color: color-mix(in srgb, var(--primary), white 10%);
  display: grid;
  place-items: center;
  background: var(--bg-color);
  border-radius: var(--radius);
  transition: background 0.3s;
}

.button:hover {
  --bg-color: color-mix(in srgb, var(--primary), black 5%);
}

Highlights:

  • Zero runtime overhead
  • Native browser support, no toolchain required
  • Gradually improving modularity (e.g., @scope and other new features)

Drawbacks:

  • Lacks programming capabilities (loops, conditionals, etc.)
  • Variables can only be used for values, not selectors
  • Legacy browser compatibility issues

Performance Showdown

Performance comparison for 1,000 dynamically styled buttons:

Approach Size First Render Style Update
CSS-in-JS (runtime) Large Slow Slow
Sass Medium Fast Fast
CSS + JS Variables Small Fastest Medium

CSS-in-JS can cause memory leaks in complex interactive apps because style objects accumulate as components unmount.

Team Collaboration Costs

  • CSS-in-JS: Requires consensus on library choice (e.g., Emotion vs. styled-components)
  • Preprocessors: Need conventions for nesting depth and mixin usage
  • Vanilla CSS: Naming conventions (BEM vs. SMACSS) often spark debates
# Typical CSS naming collision
.header .title {} /* Product page */
.header .title {} /* Blog page */

Toolchain Dependencies

  • CSS-in-JS requires Babel plugins for syntax highlighting
  • Sass needs Ruby or Dart compilers
  • Vanilla CSS may rely on PostCSS for polyfills
// Common webpack configuration differences
module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: ['sass-loader']
      },
      {
        test: /\.styled\.js$/,
        use: ['styled-components-loader']
      }
    ]
  }
}

The Dark Side of Style Maintenance

Real-world migration案例 from a large project:

  1. Migrating from Sass to CSS-in-JS: Required rewriting 200+ mixins
  2. Reverting from CSS-in-JS to Sass: Lost all dynamic theme logic
  3. Adopting vanilla CSS: Manually maintaining 10+ variable files

Future Trends

  • CSS WG's @scope and @layer may change the game
  • WASM-accelerated CSS processors (e.g., Lightning CSS) are rising
  • Browsers逐步支持CSS模块导入
/* Possible future CSS syntax */
@import "./theme.css" layer(utilities);
@scope (.card) to (.content) {
  :scope { padding: 2rem; }
}

Which Approach Should You Choose?

Small React apps: CSS-in-JS enables rapid iteration
Design system projects: Sass is better for static style libraries
Performance-critical needs: Vanilla CSS with careful JS variable control
Legacy system overhauls: PostCSS逆向兼容可能是唯一选择

There's no silver bullet—only what fits your current scenario. When you're debugging z-index issues at 3 AM, any choice might make you want to tear your hair out.

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.