为什么在vue中style-components没有火起来?
再刚使用
react
的styled-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-components
在react
中的用法
示例一:(使用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-components
为react
开发中解决了以下几个问题:
-
组件化的样式管理:使用 styled-components,样式与组件紧密集成在一起,每个组件都可以拥有自己的独立样式。这种方式使得开发者可以更轻松地维护和重用样式,而无需关心全局 CSS 类名的命名冲突和样式覆盖的问题。
-
动态和响应式样式:styled-components 提供了一种方便的方式来根据组件的状态、属性或上下文动态生成样式。
-
CSS-in-JS 的好处:styled-components 将 CSS 和组件逻辑紧密结合在一起,将样式定义直接编写在组件代码中。这种方式提供了更强的可读性和可维护性,同时也允许开发者利用 JavaScript 的能力来进行条件渲染和复杂样式逻辑的处理。
-
自动前缀和全局样式隔离:styled-components 内置了自动前缀功能,根据浏览器要求自动生成所需的 CSS 前缀。此外,由于其采用了全局样式隔离的方式,避免了全局 CSS 污染和样式冲突的问题。
-
更好的开发者体验:styled-components 提供了直观、简洁的 API,使得编写和管理样式变得更加容易和愉快。它还通过标签模板字符串的方式提供了丰富的语法糖,使样式的编写更具可读性和可扩展性。
以上就是在
react
中styled-components
基础应用场景。如果你对该组件还有其他用法可以打在评论区供大家学习。
react
中styled-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
其实在看完
react
的styled-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-components
在react
解决了什么问题的角度去考虑我觉得最大的有点就是样式隔离 ,但是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-components
中Css In JS
的思想应该通过以上学习有了一定的了解,你还能从别的方面分析**为什么在vue中style-components没有火起来?**可以在评论区留言,如果对您有帮助欢迎点赞收藏。
参考文章;