为什么在vue中style-components没有火起来?

为什么在vue中style-components没有火起来?

再刚使用reactstyled-components的时候,我是震惊的,震惊的不是该组件有多好用,震惊的是``模板字符串语法竟然可以这么用。 我当时还质疑说,都是阮一峰的《ES6标准入门》中没有说这个语法的锅,我都看了好几篇了指定没有,没发现啊, 最后回家再翻开真的是啪啪打脸。咱们一起回顾下这个模板字符串中的标签语法

它可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为标签模板 功能(tagged template)。如下:

示例一:

js 复制代码
alert`hello`
// 等同于
alert(['hello'])

示例二:

如果模板字符里面有变量,就不是简单的调用了,而是会将模板字符串先处理成多个参数,再调用函数

js 复制代码
let a = 5;
let b = 10;
tag`Hello ${ a + b } world ${ a * b }`;
// 等同于
tag(['Hello ', ' world ', ''], 15, 50);

function tag(stringArr, ...values) {
  // ...
}

这么看是不是就比较清晰了,大致就是立即调用该方法(alert示例一),然后把模板语法再没变量 的时候整体作为数组字符串 传进该方法(alert示例一)。 存在变量的情况下立即调用该方法(tag示例二),会以变量为切割第一个数组是切割后的多搁字符串,切割出来的变量依次当作参数传进该方法中(tag示例二)。

在react 中使用styled-components

简单输出下styled-componentsreact中的用法

示例一:(使用html标签当作组件使用)

js 复制代码
import styled from 'styled-components'

// div 标签
const Container = styled.div` 
 display: flex;
 justify-content: space-between;
`;

// span 标签
const CustomSpan = styled.span`
 color: red;
`;

export const Header = () => { 
  return (
    <Container>
      <CustomSpan>text</CustomSpan>
    </Container>
  ) 
}

示例二:(还可以对子元素经行样式修改)

js 复制代码
import styled from 'styled-components'

// div 标签
const Container = styled.div` 
 display: flex;
 justify-content: space-between;

 > span {
   color: red;
 }
 ...
`;
export const Header = () => { 
  return (
    <Container>
      <span>text</span>
    </Container>
  ) 
}

示例三:(使用变量)这里是可以使用props 传进来的

js 复制代码
const Container = styled.div`
  height: 60px;
  display: flex;
  margin: 8px 8px 0;
  justify-content: space-between;
  background-color: ${(p) => p.titleBackground};
  border-bottom: 1px solid ${(p) => p.borderColor};
`

export const Header = () => { 
  return (
    <Container borderColor="#fff" titleBackground="red">
      <span>text</span>
    </Container>
  ) 
}

使用ts的情况下可以使用泛型约束如下:

tsx 复制代码
const Container = styled.div<{
  borderColor: string;
  titleBackground: string;
}>
`
  height: 60px;
  display: flex;
  margin: 8px 8px 0;
  justify-content: space-between;
  background-color: ${(p) => p.titleBackground};
  border-bottom: 1px solid ${(p) => p.borderColor};
`

export const Header = () => { 
  return (
    <Container borderColor="#fff" titleBackground="red">
      <span>text</span>
    </Container>
  ) 
}

示例四:可以使用传参判断是否需要该样式使用css``方法得到值

tsx 复制代码
import styled, { css } from 'styled-components'

const Button = styled.button<{ $primary?: boolean; }>`
  background: transparent;
  border-radius: 3px;
  border: 2px solid #BF4F74;
  color: #BF4F74;
  margin: 0.5em 1em;
  padding: 0.25em 1em;

  ${props => props.$primary && css`
    background: #BF4F74;
    color: white;
  `}
`;

const Container = styled.div`
  text-align: center;
`

render(
  <Container>
    <Button>Normal Button</Button>
    <Button $primary>Primary Button</Button>
  </Container>
);

styled-componentsreact开发中解决了以下几个问题:

  1. 组件化的样式管理:使用 styled-components,样式与组件紧密集成在一起,每个组件都可以拥有自己的独立样式。这种方式使得开发者可以更轻松地维护和重用样式,而无需关心全局 CSS 类名的命名冲突和样式覆盖的问题。

  2. 动态和响应式样式:styled-components 提供了一种方便的方式来根据组件的状态、属性或上下文动态生成样式。

  3. CSS-in-JS 的好处:styled-components 将 CSS 和组件逻辑紧密结合在一起,将样式定义直接编写在组件代码中。这种方式提供了更强的可读性和可维护性,同时也允许开发者利用 JavaScript 的能力来进行条件渲染和复杂样式逻辑的处理。

  4. 自动前缀和全局样式隔离:styled-components 内置了自动前缀功能,根据浏览器要求自动生成所需的 CSS 前缀。此外,由于其采用了全局样式隔离的方式,避免了全局 CSS 污染和样式冲突的问题。

  5. 更好的开发者体验:styled-components 提供了直观、简洁的 API,使得编写和管理样式变得更加容易和愉快。它还通过标签模板字符串的方式提供了丰富的语法糖,使样式的编写更具可读性和可扩展性。

以上就是在reactstyled-components基础应用场景。如果你对该组件还有其他用法可以打在评论区供大家学习。

reactstyled-components实现原理

这个styled-components是怎么实现让返回值像组件一样使用呢?我就看源码找资料如下:

简单重构下styled-components:

js 复制代码
const StyledExt = (TargetComponent, style) => class extends React.Component {
  componentDidMount() {
    this.element.setAttribute('style', style);
  }

  render() {
    return (
      <TargetComponent {...this.props} ref={element => this.element = element } />
    );
  }
};

let Styled = (TargetComponent) => ([style]) => StyledExt(TargetComponent, style);
styled.button = ([style]) => StyledExt('button', style);

const Button1 = Styled('button')`
  color: red;
`;
const Button2 = Styled.button`
  color: red;
`;

上面就很清晰了哈以上就是styled-components的简易版本核心代码,像解析变量方法咱们当然也是继续处理就好了如下:

js 复制代码
const StyledExt = (TargetComponent, strs, args) => class extends React.Component {
  initStyle() {
    const style = args.reduce((pre, cur, index) => {
      const isFunc = typeof cur === 'function'; // 判断是不是函数
      const value = isFunc ? cur(this.props) : cur;
      return pre + value + strs[index + 1];
    }, strs[0]);

    this.element.setAttribute('style', style);
  }

  componentDidMount() {
    this.initStyle();
  }

  componentDidUpdate() {
    this.initStyle();
  }

  render() {
    return (
      <TargetComponent {...this.props} ref={element => this.element = element } />
    );
  }
};

let Styled = (TargetComponent) => (strs, ...args) => StyledExt(TargetComponent, strs, args);
styled.button = (strs, ...args) => StyledExt('button', strs, args);

const Button1 = Styled('button')`
  color: ${(p) => p.color};
`;
const Button2 = Styled.button`
  color: ${(p) => p.color};
`;

以上可以作为styled-components的实现核心, 方便理解。

vue中使用styled-components

其实在看完reactstyled-components后,我就在想怎么能让vue也能用的上这个组件呢? 下班再地铁上想了一路也没想到要怎么实现,哈哈。 我就再网上查了下发现已经有vue-styled-components ,star:1.3k。 那么咱们下面看下再vue中是怎么实现的呢?

我直接把官方代码拿来了,熟悉vue语法的应该也一眼就能看懂了如下:

js 复制代码
  const createStyledComponent = (target, rules, props, options) => {
    const {
      attrs = []
    } = options
    const componentStyle = new ComponentStyle(rules)

    // handle array-declaration props
    const currentProps = normalizeProps(props)
    const prevProps = normalizeProps(target.props)

    const StyledComponent = {
      inject: {
        $theme: {
          default: function () {
            return () => ({ })
          }
        }
      },
      props: {
        as: [String, Object],
        value: null,
        ...currentProps,
        ...prevProps
      },
      data () {
        return {
          localValue: this.value
        }
      },
      render (createElement) {
        const children = []
        for (const slot in this.$slots) {
          if (slot === 'default') {
            children.push(this.$slots[slot])
          } else {
            children.push(createElement('template', { slot }, this.$slots[slot]))
          }
        }

        return createElement(
          // Check if target is StyledComponent to preserve inner component styles for composition
          isVueComponent(target) ? target : this.$props.as || target,
          {
            class: [this.generatedClassName],
            props: this.$props,
            domProps: {
              ...this.attrs,
              value: this.localValue
            },
            on: {
              ...this.$listeners,
              input: event => {
                if (event && event.target) {
                  this.localValue = event.target.value
                }
              }
            },
            scopedSlots: this.$scopedSlots
          },
          children
        )
      },
      methods: {
        generateAndInjectStyles (componentProps) {
          return componentStyle.generateAndInjectStyles(componentProps)
        }
      },
      computed: {
        generatedClassName () {
          const { context, attrs } = this
          const componentProps = { ...context, ...attrs }
          return this.generateAndInjectStyles(componentProps)
        },
        theme () {
          return this.$theme()
        },
        context () {
          return {
            theme: this.theme,
            ...this.$props
          }
        },
        attrs () {
          const resolvedAttrs = {}
          const { context } = this

          attrs.forEach((attrDef) => {
            let resolvedAttrDef = attrDef

            if (typeof resolvedAttrDef === 'function') {
              resolvedAttrDef = resolvedAttrDef(context)
            }

            for (const key in resolvedAttrDef) {
              context[key] = resolvedAttrs[key] = resolvedAttrDef[key]
            }
          })

          return resolvedAttrs
        }
      },
      watch: {
        value (newValue) {
          this.localValue = newValue
        },
        localValue () {
          this.$emit('input', this.localValue)
        }
      },
      extend (cssRules, ...interpolations) {
        const extendedRules = css(cssRules, ...interpolations)
        return createStyledComponent(target, rules.concat(extendedRules), props, options)
      },
      withComponent (newTarget) {
        return createStyledComponent(newTarget, rules, props, options)
      }
    }

    return StyledComponent
  }

大致就是利用vue render语法返回一个vue的模板对象。当然实现起来还有好多细节需要考虑,咱们就了解到基础实现就可以了。

为什么在vue中style-components没有火起来

其实咱们可以从style-componentsreact解决了什么问题的角度去考虑我觉得最大的有点就是样式隔离 ,但是vue中是支持使用<style scoped></style> scoped关键字做样式隔离的。

像动态样式也可以使用以下方式解决

vue 复制代码
<template>
  <div :style="computedStyle">123</div>
</template>

<script>
export default {
  name: 'Test',

  data() {
    return {
      color: '#fff',
    };
  },

  computed: {
    computedStyle() {
      return `background: ${this.color}`
    },
  },
}
</script>

还有像vue3语法支持v-bind()语法后感觉就更用不上style-components了,如下:

vue 复制代码
<script setup>
const theme = {
  color: 'red'
}
</script>

<template>
  <p>hello</p>
</template>

<style scoped>
p {
  color: v-bind('theme.color');
}
</style>

其实style-components我看网上也有说其性能等问题,我觉得就是还是按需要决定要不要使用吧,他确实解决了很多问题是个很好的东西不可否认,假如你对界面性能有极致的需求可以放弃使用,正常的功能还是应用下更方便。

结语

style-componentsCss In JS的思想应该通过以上学习有了一定的了解,你还能从别的方面分析**为什么在vue中style-components没有火起来?**可以在评论区留言,如果对您有帮助欢迎点赞收藏。

参考文章;

相关推荐
一只小风华~13 分钟前
Web前端开发:CSS Float(浮动)与 Positioning(定位)
前端·css·html·html5·web
前端张三15 分钟前
vue3中ref在js中为什么需要.value才能获取/修改值?
前端·javascript·vue.js
moyu8417 分钟前
前端从后端获取数据的流程与指南
前端
爱的叹息35 分钟前
解决 Dart Sass 的旧 JS API 弃用警告 的详细步骤和解决方案
javascript·rust·sass
涛哥码咖35 分钟前
Rule.resourceQuery(通过路径参数指定loader匹配规则)
前端·webpack
夕水1 小时前
这个提升效率宝藏级工具一定要收藏使用
前端·javascript·trae
会飞的鱼先生1 小时前
vue3 内置组件KeepAlive的使用
前端·javascript·vue.js
苹果酱05672 小时前
【Azure Redis 缓存】在Azure Redis中,如何限制只允许Azure App Service访问?
java·vue.js·spring boot·mysql·课程设计
斯~内克2 小时前
前端浏览器窗口交互完全指南:从基础操作到高级控制
前端
Mike_jia2 小时前
Memos:知识工作者的理想开源笔记系统
前端