每天一小步 <scoped是怎么保护CSS类的>

1,311 阅读2分钟

前言

在开始正片之前,还是先抛一块砖👇

最近在写自己的小项目,引入了NProgress作为页面加载的进度条,但是感觉内置的background-color和项目的主题不太搭,所以打算换一个背景色,由此引出了一个scoped的问题

通过在度娘一番搜索,我很快就找到了替换background-color的解决方法👇

/* App.vue */
#nprogress .bar{
    background: red !important;
}

然而就在我按下crtl s的时候,却发现样式并没有生效,在反复刷新之后仍然没有生效。这时我想着要不直接改nprogress.css文件算了,但是身体里不羁的内卷灵魂阻止了我的行动,于是乎就有了本文的启发

这时我发现在<style>标签上有一个scoped属性,由于在App.vue文件中有一些特定的样式,因此加上了scoped保护属性不重名,才导致了上面样式的不生效

因此我把这个样式抽离到了全局CSS文件中,然后直接引入到main.ts,就实现了background-color的修改

由此引伸出两个问题:

1. 为什么使用了scoped就能防止样式污染?

2. scoped是怎么工作的?

为什么使用了scoped就能防止样式污染

首先我们来创建一个Vue项目看一下网页中的效果(这里使用的是Vue3 + Vite进行演示)

scoped演示.jpg

我们可以看到编译出来的html文档中,为元素添加了[data-v-hash]属性

在控制台中,也为添加了scoped属性的<style>标签中的选择器加上了[data-v-hash]

选择器scoped.png

Vue3的官方文档中是这样解释的👇

<style> 标签带有 scoped attribute 的时候,它的 CSS 只会应用到当前组件的元素上。这类似于 Shadow DOM 中的样式封装。它带有一些注意事项,不过好处是不需要任何的 polyfill。它的实现方式是通过 PostCSS 将以下内容:

<style scoped>
.example {
  color: red;
}
</style>

<template>
  <div class="example">hi</div>
</template>

转换为:

<style>
.example[data-v-f3f3eg9] {
  color: red;
}
</style>

<template>
  <div class="example" data-v-f3f3eg9>hi</div>
</template>

简单地说就是通过为html元素选择器加上特殊的标识符,来达到唯一属性的目的,就不会污染全局环境

那么这种方式是怎么实现的呢?接着往下看

scoped是怎么工作的?

在上面的Vue3官方文档介绍中,提到scoped功能是由PostCSS实现的,那么我们来看看具体使用了什么插件

下面是Vue3的部分源码👇最终可以定位到postcss-modules-scope这个插件

// compiler-sfc.cjs.js

const plugin = (options = {}) => {
  const generateScopedName =
    (options && options.generateScopedName) || plugin.generateScopedName;
  const generateExportEntry =
    (options && options.generateExportEntry) || plugin.generateExportEntry;
  const exportGlobals = options && options.exportGlobals;
  return {
    postcssPlugin: "postcss-modules-scope",
    ...
  };
};

官方的npm文档中可以了解到,这个插件可以将CSS类转换成CSS Module,这也就是为什么不用担心污染全局作用域的原因

把下面的代码👇


:local(.continueButton) {
  color: green;
}

转换为👇

:export {
  continueButton: __buttons_continueButton_djd347adcxz9;
}
.__buttons_continueButton_djd347adcxz9 {
  color: green;
}

因此可以用下面这种方式直接在JS中使用,而不用担心污染全局作用域

import styles from "./buttons.css";
elem.innerHTML = `<button class="${styles.continueButton}">Continue</button>`;

至于最终编译成控制台中的html文档形式,由于本人能力有限,目前对Vue3的源码理解还处于非常浅薄的状态,如果之后有更深入的理解再更新文章(希望有大佬能在社区科普一下^_^)

结语

本来这篇文章是打算深入学习一下Vue3源码的,但是无奈本人能力有限还不能很好的理解其中的原理, 如果有任何需要修改或者错误的地方,恳请指出,感激不尽