03、另类中的异类 styled-components

styled-components.png

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

PS:转载请注明出处
作者: TigerChain
地址: http://www.jianshu.com/p/dd2a5b1b95c5
本文出自 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 的人都会点个喜欢或转发的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,827评论 25 707
  • 项目地址 从头开始建立一个React App - 项目基本配置 npm init 生成 package.json ...
    瘦人假噜噜阅读 89,514评论 33 78
  • 1.一个人在学习的过程中,要完美掌握某项复杂技能,就要一遍又一遍艰苦练习,而练习的时长必须达到一个最小临界量。事实...
    好吃懒做的流氓灿阅读 427评论 0 1
  • 生活就是简单平淡,只要想做的事,就算遇到了困难,总是有解决的办法,要善于发现与拥有平静的心。
    夏夏茶点阅读 196评论 0 0
  • 我对喜剧小品有一种独特的感觉。 什么时候开始加深对喜剧小品的认识呢?从关注欢乐喜剧人开始吧。每年的春节联欢晚会都在...
    潮汐有信阅读 385评论 1 0