What is CSS-in-JS?

As the name implies, csS-in-JS means you can use JS to write CSS styles, so why use JS to write CSS? I write CSS well, why do I have to make myself feel uncomfortable? You’ve heard the term separation of concerns before, and if you haven’t, you’ve probably heard at least one sentence: keep HTML, CSS, and JS separate, don’t couple them together, don’t write inline styles, inline scripts, etc., like 👇

<p style="line-height: 20px" onclick="console.log('styled-components')">
    CSS-in-JS
</p>
Copy the code

However, React broke this principle. All in JS is its classic development concept. Although it violates the principle of “separation of concerns”, it is beneficial to the isolation of components, making them highly decouples and highly reusable.

Why are similar libraries rarely seen in Vue

I believe that those who have experience in Vue development know that this is the way to write components 👇

<template>
    <h1>Vue</h1>
</template>

<script>
export default {
    name: 'vue'
}
</script>

<style scoped>
h1 {
    color: # 999;
}
</style>
Copy the code

CSS is perfectly componentized directly. Unlike React, where the components are separate from the CSS file, the components are integrated directly into one file, and you don’t need all the bells and whistles. However, there are also some disadvantages, such as CSS can not accept the JS passvalue 👇

<template>
    <div></div>
</template>

<script>
import img from '@/assets/img.png'

export default {
    name: 'vue',
    data () {
        return {
            img
        }
    }
}
</script>

<style scoped>
div {
    width: 100vw;
    height: 100vh;
}
</style>
Copy the code

What if I want a div to have a background image? What should I say in the

<template>
    <div :style="{background: `url(${img})`}"></div>
</template>

<script>
import img from '@/assets/img.png'

export default {
    name: 'vue',
    data () {
        return {
            img
        }
    }
}
</script>

<style scoped>
div {
    width: 100vw;
    height: 100vh;
}
</style>
Copy the code

However, this will compile into inline styles embedded in div tags, which is not easy to maintain.

Also, there are many styles that are actually common, such as Flex 👇

<template>
    <div></div>
</template>

<script>
export default {
    name: 'vue'
}
</script>

<style scoped>
div {
    display: flex;
    align-items: center;
    justify-content: center;
}
</style>
Copy the code

It’s not only tedious, it’s not easy to maintain, and the most important thing is that every component using this style will generate a div[data-v-xxx] style, which is a huge amount of code redundancy, will greatly increase the size of your CSS file, and slow down your project.

You might be smart enough to think: why don’t I just define a global CSS style? Add a class wherever it is needed. Just like bootstrap, add the class name to the tag.

Yes, this is a good solution, but remember that when you use bootstrap, you don’t just add the class name. You also need to install the DOM structure that is officially defined to write your <template>, or you need to copy and paste it. The most important thing is that there is no way to pass parameters. Although flex is a common style, not everything requires center alignment, and if you need to dynamically change align-items and justify content, you have to use the stupid method of writing all the classes with possible values.

You might say, well, why don’t I just write a common component 👇👇👇

<template>
    <div :style="{alignItems: align, justifyContent: justify}">
        <slot></slot>
    </div>
</template>

<script>
export default {
    name: 'vue'.props: {
        align: {
            type: String.default: 'center'
        },
        justify: {
            type: String.default: 'center'}}},</script>

<style scoped>
div {
    display: flex;
}
</style>
Copy the code

But this brings up the question, what if I need a component to look like this? Doesn’t it have to be wrapped in div, so you say, “Isn’t that easy?” Why don’t you just write dynamic components?

<template>
    <component :style="{alignItems: align, justifyContent: justify}" :is="dom">
        <slot></slot>
    </component>
</template>

<script>
export default {
    name: 'vue'.props: {
        align: {
            type: String.default: 'center'
        },
        justify: {
            type: String.default: 'center'
        },
        dom: {
            type: String.default: 'div'}}}</script>

<style scoped>
div {
    display: flex;
}
</style>
Copy the code

What if I want a bunch of components to share a style? For example, with nested writing like element-UI, rather than a Low level approach like global CSS, you might say: No problem, I’ll encapsulate it for you 👇

// Root.vue
<template>
    <div>
        <slot></slot>
    </div>
</template>

<script>
export default {
    name: 'root'.provide: {
        color: 'yellow'}}</script>
Copy the code
// Child1.vue
<template>
  <h1 :style="{color}">
    <slot></slot>
  </h1>
</template>

<script>
export default {
    name: 'child1'.inject: {
        color: {
            from: 'color'.default: 'blue'}}}</script>
Copy the code
// Child2.vue
<template>
  <h2 :style="{color}">
    <slot></slot>
  </h2>
</template>

<script>
export default {
    name: 'child2'.inject: {
        color: {
            from: 'color'.default: 'green'}}}</script>
Copy the code

This is all you need to use it:

// App.vue
<template>
  <root>
    <child1>Child1</child1>
    <child2>Child2</child2>
  </root>
</template>

<script>
import Root from '@/components/Root'
import Child1 from '@/components/Child1'
import Child2 from '@/components/Child2'

export default {
  name: 'app'.components: {
    Root,
    Child1,
    Child2
  }
}
</script>
Copy the code

If you don’t see this color, just change the color: ‘yellow’ in the provide in Root to: ‘gray’ and refresh the page 👇

This truly implements shared styles between components! But don’t you think it’s a hassle? Not only more than a layer of unnecessary DOM structure, and just for a style reuse and style componentization, it took so much effort, even some of the foundation is not very solid partners see here are faint, in fact, there has long been a packaged library on the market, we are repeating the wheel. For example, if I want to inherit the style, which you can do with a preprocessor like Sass or Less or Stylus, what if I want to inherit the style of the default tag and extend it? I want to inherit the default style of the A tag (underline, click to turn red, click to turn purple, etc.) and add some font size, line advanced style, and just write it by hand. Next, I will introduce a famous CSS-in-JS library, which is excellent in composition, mature and stable, and popular in the React ecosystem but unknown in the Vue ecosystem: Styled – Components!

vue-styled-components

As the name suggests, Softing-Components are styled components. Since softing-Components are a library tailor-made for React, they are not suitable for use in the Vue project. I know you must start to curse when you see this: I watched it with great interest. As a result, you told me that Styled – Components are not suitable for the Vue project? Don’t worry. Although The Styled – Components cannot be used in the Vue project, the Styled – Components team specially created a Vue-Styled – Components for Vue. Very similar to React’s Softing-Components usage, we will start with a simplest example, starting with installation:

npm i -S vue-styled-components

or

yarn add vue-styled-components

Then, when writing components, you can no longer write xxx.vue, but instead xxx.js. The simplest way to think about it is that it is a minimalist component, with no <template> or <script>, and only focuses on styling. It is written as 👇

// Flex.js
import styled from 'vue-styled-components'

const Flex = styled.div` display: flex; align-items: center; justify-content: center; `

export {
    Flex
}
Copy the code

Then you can write any CSS style you want in your string template. Not only does it work exactly like CSS, but it also supports nested syntax like Sass, Less, and Stylus:

// Flex.js
import styled from 'vue-styled-components'

const Flex = styled.div` display: flex; align-items: center; justify-content: center; &:hover { background: gray; } > :first-child { align-self: flex-start } > :last-child { align-self: flex-start } `

export {
    Flex
}
Copy the code

Use the xxx.js component in the same way that you normally use the xxx.vue component:

// App.vue
<template>
  <flex>
    <p>styled</p>
    <span>components</span>
  </flex>
</template>

<script>
import { Flex } from '@/components/Flex'

export default {
    name: 'child1'.components: {
        Flex
    }
}
</script>
Copy the code

As you can see, we do not define <slot>, but it can be inserted into the normal position, it will automatically find the default position to insert, because each tag is a component, there is no more complex DOM nested structure. Let’s see how to pass parameters to CSS:

// Ok.js
import styled from 'vue-styled-components'

const Ok = styled('div', {
  bg: {
    type: String.default: '#eee'}})`
    width: 100px;
    height: 100px;
    background: ${ props => props.bg }
`

export { Ok }
Copy the code

Instead of following a dot and string template directly, the dots are replaced by parentheses. The first parameter, as styled, is a string, representing what you want in the DOM tag. The second parameter is exactly the same as the props you wrote in the Vue component.

// Ok.js
import styled from 'vue-styled-components'

const Ok = styled('div'['bg'])`
  width: 100px;
  height: 100px;
  background: ${ props => props.bg }
`

export { Ok }
Copy the code

The first argument to the function is the set of properties that you define. After that definition, let’s see how to use it:

// App.vue
<template>
  <ok bg="# 333"/>
</template>

<script>
import { Ok } from '@/components/Ok'

export default {
    name: 'app'.components: { Ok }
}
</script>
Copy the code

You can see that the value we passed in works perfectly, and we can write any expression inside the function:

// Ok.js
import styled from 'vue-styled-components'

const Ok = styled('div'['bg'])`
  width: 100px;
  height: 100px;
  background: ${props => {
    alert(Awesome!)
    return props.bg ? 'green' : 'blue'
  }}
`

export { Ok }
Copy the code

You can see that the logic of the function you wrote runs, and it provides a shared data writing method similar to the Element-UI:

import {ThemeProvider} from 'vue-styled-components'

new Vue({
// ...
components: {
  'theme-provider': ThemeProvider
},
// ...
})
Copy the code

Use <theme-provider> as the root component:

<theme-provider :theme="{ primary: 'black' }">
<wrapper>
  // ...
</wrapper>
</theme-provider>
Copy the code

The child component needs to receive the following data:

const Wrapper = styled.default.section`
    padding: 4em;
    background: ${props => props.theme.primary};
 `
Copy the code

Here’s another example of inheritance:

import StyledButton from './StyledButton'

const TomatoButton = StyledButton.extend` color: tomato; border-color: tomato; `

export default TomatoButton
Copy the code

This makes it perfectly possible to extend and reuse styles, and even extend native tags:

const Button = styled.button` background: green; color: white; `
const Link = Button.withComponent('a')
Copy the code

Contrast with Vue native components

Ue – Styled – Components will generate a random class name after compilation:

The Vue native component writes a style that generates a random attribute of data-V-xxx:

In this way, there will be a large gap in the efficiency of the selectors. For the Wue-Styled -components, there is only one selector, while the original component is the combination of two selectors. Moreover, the efficiency of the attribute selector is relatively low, so the gap is opened invisibly.

The only way native components can pass values to the CSS is by:

Instead of appearing on the label after passing values, the attributes are embedded directly into the CSS:

Moreover, due to the style of JS, Vue-Styled – Components can write JS code, which is very convenient. For more interesting features, please styled- Components

Go get this thing and refactor your project!

Previous excellent article

  • Microsoft launches comments section on GitHub
  • Vue 3.0.3: New CSS variable passing and the latest Ref Proposal
  • “Don’t underestimate the nine grid, one question can let a candidate reveal his true colors!”
  • “Mobile Layout Interview Questions to test your CSS Skills (Center)”
  • A series of confusing behaviors after setting prototype Objects as Proxies
  • Vue’s Super Fun New Feature: DOM Portal
  • A fun new feature of Vue: Introducing JS variables into CSS
  • Create your own Visual Data Map without any libraries
  • Is It Finally Vue’s Turn to Inspire React?
  • A Small Pit in Vue3 on IOS
  • Upgrade your React project to Immer instead of Immutable by 2020
  • “Soul Interrogation from the Author of React Hooks and Immutable”
  • Good news, Vue3 official document is available in Chinese!
  • Hooks use of the New VUe-Router
  • Vue 3:20 Years of Status Updates
  • React 17 is officially a transition version!
  • Yu Yuxi: The Design Process of Vue3
  • The Father of Node’s refactoring Deno is finally released. Will it Replace Node after all?
  • The Vue3 beta was released early this morning and openly supports scaffolding!