CSS variables can communicate better with JavaScript, and CSS variables are runtime.
Through this article, you will know and understand the following concepts:
- SFC Style-the style of the single file component;
- Native CSS variables-standard specifications defined by CSS authors;
- SFC Style Variables proposal (old version);
- SFC style CSS variable injection (new version);
- Use CSS variable injection and use native CSS variables in Vue3;
- The principle behind variable injection;
- Advantages of CSS variable injection.
In the SFC Style Variables proposal, it was introduced that the Vue SFC style provides simple CSS combination and encapsulation, but it is purely static — which means that so far we have no ability to dynamically update the style according to the state of the component at runtime .
Now most modern browsers support the native CSS variable , we can use it to easily connect the state and style of the component.
SFC Style Brief Introduction
Vue Single File Component (SFC) specification introduced that the .vue
file is a custom file type that uses HTML-like syntax to describe a Vue component. Each .vue
file contains three types of top-level language blocks <template>
, <script>
and <style>
, and also allows adding optional custom blocks.
Style language block:
- The default match:
/\.css$/
. A
.vue
file can contain multiple<style>
tags.The <style> tag can have scoped or module attributes (see scoped CSS and CSS Modules) to help you encapsulate the style into the current component. Multiple <style> tags with different packaging modes can be mixed in the same component.
.css
that match the 060d9262f21fc1 file (orlang
feature) will be applied to the content of<style>
vue-loader
will parse the file, extract language blocks, process them through other loaders if necessary, and finally assemble them into an ES Module, whose default export is an object of Vue.js component options.
Stlye module can be used lang
attribute specifies pretreatment CSS language (sass, less, stylus), as follows:
/* lang 属性指定扩展名 */
<style lang="sass">
/* write Sass!
</style>
You can also use the src
attribute to introduce external style resources:
<style src="./style.css"></style>
/* 从 npm 依赖中引入资源 */
<style src="todomvc-app-css/index.css">
More Vue Single File Component (SFC) specification introduction.
Native CSS variables
CSS variables are standard specifications defined by CSS authors.
CSS variables are also called CSS custom properties. The values it contains can be reused throughout the document. Examples are as follows:
/* :root 伪类代表 HTML 文档的根元素,是存放自定义属性的最佳位置。*/
:root {
/* --text-color 为自定义属性 */
--text-color: #000000;
}
p {
/* 使用时,需要使用 var() 函数并传入自定义属性。 */
color: var( --text-color );
font-size: 16px;
}
h1 {
color: var( --text-color );
font-size: 42px;
}
Define and use CSS custom properties
Before using CSS custom properties, we need to declare the custom properties first. The property name needs to --
), and the property value can be any valid CSS value. Like other attributes, custom attributes are also written in the rule set, as follows:
element {
--main-bg-color: brown;
}
specified by the 160d9262f22147 rule set defines the visible scope of the custom attribute. usual best practice for 160d9262f22148 is to define it under the root pseudo-class :root
so that it can be accessed anywhere in the HTML document.
:root {
--main-bg-color: brown;
}
Note: custom attribute name is case sensitive,--my-color
and--My-color
will be considered as two different custom attributes.
As mentioned earlier, when using a local variable, use var()
) function wrap to represent a legal attribute value:
element {
background-color: var(--main-bg-color);
}
:root {
--main-bg-color: brown;
}
.one {
color: white;
background-color: var(--main-bg-color);
margin: 10px;
width: 50px;
height: 50px;
display: inline-block;
}
For more information about the inheritance and alternate values of Using CSS custom properties (variables) .
SFC proposal
sfc-style-variables main overview pointed out that this proposal supports single-file component state-driven CSS variables injected into the single-file component style .
<template>
<div class="text">hello</div>
</template>
<script>
export default {
data() {
return {
color: 'red'
}
}
}
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
Why use state-driven CSS variables
Since the Vue SFC style provides simple CSS matching and encapsulation, it is purely static-which means that so far we have not been able to dynamically update the style according to the state of the component at runtime.
Now most modern browsers support the native CSS variable , we can use it to easily connect the state and style of the component.
About proposal design
In this proposal design (old version), Vue SFC Style supports vars binding, which accepts a key/values expression as CSS variable injection. It is calculated in the same context as the expression in <template>
The variable will be applied to the root element of the component as an inline style. In the above example, a given value {color:'red'}
of vars
binding, will be rendered HTML:
<div style="--color:red" class="text">hello</div>
In scoped
mode
When used in scoped
mode, you need to ensure that CSS variables will not leak to descendant components or accidentally shadow CSS variables to higher layers of the DOM tree. The applied CSS variables will be prefixed with the scope ID of the component:
<div style="--6b53742-color:red" class="text">hello</div>
Please note that when scoped and vars exist at the same time, all CSS variables are treated as local variables.
In this case, to use global CSS variables, you need to use the global:
prefix:
<style scoped vars="{ color }">
h1 {
color: var(--color);
font-size: var(--global:fontSize);
}
</style>
Disadvantages of the old proposal design
- Need to manually declare
vars
to disclose the variables that can be used. - There is no obvious visual cues that variables are injected and responded to.
- Different behaviors in
scoped/non-scoped
- In
non-scoped
mode, CSS variables will leak to child components. - In
scoped
mode, the use of ordinary CSS variables declared outside the component requires theglobal:
prefix. (Usually CSS variable usage is best kept the same inside and outside the component)
Improvements in the new version of the proposal
In order to solve the above problems, the improved usage of the new version is as follows:
<template>
<div class="text">hello</div>
</template>
<script>
export default {
data() {
return {
color: 'red',
font: {
size: '2em'
}
}
}
</script>
<style>
.text {
color: v-bind(color);
/* expressions (wrap in quotes) */
font-size: v-bind('font.size');
}
</style>
- There is no need to explicitly declare which properties are injected as CSS variables (
v-bind()
in CSS); - The visual difference of response variables is more obvious;
- The same behavior in
scoped/non-scoped
- Will not leak into sub-components;
- The use of ordinary CSS variables is not affected.
Use in Vue3
The example is not described in detail, please understand it in conjunction with the notes.
The example contains:
- Used the new
script setup
; - The definition of native CSS variables and the use of
var()
; v-bind
CSS variable injection.- At runtime, responsively change the CSS style;
- Recommended style.
<template>
<div class="root">
<span class="test" @click="changeColor"> Vue GoldenLayout</span>
<div>
</template>
// script setup
<script lang="ts" setup>
import { defineComponent, reactive } from "vue";
// 将 css 样式单独抽离
import { style } from "../styles/vlogo";
const css = reactive({ ...style });
// 点击,修改组件的样式
const changeColor = () => (css.color = "yellow");
</script>
<style>
.root {
// 原生 css 变量的定义
--custom-color: v-bind("css.background");
}
.test {
// 使用 v-bind 进行状态驱动
color: v-bind("css.color");
// 使用 var()
background: var(--custom-color);
}
</style>
The final rendering and the compiled HTML and CSS:
Note: When using CSS variable injection in vue3, the recommended style is to extract the CSS style separately from .
The principle behind variable injection
We learned in the above SFC Style brief introduction vue-loader
will parse the .vue
file and extract the language blocks, if necessary, process them through other loaders, and finally assemble them into an ES Module. Its default export is a Vue The object of .js component options.
If <style>
by the lang
property, other pretreatment CSS language ( less
, sass
), etc., can be matched build tool (webpack, vite) configured loader
for a particular process.
/*packages/compiler-sfc/sfc/stylePreprocessors.ts */
// .scss/.sass processor
const scss: StylePreprocessor = (source, map, options, load = require) => {...}
const sass: StylePreprocessor = (source, map, options, load) => {...}
// .less
const less: StylePreprocessor = (source, map, options, load = require) => {...}
// .styl
const styl: StylePreprocessor = (source, map, options, load = require) => {...}
Part of the compilation in Vue3 SFC Style is mainly done by postcss
, which corresponds to the doCompileStyle()
method packages/compiler-sfc/sfc/compileStyle.ts
Here, let's take a look at its <style>
, and the corresponding code (code omitted):
export function doCompileStyle(
options: SFCAsyncStyleCompileOptions
): SFCStyleCompileResults | Promise<SFCStyleCompileResults> {
const {
...
id,
...
} = options
...
const plugins = (postcssPlugins || []).slice()
plugins.unshift(cssVarsPlugin({ id: shortId, isProd }))
...
}
It can be seen in the use of postcss
compile <style>
will be added before cssVarsPlugin
plug-ins, and to cssVarsPlugin
incoming shortId
(ie scopedId
replace data-v
results within) and isProd
(whether in a production environment).
cssVarsPlugin
is used postcss
plug provided Declaration
method , to access <style>
values of all CSS properties declared, each visit to match a regular v-bind
content of the command, and then use replace()
the property value replacement method is var(--xxxx-xx)
, Behaved like this in the above example:
cssVarsPlugin
plug-in:
const cssVarRE = /\bv-bind\(\s*(?:'([^']+)'|"([^"]+)"|([^'"][^)]*))\s*\)/g
const cssVarsPlugin: PluginCreator<CssVarsPluginOptions> = opts => {
const { id, isProd } = opts!
return {
postcssPlugin: 'vue-sfc-vars',
Declaration(decl) {
// rewrite CSS variables
if (cssVarRE.test(decl.value)) {
decl.value = decl.value.replace(cssVarRE, (_, $1, $2, $3) => {
return `var(--${genVarName(id, $1 || $2 || $3, isProd)})`
})
}
}
}
}
Here the variable name of var()
(the content after --
genVarName()
method, which will generate different values isProd
to true
or false
function genVarName(id: string, raw: string, isProd: boolean): string {
if (isProd) {
return hash(id + raw)
} else {
return `${id}-${raw.replace(/([^\w-])/g, '_')}`
}
}
The above is just a partial interpretation of the related processing of the SFC Style block. For a more complete source code analysis, please refer to Vue 3's SFC Style CSS Variable Injection proposal behind .
Advantages of CSS variable injection
- Theme-Make theme changes through responsive global styles. (Refer to Naive UI).
- Compared with other CSS preprocessing languages (Less, Sass, etc.), it is free of installation and no need to configure
loader
. - Combined with responsive features, it can be well modularized without exporting CSS style files.
Finally, the point I want to complain about is that the change from Vue2 to Vue3 is a change in thinking. Vue 3 gives users too many choices and makes them at a loss.
reference:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。