深入理解Vue3中style的scoped
概述
scoped
的作用就是样式模块化(CSS Module
),即给组件每一个元素(以及非动态添加的子组件的根元素)加上一个data-v-xxxx
的属性,样式选择器也会格式化成选择器[data-v-xxxx]
,这样就做到了样式隔离,每个组件内定义的样式只对该组件生效,避免了不同组件或页面的样式(选择器)冲突。本文将以vue3
为例,深入了解scoped
原理。
scoped
实践
vue3
组件是如下定义样式:
<template>
<div class="header">
<span>标绘管理</span>
</div>
</template>
<style scoped>
.header span {
position: relative;
margin-left: 54px;
font-size: 16px;
display: inline-flex;
height: 24px;
line-height: 24px;
font-family: "Microsoft YaHei";
font-weight: bold;
color: #303133;
}
</style>
- 效果如下:
scoped
源码分析
compiler-sfc
模块
vue3
中有个模块@vue/compiler-sfc
,这个模块是单独拎出来,不会被打包到vue.global.js
。compiler-sfc
主要作用就是用来编译单文件组件,就是.vue
。因为scoped
的实现是在compiler-sfc
模块中,所以本文的所有的讨论也是基于SFC
。
vite
与plugin-vue
vue3
如果是通过vite
搭建的,那么compiler-sfc
会通过vite
的plugin-vue
调用,这在script 标签的 setup 实现原理中有讲解,可以简短回顾。
Style 样式选择器中的处理
在 plugin-vue
中会读取.vue
组件,并识别<style></style>
部分,如下
if (query.type === "style") {
return transformStyle(
code,
descriptor,
Number(query.index || 0),
options.value,
this,
filename
);
}
transformStyle
函数会将<style></style>
的 code 传给compiler.compileStyleAsync
,并返回编译的结果,其中参数包含id
和scoped
。
-
id
的生成id
包含于descriptor
中,其生成过程如下所示:
descriptor.id = getHash(normalizedPath + (isProduction ? source : "")); // normalizedPath:序列化文件路径后的字符串,如果是生产环境,还会加上源码
function getHash(text) {
return node_crypto
.createHash("sha256")
.update(text)
.digest("hex")
.substring(0