css 样式隔离

BEM(Block Element Modifier)

  • Block:表示一个独立的、具有明确功能的实体。例如,.header 或 .nav。
  • Element:表示一个 Block 内的元素。它始终与其父 Block 相关联,并使用双下划线分隔。例如,.button__icon 或 .form__input
  • Modifier:表示 Block 或 Element 的状态、变体或特性。它使用双连字符分隔。例如,.button–primary 或 .form__input–invalid。
/* 基本 Block */
.button {
  background-color: #4CAF50;
  border: none;
  color: white;
  padding: 15px 32px;
  text-align: center;
  display: inline-block;
  font-size: 16px;
  margin: 4px 2px;
}

/* Element */
.button__icon {
  /* 添加 icon 的样式 */
}

/* Modifier */
.button--primary {
  background-color: #008CBA;
}

.button--danger {
  background-color: #f44336;
}

CSS Modules

  • 每个组件都有独立的 CSS 文件,并在构建过程中为每个类生成唯一的哈希值,以确保样式隔离
// 通过 css-loader 转换 hash 类名
const path = require('path');
const { getLocalIdent } = require('./css-modules');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              module: true,
              getLocalIdent: (loaderContext, localIdentName, localName, options) => {
                return getLocalIdent(loaderContext, localIdentName, localName, options);
              },
            },
          },
        ],
      },
    ],
  },
};
// 将类名转换成唯一 hash
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');

function generateHashedClassName(filename, className) {
  const content = fs.readFileSync(filename, 'utf-8');
  const hash = crypto
    .createHash('md5')
    .update(content)
    .digest('base64')
    .substr(0, 5);
  
  return `${className}--${hash}`;
}

function getLocalIdent(loaderContext, localIdentName, localName, options) {
  const fileName = path.basename(loaderContext.resourcePath);
  return generateHashedClassName(loaderContext.resourcePath, localName);
}

module.exports = {
  getLocalIdent
};

Shadow DOM

  • 使用 Shadow DOM 技术,组件的 CSS 样式会被限制在组件的局部范围内,从而实现样式隔离。

详情

CSS-in-JS

  • 动态生成样式:在 CSS-in-JS 中,组件的样式可以通过 inline JavaScript 对象或模板字符串直接在组件中赋值。这意味着可以根据组件的属性和状态动态调整样式。
  • 局部作用范围:由于 CSS-in-JS 将样式直接与特定组件关联,每个组件的样式都具有局部作用范围。通过局部变量和组件生命周期钩子,可以在组件卸载时自动取消应用其样式,避免影响其他组件的样式。
  • 唯一的类名:当使用 CSS-in-JS 库(例如 styled-components)时,它们会在运行时为每个组件生成唯一的类名。这使得每个组件的样式完全分离,不会与其他组件发生命名冲突。
  • 优化渲染:CSS-in-JS 库会在组件挂载时自动将样式插入到 DOM 中。这样可以确保只有必要的样式被渲染,以提高性能和避免渲染不必要的全局样式。

核心是将唯一样式和元素关联起来

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSS-in-JS</title>
</head>
<body>
    <button id="myButton">Click me!</button>

    <script src="css-in-js.js"></script>
</body>
</html>
const myButton = document.getElementById('myButton');

// 生成唯一哈希值
function generateHash() {
  return Math.random().toString(36).substr(2, 5);
}

// 创建样式标签
function createStyleTag(css) {
  const style = document.createElement('style');
  style.type = 'text/css';
  style.appendChild(document.createTextNode(css));
  document.head.appendChild(style);
}

// 将样式与元素关联
function cssInJs(element, styles) {
  const hashedClassName = 'css_in_js_' + generateHash();
  element.classList.add(hashedClassName);

  const css = `
    .${hashedClassName} {
      background-color: lightblue;
      color: white;
      padding: 0.5rem 1rem;
      border: none;
      font-size: 1.2rem;
      cursor: pointer;
    }
  `;
  createStyleTag(css);
}

// 应用样式
cssInJs(myButton);

Scoped CSS

  • 使用 Scoped CSS 技术来实现 CSS 隔离,每个组件的样式都会被包裹在一个特定的属性选择器中,避免样式冲突和影响。
const myButton = document.getElementById('myButton');
const styleElement = document.getElementById('css');

// 生成唯一哈希值
function generateHash() {
  return Math.random().toString(36).substr(2, 5);
}

// 添加唯一属性
function addScopedAttribute(element, attributeName) {
  element.setAttribute(attributeName, '');
}

// 创建 Scoped CSS 的样式
function createScopedCSS(attributeName, css) {
  // 将应用到组件的原始选择器类名替换成属性选择器
  // .myButton {...} 类样式将变为 .myButton[data-scoped-abcdef] {...}
  const scopedCSS = css.replace(/(\.\w+)/g, `$1[${attributeName}]`);
  styleElement.textContent += scopedCSS;
}

// 实现 Scoped CSS
function scopedCSS(element, styles) {
  const uniqueAttribute = `data-scoped-${generateHash()}`;
  // 将唯一属性设置到元素上,从而能通过属性选择器匹配
  addScopedAttribute(element, uniqueAttribute);
  createScopedCSS(uniqueAttribute, styles);
}

// 定义样式
const buttonStyles = `
  .myButton {
    background-color: lightblue;
    color: white;
    padding: 0.5rem 1rem;
    border: none;
    font-size: 1.2rem;
    cursor: pointer;
  }
`;

// 应用样式
scopedCSS(myButton, buttonStyles);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值