03、另类中的异类 styled components

1,012 阅读7分钟

styled-components.png

版权声明:本文为博主原创文章,未经博主允许不得转载。

PS:转载请注明出处 作者: TigerChain 地址: www.jianshu.com/p/dd2a5b1b9… 本文出自 TigerChain 简书 ReactNative 系列

教程简介

  • 1、阅读对象

本篇教程适合新手阅读,老手直接略过

我们都知道在 RN 中使用的的内联样式,是通过 StyleSheet 组件创建出来的样式「当然也可以使用 const style={} 来定义」

对于 web 开发者来说,特别是使用 css 样式的人来说写样式的时候总是不自觉的要加一个 - ,那么你也可以使用样式组件--styled-components ,这们来重点介绍一下 styled-components

一、 什么是 styled-components

熟悉前端的童鞋都知道,html 中引入 css 有三种方式

  • 嵌入式
<head> 
 <style type="text/css"> 
   h3{color:red} 
   span{color:blue} 
 </style> 
<head> 
  • 内联式
<p style="color:#FFEECC;font-weight:bold;">内联样式</p> 

  • 外部引用入,有 link import 等

这里说一下 link

<link rel="stylesheet" type="text/css" href="theme.css" />

<html>
 指令语句
</html>

在 RN 中使用的也是内联样式,那有没有一个既有 web 开发中 css 书写特性,也能把 React 组件化思想结合在一起「有样式的组件」组件呢?styled-components 就能满足我们的需求

styled-components 则是一个把组件和 style 联系在一起的一个组件库,为 React 和 ReactNative 提供一个干净,易于使用的界面,说白了就是在 js 上写 css ,移除了组件和样式的映射关系。是 style 的另一种思想,简单的举个例子来直观的感受一下吧,如下:定义了一个带有样式的 Button ,怎么样帅吧

const Button = styled.Button`
	color:red;
   font-size:16;
	margin:1;
	padding:0.5,1;
    ...
`

PS:其中 styled.Button 是一个标签模版函数,紧跟在后面的 `...` 是一个模版字符串,模版函数和模版字符串都是 ES6 的语法,如果不清楚的可以查看 ES6 相关知识

二、styled-components 的使用

这里我们以 RN 举例子来说明,React 使用套路是一样的

1、在指定目录下创建一个 RN 项目

react-native init styleComponent

2、安装 styled-components

npm install --save styled-components 或 yarn add styled-components

我们看一下 package.json 中已经成功添加了 styled-components 并且版本是 2.2.0

dep_style_com.png

3、新建 app 目录,并且新建 main.js,并将 main.js 添加到时 index.android.js 和 index.ios.js 中

修改 index.android.js 和 index.ios.js 核心代码

modify_index.png

4、在 app 目录中新建 inlineStyle.js 和 StyleComponent.js 文件

  • 我们使用传统的 rn 样式方法来创建一个背景为红色,并且字为红色的按钮,如下:

inline_button.png

# inlineStyle.js

import React, { Component } from 'react';
import {
  View,
  Text,
  StyleSheet,
} from 'react-native';

/**
 * @author TigerChain
 * 普通的内联样式的组件
 * @type {Object}
 */
export default class InlineStyle extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.inlineText}>I'm the InlineStyle button</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    backgroundColor:'red',
    height:40,
    justifyContent:'center',
    alignItems:'center'
  },
  inlineText:{
    color:'yellow',
    fontSize:18
  }
});
  • 2、定义 StyleComponent.js
# StyleComponent.js

import React, { Component } from 'react';
import {
  View,
  Text,
  StyleSheet,
} from 'react-native';

import styled from 'styled-components/native'

/**
 * @author TigerChain
 * 使用 styled-components 声明组件
 * @type {Object}
 */

 const StyledView  = styled.View`
   background-color: red;
   height: 40;
   justify-content:center;
   align-items: center;
 `

const StyleText = styled.Text`
  color: yellow;
  font-size: 18;
`

const StyleComponent = (props)=>(
  <StyledView>
    <StyleText>styled-components 声明的 Button</StyleText>
  </StyledView >
)

module.exports = StyleComponent

style-components 格式

我们可以看到使用 styled-components 来编写样式组件的格式是

const CustomView = styled.XXX`
 
 样式

`

其中 XXX 就是 RN 中的组件,比如 Text,View,Button 等

PS:使用 styled-components 要先引入这个库 import styled from 'styled-components/native' 我们来对比一下两种方式吧

inline_vs_stylecom.png

把两个组件组合到 main.js 中

in_main.png

两种方式写的生成的一个相同的组件最后运行效果如下:

gen_button.png

经过以上代码,我们简单的了解了一下 styled-components 是个什么鸟,并且如何使用,下面我们来说一些高级用法吧

三、styled-components 的高级用法

1、传递属性「定制组件」

  • 1、我们先看一个 React 的例子吧「注意这里说的是 React 」

假如我们想要定义按钮的样式,分别为大、小,我们会这样做,新建一个 css 文件

css 文件

.button{
	background:red;
	border-radius: 8px;
	color: yellow;
}
.large{
	height:60px;
	width:180px;
}
.small{
	height:40px;
	width:100px;
}

js 文件

class CustomButton extends React.Component {
  render() {
    return (
      <div>
        <button className="small">按钮</button>
        <button className="large">按钮</button>
      </div>
    )
  }
}

我们想在 rn 使用 styled-components 实现以上类似的功能如何办呢?传递 props 就派上用场了,废话不多说,直接上代码

  • 2、使用 styled-components 完成上面的按钮组件

修改 styleComponent.js,并声明一个可指定大小的按钮组件

const CustomButton = styled.View`
  background-color: ${props => props.red?'red':blue};
  border-radius: 8;
  margin-top: 10;
  justify-content: center;
  align-items: center;
  height: ${props => props.small?40:60};
  width:${props => props.small?100:180}
`

然后引用即可

<CustomButton small red>
   <StyleText >TigerChain</StyleText>
</CustomButton>

pass_props.png

2、对任何组件进行样式化

我们项目中有时会用三方组件,我们想必变三方组件的样式,styles-components 也是完美支持的,看核心代码

// 定义可被任意样式的组件 熟悉 props 的都知道这里的 children是啥意思,style 就不用说了
const CustomText = ({style,children}) =>(
  //这里还可以是任意三方组件
  <Text style={style}> 
    {children}
  </Text>
)
// 给上面的组件添加样式
const StyledText2 = styled(CustomText)`
  color: palevioletred;
  font-weight: bold;
  font-size: 18;
`;

使用方式

  <CustomText>TigerChain</CustomText>
   <StyledText2>给 TigerChain 添加样式</StyledText2>

运行查看结果:

any_style.png

大体一个过程如下:

any_style_info.png

3、样式继承

  • 格式:
const CustomComponent = styled.Component`
	样式
`

const MyComponent = CustomComponent.extend`
	扩展样式或重写样式
	...
`
  • 在 styleComponent.js 中添加自定义样式扩展组件
//定义可继承的组件
const MyCusTomText = styled.Text`
  color: red;
  margin-top: 8;
`
//扩展组件
const ExtendText = MyCusTomText.extend`
  font-size:25;
  font-weight:bold;
`
  • 使用
  <MyCusTomText>自定义文本</MyCusTomText>
  <ExtendText>扩展文本</ExtendText>
  • 运行查看结果

exnted_style.png

4、附加 props 使用 .attrs 构造函数

在 .attrs 构造函数中,我们可以声明静态 props ,动态 props ,目的是为了避免传递不必要的属性

  • 格式:
const MyView = styled.View.attrs({
	...
	xxx:props => props.yyy
	...
})`
	xxx:${props => props.xxx}
`
  • 以上格式看起来比较模糊,我们通过代码来感受一下
// 使用 .atrr 来添加额外的 props

const AtrrView = styled.View.attrs({
  //声明一些 props 属性
  height:props => props.height || 40,
  background:props => props.bgcolor || 'red',

})`

border-radius:5;
height:${props => props.height}
background-color:${props => props.background}
justify-content:center;
align-items:center;

`

使用并查看结果

<AtrrView bgcolor='orange' height='80'>
   <Text>
     我是 .attrs 属性
   </Text>
</AtrrView>

结果如下:

attr_style.png

过程分析

attr_style_detail.png

总结:

经过以上我们对 styled-componets 有了一个大体的认识和了解,styled-components 还支持动画等,具体可以去官网查看,总结 styled-componentd 有以下特点

  • 是 css in js 的一种实现方式
  • 移除了样式和组件的映射关系
  • 使用组件看起来更清晰
  • 使样式组件和逻辑组件更好的区分开,使得两种组件解耦
  • 与传统单一职责相比,styled-components 就相当于富二代,与生俱来就可以写一个 style 的组件「传统的推崇 html css js 等都要分开」

这个玩意开始写的时候人很别扭「特别对于喜欢 js css html 分离的人」,但是写着写着就有一种巴黎欧莱雅的感觉---你值得拥有

三、性能对比

既然 styled-components 让组件拥有完整的功能,并且是目前前端变化的趋势,那么我们到底该不该使用 styled-components 呢?说到这里估计有人要骂笔者了「我靠,我都照着你说的的代码撸了一遍了,你居然这样说...」。我只想说年轻人,稍安勿躁

一个组件不光是好用就是我们要选择的理由,我们还要考虑性能、组件对包大小的影响,兼容性的影响等等,这里我们对比一下 styled-components 和传统的 in-line css 的性能

开始撸码

在上面 demo 的基本上添加代码,在这里我们要统计一代码的执行时间,所以安装一个依赖库 react-native-console-time-polyfill

yarn add react-native-console-time-polyfill
  • 1、在 app 目录中新建 StyledComponentsPerformance.js
/* @flow weak */

import React, { Component} from 'react';
import {
  View,
  Text,
  StyleSheet,
  ListView
} from 'react-native';

import styled from 'styled-components/native'
import 'react-native-console-time-polyfill'

import data from './Data'

const ListItemView = styled.View`
  padding-top: 5;
  padding-bottom: 5;
  border-bottom-width: 1;
  border-bottom-color: red;
`;

const ListItemText = styled.Text`
  color:red;
  font-size: 18;
`;

const ScrollViewStyled = styled.ScrollView`
`;

const ListViewStyled = styled.ListView`
`;

/**
 * 样式组件性能测试
 * @type {ListView}
 */
export default class StyleComponentPerformance extends Component {
  constructor(props) {
    super(props);
    const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
    this.state = {
      dataSource: ds.cloneWithRows(data),
    };
  }
  componentWillMount() {
    console.log(`ListView - 渲染 ${data.length} 个组件`);
    console.time('styled-components');
  }

  componentDidMount() {
    console.timeEnd('styled-components');
  }

  renderRow = (row) =>(
    <ListItemView><ListItemText>{row.name}</ListItemText></ListItemView>
  )

  renderListView() {
    return (
      <ListViewStyled
        dataSource={this.state.dataSource}
        renderRow={this.renderRow}
        enableEmptySections={true}
      />
    );
  }

  renderScrollView() {
    return (
      <ScrollViewStyled>
        {data.map((row, index) => <ListItemView key={index}><ListItemText>{row.name}</ListItemText></ListItemView>)}
      </ScrollViewStyled>
    );
  }

  render() {
    return this.renderListView();
    // return this.renderScrollView();
  }
}

  • 2、在 app 中目录中新建 InlineStylePerformance.js
/* @flow weak */

import React, { Component } from 'react';
import {
  View,
  Text,
  StyleSheet,
  ListView,
  ScrollView
} from 'react-native';

import 'react-native-console-time-polyfill'
import data from './Data'

/**
 * inLine style 性能测试
 * @type {ListView}
 */
export default class InlineStylePerformance extends Component {
  constructor(props) {
      super(props);
      const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
      this.state = {
        dataSource: ds.cloneWithRows(data),
      };
    }

    componentWillMount() {
      console.log(`ListView - 渲染 ${data.length} 个组件`);
      console.time('inline-style');
    }

    componentDidMount() {
      console.timeEnd('inline-style');
    }

    renderRow = (row) =>(
      <View style={styles.ListItemView}><Text style={styles.textStyle}>{row.name}</Text></View>
    )

    renderListView() {
      return (
        <ListView
          dataSource={this.state.dataSource}
          renderRow={this.renderRow}
          enableEmptySections={true}
        />
      );
    }

    renderScrollView() {
      return (
        <ScrollView>
          {data.map((row, index) => (
            <View style={styles.ListItemView} key={index}><Text style={styles.textStyle}>{row.name}</Text></View>
          ))}
        </ScrollView>
      );
    }

    render() {
      return this.renderListView();
      // return this.renderScrollView();
    }
}

const styles = StyleSheet.create({
  ListItemView: {
  paddingTop: 5,
  paddingBottom: 5,
  borderBottomWidth: 1,
  borderBottomColor: 'red',
},
textStyle:{
  fontSize:18,
  color:'red',
}

});

  • 3、新建 Data.js
const data = []
for(let i=0;i<2000;i++){
  data.push({name:`测试 === ${i}`})
}

export default data
  • 4、测试,在 main.js 引入即可

test_perfor.png

在这里我们在模拟器或是真机上开启远程 debug 调试,然后打开 chrome 调试就可以看到 console 打出的 log 了

romote_debug.png

打开 chrome 调试工具

show_log.png

经过五次对比,得出以下结果

inline_vs_style-components.png

结论

  • 由于我们是开启远程调试工具,所以和网络也有关系,得出的结论不一定是最准确的
  • 但是也说明了一些问题 styled-components 目前的版本来说「2.2.0」性还是比 in-line 样式的性能稍有点差,
  • styled-components 完全可以使用在项目中,并且相信以后的版本会逐渐缩小和 in-line 样式的性能的微小差距

到此为止,我们就介绍完了 styled-components 的用法和性能比较,快上手试试吧

最近开了公号,以后文章内容第一时间会发在公号中,希望大家关注,扫描以下二维码即可关注

据说每一个勤奋努力想成为非常牛 B 的人都会点个喜欢或转发的