03、另類(lèi)中的異類(lèi) styled-components

styled-components.png

版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。

PS:轉(zhuǎn)載請(qǐng)注明出處
作者: TigerChain
地址: http://www.itdecent.cn/p/dd2a5b1b95c5
本文出自 TigerChain 簡(jiǎn)書(shū) ReactNative 系列

教程簡(jiǎn)介

  • 1、閱讀對(duì)象

本篇教程適合新手閱讀,老手直接略過(guò)

我們都知道在 RN 中使用的的內(nèi)聯(lián)樣式,是通過(guò) StyleSheet 組件創(chuàng)建出來(lái)的樣式「當(dāng)然也可以使用 const style={} 來(lái)定義」

對(duì)于 web 開(kāi)發(fā)者來(lái)說(shuō),特別是使用 css 樣式的人來(lái)說(shuō)寫(xiě)樣式的時(shí)候總是不自覺(jué)的要加一個(gè) - ,那么你也可以使用樣式組件--styled-components ,這們來(lái)重點(diǎn)介紹一下 styled-components

一、 什么是 styled-components

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

  • 嵌入式
<head> 
 <style type="text/css"> 
   h3{color:red} 
   span{color:blue} 
 </style> 
<head> 
  • 內(nèi)聯(lián)式
<p style="color:#FFEECC;font-weight:bold;">內(nèi)聯(lián)樣式</p> 

  • 外部引用入,有 link import 等

這里說(shuō)一下 link

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

<html>
 指令語(yǔ)句
</html>

在 RN 中使用的也是內(nèi)聯(lián)樣式,那有沒(méi)有一個(gè)既有 web 開(kāi)發(fā)中 css 書(shū)寫(xiě)特性,也能把 React 組件化思想結(jié)合在一起「有樣式的組件」組件呢?styled-components 就能滿足我們的需求

styled-components 則是一個(gè)把組件和 style 聯(lián)系在一起的一個(gè)組件庫(kù),為 React 和 ReactNative 提供一個(gè)干凈,易于使用的界面,說(shuō)白了就是在 js 上寫(xiě) css ,移除了組件和樣式的映射關(guān)系。是 style 的另一種思想,簡(jiǎn)單的舉個(gè)例子來(lái)直觀的感受一下吧,如下:定義了一個(gè)帶有樣式的 Button ,怎么樣帥吧

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

PS:其中 styled.Button 是一個(gè)標(biāo)簽?zāi)0婧瘮?shù),緊跟在后面的 `...` 是一個(gè)模版字符串,模版函數(shù)和模版字符串都是 ES6 的語(yǔ)法,如果不清楚的可以查看 ES6 相關(guān)知識(shí)

二、styled-components 的使用

這里我們以 RN 舉例子來(lái)說(shuō)明,React 使用套路是一樣的

1、在指定目錄下創(chuàng)建一個(gè) RN 項(xiàng)目

react-native init styleComponent

2、安裝 styled-components

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

我們看一下 package.json 中已經(jīng)成功添加了 styled-components 并且版本是 2.2.0

dep_style_com.png

3、新建 app 目錄,并且新建 main.js,并將 main.js 添加到時(shí) index.android.js 和 index.ios.js 中

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

modify_index.png

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

  • 我們使用傳統(tǒng)的 rn 樣式方法來(lái)創(chuàng)建一個(gè)背景為紅色,并且字為紅色的按鈕,如下:
inline_button.png
# inlineStyle.js

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

/**
 * @author TigerChain
 * 普通的內(nèi)聯(lián)樣式的組件
 * @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 來(lái)編寫(xiě)樣式組件的格式是

const CustomView = styled.XXX`
 
 樣式

`

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

PS:使用 styled-components 要先引入這個(gè)庫(kù)
import styled from 'styled-components/native' 我們來(lái)對(duì)比一下兩種方式吧

inline_vs_stylecom.png

把兩個(gè)組件組合到 main.js 中

in_main.png

兩種方式寫(xiě)的生成的一個(gè)相同的組件最后運(yùn)行效果如下:

gen_button.png

經(jīng)過(guò)以上代碼,我們簡(jiǎn)單的了解了一下 styled-components 是個(gè)什么鳥(niǎo),并且如何使用,下面我們來(lái)說(shuō)一些高級(jí)用法吧

三、styled-components 的高級(jí)用法

1、傳遞屬性「定制組件」

  • 1、我們先看一個(gè) React 的例子吧「注意這里說(shuō)的是 React 」

假如我們想要定義按鈕的樣式,分別為大、小,我們會(huì)這樣做,新建一個(gè) 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 實(shí)現(xiàn)以上類(lèi)似的功能如何辦呢?傳遞 props 就派上用場(chǎng)了,廢話不多說(shuō),直接上代碼

  • 2、使用 styled-components 完成上面的按鈕組件

修改 styleComponent.js,并聲明一個(gè)可指定大小的按鈕組件

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、對(duì)任何組件進(jìn)行樣式化

我們項(xiàng)目中有時(shí)會(huì)用三方組件,我們想必變?nèi)浇M件的樣式,styles-components 也是完美支持的,看核心代碼

// 定義可被任意樣式的組件 熟悉 props 的都知道這里的 children是啥意思,style 就不用說(shuō)了
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>

運(yùn)行查看結(jié)果:

any_style.png

大體一個(gè)過(guò)程如下:

any_style_info.png

3、樣式繼承

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

const MyComponent = CustomComponent.extend`
    擴(kuò)展樣式或重寫(xiě)樣式
    ...
`
  • 在 styleComponent.js 中添加自定義樣式擴(kuò)展組件
//定義可繼承的組件
const MyCusTomText = styled.Text`
  color: red;
  margin-top: 8;
`
//擴(kuò)展組件
const ExtendText = MyCusTomText.extend`
  font-size:25;
  font-weight:bold;
`
  • 使用
  <MyCusTomText>自定義文本</MyCusTomText>
  <ExtendText>擴(kuò)展文本</ExtendText>
  • 運(yùn)行查看結(jié)果
exnted_style.png

4、附加 props 使用 .attrs 構(gòu)造函數(shù)

在 .attrs 構(gòu)造函數(shù)中,我們可以聲明靜態(tài) props ,動(dòng)態(tài) props ,目的是為了避免傳遞不必要的屬性

  • 格式:
const MyView = styled.View.attrs({
    ...
    xxx:props => props.yyy
    ...
})`
    xxx:${props => props.xxx}
`
  • 以上格式看起來(lái)比較模糊,我們通過(guò)代碼來(lái)感受一下
// 使用 .atrr 來(lái)添加額外的 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;

`

使用并查看結(jié)果

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

結(jié)果如下:

attr_style.png

過(guò)程分析

attr_style_detail.png

總結(jié):

經(jīng)過(guò)以上我們對(duì) styled-componets 有了一個(gè)大體的認(rèn)識(shí)和了解,styled-components 還支持動(dòng)畫(huà)等,具體可以去官網(wǎng)查看,總結(jié) styled-componentd 有以下特點(diǎn)

  • 是 css in js 的一種實(shí)現(xiàn)方式
  • 移除了樣式和組件的映射關(guān)系
  • 使用組件看起來(lái)更清晰
  • 使樣式組件和邏輯組件更好的區(qū)分開(kāi),使得兩種組件解耦
  • 與傳統(tǒng)單一職責(zé)相比,styled-components 就相當(dāng)于富二代,與生俱來(lái)就可以寫(xiě)一個(gè) style 的組件「?jìng)鹘y(tǒng)的推崇 html css js 等都要分開(kāi)」

這個(gè)玩意開(kāi)始寫(xiě)的時(shí)候人很別扭「特別對(duì)于喜歡 js css html 分離的人」,但是寫(xiě)著寫(xiě)著就有一種巴黎歐萊雅的感覺(jué)---你值得擁有

三、性能對(duì)比

既然 styled-components 讓組件擁有完整的功能,并且是目前前端變化的趨勢(shì),那么我們到底該不該使用 styled-components 呢?說(shuō)到這里估計(jì)有人要罵筆者了「我靠,我都照著你說(shuō)的的代碼擼了一遍了,你居然這樣說(shuō)...」。我只想說(shuō)年輕人,稍安勿躁

一個(gè)組件不光是好用就是我們要選擇的理由,我們還要考慮性能、組件對(duì)包大小的影響,兼容性的影響等等,這里我們對(duì)比一下 styled-components 和傳統(tǒng)的 in-line css 的性能

開(kāi)始擼碼

在上面 demo 的基本上添加代碼,在這里我們要統(tǒng)計(jì)一代碼的執(zhí)行時(shí)間,所以安裝一個(gè)依賴(lài)庫(kù) 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`
`;

/**
 * 樣式組件性能測(cè)試
 * @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} 個(gè)組件`);
    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 性能測(cè)試
 * @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} 個(gè)組件`);
      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:`測(cè)試 === ${i}`})
}

export default data
  • 4、測(cè)試,在 main.js 引入即可
test_perfor.png

在這里我們?cè)谀M器或是真機(jī)上開(kāi)啟遠(yuǎn)程 debug 調(diào)試,然后打開(kāi) chrome 調(diào)試就可以看到 console 打出的 log 了

romote_debug.png

打開(kāi) chrome 調(diào)試工具

show_log.png

經(jīng)過(guò)五次對(duì)比,得出以下結(jié)果

inline_vs_style-components.png

結(jié)論

  • 由于我們是開(kāi)啟遠(yuǎn)程調(diào)試工具,所以和網(wǎng)絡(luò)也有關(guān)系,得出的結(jié)論不一定是最準(zhǔn)確的
  • 但是也說(shuō)明了一些問(wèn)題 styled-components 目前的版本來(lái)說(shuō)「2.2.0」性還是比 in-line 樣式的性能稍有點(diǎn)差,
  • styled-components 完全可以使用在項(xiàng)目中,并且相信以后的版本會(huì)逐漸縮小和 in-line 樣式的性能的微小差距

到此為止,我們就介紹完了 styled-components 的用法和性能比較,快上手試試吧

最近開(kāi)了公號(hào),以后文章內(nèi)容第一時(shí)間會(huì)發(fā)在公號(hào)中,希望大家關(guān)注,掃描以下二維碼即可關(guān)注

據(jù)說(shuō)每一個(gè)勤奮努力想成為非常牛 B 的人都會(huì)點(diǎn)個(gè)喜歡或轉(zhuǎn)發(fā)的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,872評(píng)論 25 709
  • 項(xiàng)目地址 從頭開(kāi)始建立一個(gè)React App - 項(xiàng)目基本配置 npm init 生成 package.json ...
    瘦人假?lài)?/span>閱讀 89,729評(píng)論 33 78
  • 1.一個(gè)人在學(xué)習(xí)的過(guò)程中,要完美掌握某項(xiàng)復(fù)雜技能,就要一遍又一遍艱苦練習(xí),而練習(xí)的時(shí)長(zhǎng)必須達(dá)到一個(gè)最小臨界量。事實(shí)...
    好吃懶做的流氓燦閱讀 507評(píng)論 0 1
  • 生活就是簡(jiǎn)單平淡,只要想做的事,就算遇到了困難,總是有解決的辦法,要善于發(fā)現(xiàn)與擁有平靜的心。
    夏夏茶點(diǎn)閱讀 220評(píng)論 0 0
  • 我對(duì)喜劇小品有一種獨(dú)特的感覺(jué)。 什么時(shí)候開(kāi)始加深對(duì)喜劇小品的認(rèn)識(shí)呢?從關(guān)注歡樂(lè)喜劇人開(kāi)始吧。每年的春節(jié)聯(lián)歡晚會(huì)都在...
    潮汐有信閱讀 433評(píng)論 1 0

友情鏈接更多精彩內(nèi)容