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_scoped
,css_module
计划更通用,不扭转其自身的权重,而且渲染性能要比前者好很多,所以更举荐大家应用css_module
。
不足之处
然而不论是css_scoped
还是css_module
,都绕不开2大毛病:
- 因为加上了随机字符,所以如果想在父组件中笼罩子组件中的款式变得麻烦,尽管
css_scoped
能够应用穿透,但这样容易引发别的问题。 - 加上随机字符让class名称变得不优雅,也影响编译速度。
css命名空间
咱们来回顾一下,在css_scoped
和css_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_module
和css_命名空间
联合起来,组件的命名空间由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
的理论案例:
- 基于Antd的后盾管理系统
- 或者应用任意一个elux工程模版:
npm create elux@latest 或 yarn create elux
如图所示,通过class名称基本上就能揣测出组件地位...
欢送交换,自己近期文章:
- 前端架构-分层而治,铁打的MV流水的C
- 手撸Router,还要啥Router框架?让react-router/vue-router躺一边凉爽去
- 不想当Window的Dialog不是一个好Modal,弹窗翻身记