概述
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