Implementation of Vue3 style CSS variable injection

Implementation of Vue3 style CSS variable injection

summary

Support for using component state-driven CSS variables (CSS custom properties) in single-file component styles.

Basic Example

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

motivation

Vue SFC styles provide straightforward CSS collocation and encapsulation, but it is purely static - meaning that, as of now, we have no ability to dynamically update styles at runtime based on the state of the component.

Now, with most modern browsers supporting native CSS variables, we can leverage it to easily connect component state and styles.

Design Details

Tags in SFC now support a custom CSS function v-bind:

<!-- in Vue SFC -->
<style>
  .text {
    color: v-bind (color);
  }
</style>

As expected, this will bind the declared value to the property color in the component state, reactively.color

The function can support arbitrary JavaScript expressions, but because JavaScript expressions may contain invalid characters in CSS identifiers, they need to be wrapped in quotes in most cases: v-bind

.text {
  font-size: v-bind ('theme.font.size');
}

When such a CSS variable is detected, the SFC compiler will do the following:

Override to a native one with a hashed variable name. The above content will be rewritten as: v-bind () var ()

.text {
  color: var (--6b53742-color);
  font-size: var (--6b53742-theme_font_size);
}

Note that the hash will be applied in all cases, regardless of whether the tag has a scope or not. This means that injected CSS variables cannot accidentally leak into child components.

The corresponding variables will be injected into the root element of the component as inline styles. For the example above, the final rendered DOM will look like this:

<div style="--6b53742-color:red;--6b53742-theme_font_size:2em;" class="text">
  hello
</div>

Injection is responsive - so if a component's properties change, the injected CSS variables will be updated accordingly. This update is independent of the component's template update, so changes to a pure CSS responsive property will not trigger a re-render of the template.

Compilation details

To inject CSS variables, the compiler needs to generate and inject the following code into the component's setup():

import { useCssVars } from 'vue'

export default {
  setup() {
    //...
    useCssVars(_ctx => ({
      color: _ctx.color,
      theme_font_size: _ctx.theme.font.size,
    }))
  },
}

... Here, the runtime helper sets up a DOM.useCssVars watchEffect that applies the variable responsively.

This compilation strategy requires that when compiling a script, a simple re-encoding analysis is first performed on the tag content to determine the list of variables to be exposed. However, this parsing phase does not incur as much overhead as the full AST-based parsing of <style>.

In production, variable names can be further hashed to reduce CSS footprint.

.text {
  color: var (--x3b2fs2);
  font-size: var (--29fh29g);
}

The corresponding generated JavaScript code will use the same hash value accordingly.

Adoption strategy

This is a new feature that is fully backwards compatible. However, we should make it clear that it relies on native CSS variables, so users need to be aware of the range of browser support.

practice

Declare two responsive properties in the script, namely wallpaperBlur and wallpaperMask. wallpaperBlur indicates the blur level of the wallpaper, and wallpaperMask indicates the transparency of the mask. Applying them to the style via v-bind means that when we change these two values ​​in the script, the style will respond to the change.

// script
const wallpaperBlur = ref('0px')
const wallpaperMask = ref('rgba(0, 0, 0, 0)')
//style
.wallpaper {
  filter: blur(v-bind(wallpaperBlur));
  bottom: calc(v-bind(wallpaperBlur) * -2);
  left: calc(v-bind(wallpaperBlur) * -2);
  right: calc(v-bind(wallpaperBlur) * -2);
  top: calc(v-bind(wallpaperBlur) * -2);
  .wallpaper-image {
    transition: background-image 0.6s, background-color 0.4s;
  }
  .wallpaper-mask {
    background-color: v-bind(wallpaperMask);
  }
}

hint

Bind the appropriate properties

In the example above, you might think that changing the transparency of the mask only requires declaring a number between 0 and 1, and then writing it in the style like this:

.wallpaper-mask {
  background-color: rgba(0, 0, 0, v-bind(wallpaperMask));
}

As mentioned above, v-bind in style will be rewritten as CSS variables during the compilation phase. The above code will be rewritten as follows:

.wallpaper-mask {
  background-color: rgba(0, 0, 0, var (--[hash]-wallpaper_mask));
}

rgba(0, 0, 0, var (--[hash]-wallpaper_mask)) is not parseable in CSS. So that's why the initial value of wallpaperMask is declared as rgba(0, 0, 0, 0). This is a very important point to note, and there are many similar situations in CSS.

Note the update of style

The corresponding variables mentioned in the design details will be injected into the root element of the component as inline styles. The final rendered DOM will look like this:

<div style="--6b53742-color:red;--6b53742-theme_font_size:2em;"></div>

When you change the properties bound in <style> in <script>, the CSS variables in the inline style will respond to the changes. However, you cannot update a single CSS variable in an inline style, which means that updating any "dynamic style" in a component will cause all inline styles in the root component to be updated. When the value of the style attribute contains a large number of CSS variables, you need to consider reorganizing your component. Because the compiled CSS variables will be injected into the root element of the component as inline styles, we cannot control this behavior and decouple a CSS variable that causes an update from other CSS variables.

Imagine a situation where the compiled CSS variables in style contain a CSS variable whose value is a huge base64. When other CSS variables in the component are updated, the entire style will be updated, which will incur additional hardware overhead. We need to separate the component that generates the base64 CSS variable so that the CSS variable can be injected into the root element of the component and will not be affected by updates to other CSS variables.

References

https://github.com/vuejs/rfcs/pull/231

This is the end of this article about the implementation of Vue3 style CSS variable injection. For more relevant Vue3 style CSS variable injection content, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Detailed analysis of the blocking problem of js and css
  • Take you to understand the basics of CSS, style
  • JS, CSS and HTML to implement the registration page
  • Vue3 implements CSS infinite seamless scrolling effect
  • React introduces antd-mobile+postcss to build mobile terminal
  • A brief discussion on the generation scheme of CSS irregular borders
  • CSS Glitch Art Explained
  • CSS3 uses transition animation and easing effect case study

<<:  Detailed instructions for installing mysql5.7 database under centos7.2

>>:  How to permanently change the host name in Linux

Recommend

Vue interpretation of responsive principle source code analysis

Table of contents initialization initState() init...

Nginx monitoring issues under Linux

nginx installation Ensure that the virtual machin...

How to simply encapsulate axios in vue

Inject axios into Vue import axios from 'axio...

Customization Method of Linux Peripheral File System

Preface Generally speaking, when we talk about Li...

Vue implements Dialog encapsulation

Table of contents Vue2 Writing Vue3 plugin versio...

How a select statement is executed in MySQL

Table of contents 1. Analyzing MySQL from a macro...

Stop using absolute equality operators everywhere in JS

Table of contents Overview 1. Test for null value...

XHTML 1.0 Reference

Arrange by functionNN : Indicates which earlier ve...

How to build php+nginx+swoole+mysql+redis environment with docker

Operating system: Alibaba Cloud ESC instance cent...

How to remove the dotted border when clicking a link in FireFox

I encountered several browser compatibility issue...

Javascript File and Blob Detailed Explanation

Table of contents File() grammar parameter Exampl...

Linux uses suid vim.basic file to achieve privilege escalation

Reproduce on Kali First set suid permissions for ...