首页 > 其他分享 >一种比css_scoped和css_module更优雅的避免css命名冲突小妙招

一种比css_scoped和css_module更优雅的避免css命名冲突小妙招

时间:2022-09-01 10:44:23浏览次数:82  
标签:color module red 小妙 组件 class css

css_scoped 与 css_module

我们知道,简单的class名称容易造成css命名重复,比如你定义一个class:

<style>
.main { float: left; }
</style>

如果别人刚好也定义了一个className:.main,你的float:left就会影响到它。

所以Vue中发明了css_scoped,其原理就是在class名称后加上一个data属性选择器:

<style scoped>
.main { float: left; } 
</style>

//转义后变成
<style>
.main[data-v-49729759] { float: left }
</style>

css_scoped是Vue的专用方案,如果你使用React等其它UI框架,那么你可以使用更通用的css_module,其原理是为样式名加hash字符串后缀,从而保证class名全局唯一:

<style module>
.main { float: left; } 
</style>

//转义后变成
<style>
.main_3FI3s6uz { float: left; } 
</style>

相比于css_scopedcss_module方案更通用,不改变其本身的权重,而且渲染性能要比前者好很多,所以更推荐大家使用css_module

不足之处

然而不管是css_scoped还是css_module,都绕不开2大缺点:

  1. 由于加上了随机字符,所以如果想在父组件中覆盖子组件中的样式变得麻烦,虽然css_scoped可以使用穿透,但这样容易引发别的问题。
  2. 加上随机字符让class名称变得不优雅,也影响编译速度。

css命名空间

我们来回忆一下,在css_scopedcss_module出现之前,人们是如何避免css命名冲突的?

对,就是人为的定义一些css命名空间

那个时候,对每个Component组件都会在其根节点上定义一个不重复的ID或者class作为其命名空间,然后其内部的其它class都会以此命名空间作为前置限定,比如:

<div class="table-list">
    <div class="hd"></div>
    <div class="bd"></div>
    <div class="ft"></div>
</div>

<style>
.table-list {
    > .hd {
        color: red
    }
    > .bd {
        color: blue
    }
} 
</style>

这样一来,只要保证根节点的class不重复,其子节点的class就不会重复。

而对于一些全局样式,人们习惯加上一个g-作为命名空间,比如:

<style>
.g-hd {
    color: red
} 
</style>

这种依靠人为约定的css命名空间,虽然比较原始,但有其优点:

  • 简单有效,按模块-组件名称的命名约定,基本上很容易保证其不重复。
  • 样式名更具语义,从任何一个dom出发,向上一定能找到其组件根节点class名,基本上就能猜到其组件所在的业务模块、组件位置等。
  • 父组件很容易利用权重覆盖子组件的任何样式。

css_namespace + css_module

如果我们把css_modulecss_命名空间结合起来,组件的命名空间由css_module自动生成,那岂不是一种更优雅的解决css冲突的方案么?

css_module中有2个特别的作用域限定符:

  • :global 该限定符下的class名称将保持原样,不会被css moudle转换,比如:
    :global { 
        .test1 { color: blue; } 
        .test2 { color: red; } 
    }
    //编译后
    .test1 { color: blue; }
    .test2 { color: red; } 
    
  • :local 该限定符下的class名称,将会被css moudle转换,比如:
      :local { 
          .test1 { color: blue; } 
          .test2 { color: red; } 
      }
      //编译后
      .test1_3zyde4l1y { color: blue; }
      .test2_2DHwuiHWM { color: red; } 
    

如果我们使用css_namespace + css_module

<div :class="styles.root">
    <div class="hd"></div>
    <div class="bd"></div>
    <div class="ft"></div>
</div>

<style module>
:global {
  :local(.root) {
    > .hd { 
     color: red;
     .title {
         font-size: 18px;
     }
    }
    > .bd { color: blue; }
  }
}
</style>

//css编译后
<style>
.root_3zyde4l1y > .hd{ color: red; }
.root_3zyde4l1y > .hd .title{ font-size: 18px; }
.root_3zyde4l1y > .bd{ color: blue; }
</style>

这样的意思是:

  • 每个组件原则上仅根节点使用css_module自动生成不重复的class名称,其余内部元素保持原始命名,不做任何转换。(当然某些情况下,也可以使用多个转换)
  • 为了保证孙子辈样式不影响别人,可以适当加入dom层级限定,比如> .hd这样就只会影响子级的.hd

去除css_moudle随机字符

<style>
.root_3zyde4l1y > .hd{ color: red; }
.root_3zyde4l1y > .hd .title{ font-size: 18px; }
.root_3zyde4l1y > .bd{ color: blue; }
</style>

根节点上的class命名带个hash小尾巴,仍然很不优雅。其实hash字符只是为了保证这个名称全局唯一而已,你也可以使用另外的方法来保证。如果你为工程设计一个有意义的目录结构,那么完全可以使用目录路径来替代hash字符串,比如你的工程目录如下:

src
├── components
│    ├── moduleA
│    │     ├── componentX
│    │     ├── componentY
│    ├── moduleB
│    │     ├── componentZ

那么:components-moduleA-componentX这个目录路径一定是全局唯一的,所以你可以使用这个路径来替代hash字符,css_module提供了自定义转换className的方法:

type getLocalIdent = (
        context: LoaderContext,
        localIdentName: string,
        localName: string
) : string;

你可以通过该方法来将目录路径映射为class名称,并替换掉一些固定的目录,比如工程目录如下:

src
├── assets
│     ├── css
│           ├── global.module.scss //全局样式
│                  ├── :local(.loading) {} //全局样式只需要加个g-前缀,编译成.g-loading
├── components
│     ├── NavBar
│           ├── index.module.scss
│                  ├── :local(.root) {} //根据目录路径可编译成即可.comp-NavBar
│
├── modules
│     ├── user
│           ├── components
│                 ├── LoginForm
│                         ├── index.module.scss
│                               ├── :local(.root) {} //根据目录路径可编译成.user-LoginForm,
│

注意的是src/modules/user/components/LoginForm/index.module.scss,根据目录路径可以生成:modules-user-components-LoginForm,但因为user是一个module,其名称是唯一的,且内部结构遵循约定,所以可以简化为:user-LoginForm

根据class名称推测文件位置

  • .g-loading - 带g-前缀,说明它是一个全局class,对应的文件一定是src/assets/css/global.module.scss
  • .comp-NavBar - 带comp-前缀,说明它是一个公共组件,对应的组件一定是src/components/NavBar
  • .user-LoginForm - 根据约定,对应的组件一定是src/modules/user/components/LoginForm

示例及源码

如果你也使用类似的工程目录,那么可以直接使用我封装好了的路径映射函数getCssScopedName

const {getCssScopedName} = require('@elux/cli-utils');
const srcPath = path.resolve(__dirname, '../src');

// webpack css-loader
{
    loader: 'css-loader',
    options: {
      importLoaders: 2,
      modules: {
        getLocalIdent: (context, localIdentName, localName) => {
          return getCssScopedName(srcPath, localName, context.resourcePath);
        },
        localIdentContext: srcPath,
      },
    },
  };

当然你也可自己实现个性化的getLocalIdent,无非就是一些正则匹配与替换罢了...

采用css_namespace + css_module的实际案例:

111.jpg

如图所示,通过class名称基本上就能推测出组件位置...

标签:color,module,red,小妙,组件,class,css
From: https://www.cnblogs.com/hiisea/p/16645688.html

相关文章

  • 下一代 CSS 框架推荐
    tailwindcss:https://tailwindcss.com/WindiCSS:https://cn.windicss.org/unocssunocss是AnthonyFu大佬写的;unocss:https://github.com/unocss/unocss......
  • 如何使用 Bootstrap 处理 CSS
    如何使用Bootstrap处理CSS大家好!如果您像我一样开始使用CSS编码并使用它进行任何大型项目,那么您肯定会因为响应式布局、溢出和选择器特异性而感到数不清的头痛。这就......
  • 如何仅使用 CSS 创建响应式网站
    如何仅使用CSS创建响应式网站使用vw和rem构建响应式页面。Photoby用户体验商店on不飞溅前言从移动浏览器或应用程序访问的网站越来越多。对我来说,在空闲......
  • 【学习笔记】CSS 动画keyframes
    【学习笔记】CSS动画keyframes必要项目@keyframes动画名称对应animation-name:动画名称动画持续时间,指动画开始到结束时间,预设为0,若没有设定,动画不会执行。下......
  • CSS — 如何使用关键帧创建简单的动画
    CSS—如何使用关键帧创建简单的动画我决定对我从事的一些项目的关键帧做一个简单的教程。我不会使用JS对于本教程。第1步—设置HTML首先添加一些简单的样板H......
  • px,魔法单位 | CSS
    px,魔法单位|CSS1px是指CSS中每个屏幕上的1个像素吗?我们都在CSS中使用px单位来为宽度、高度、边距、字体大小等赋值。但这里的问题是一个像素到底是多少。它......
  • 使用 CSS 输入标签 — 教程
    使用CSS输入标签—教程HTML对于HTML,我们需要一个带有标签和输入的容器。我们还将为输入设置占位符(“”),这将允许我们使用CSS检测输入何时具有某些值。<divcla......
  • 最好的新 CSS 功能
    最好的新CSS功能ThebestnewCSSfeaturesfor2022自1996年引入CSS以来,它仍然是Web开发堆栈中必不可少的、不断发展的部分。CSS不断推出新功能以响应现实世......
  • Sass 变量与 CSS 自定义属性
    Sass变量与CSS自定义属性这篇文章最初发表在我们自己的博客网站上一探究竟看到更多这样的帖子!在这篇文章中语境真正的区别不可知论方法最后的想法语境在......
  • 我们应该如何在 CSS 中使用 rem、em、vw 和 px?
    我们应该如何在CSS中使用rem、em、vw和px?当我们开始创建网站时,我们通过HTML创建网站的基本结构,并使用CSS使网站的设计看起来更好。让我们检查一下rem、em、vw......