RN中佈局樣式的寫法

66CCFF發表於2019-03-03

介紹原始寫法 & 及其改進寫法一

還有比較流行的 styled-components在RN中的使用 & 及其改進寫法二

1.原寫法

/**
 * 原寫法
 */
const styles1 = StyleSheet.create({
    item1:{
        width:100,
        height:200,
        backgroundColor:`#66CCFF`,
    },
    item2:{
        width:100,
        height:200,
        backgroundColor:`#66CCFF`,
    },
    item3:{
        width:50,
        height:100,
        top:50,
        left:25,
        backgroundColor:`#66CCFF`,
    }
});
console.log(`styles1 :`,styles1);
複製程式碼

原寫法的缺點在於變數不好引入,不好體現樣式間的關係,沒有計算表示式等……

2.改進寫法一

看StyleSheet.create的程式碼,就是直接返回一個物件

//在react native 0.44版本中
var StyleSheet = {
  create: function(styles) {
    return styles;
  },
複製程式碼

那就可以不限於StyleSheet.create的寫法,可以比較自由的返回一個物件了,下面給出一個我的簡單例子:


/**
 * 換一種寫法,簡單引入了變數表示式
 * 雖然還是沒有像iOS中 view.center / autolayout之類的寫法方便
 * @returns {{}}
 */
function styles2Creator() {
    let s = {};
    let itemWidth = 100;
    let itemHeight = 200;
    //引用常量
    s.item1 = {
        width:itemWidth,
        height:itemHeight,
        backgroundColor:`#66CCFF`,
    };
    //引用其他樣式的值
    s.item2 = {
        width:s.item1.width,
        height:itemHeight,
        backgroundColor:`${s.item1.backgroundColor}`,
    };
	//計算表示式 
    s.item3 = {
        width: s.item2.width / 2,
        height: s.item2.height / 2,
        top:s.item2.height / 4,
        left:s.item2.width / 4,
        backgroundColor:s.item1.backgroundColor,
    };
    //樣式的繼承
    s.item4 = {
    	...s.item3,
    	backgroundColor:`#FF00CC`,
    };
    //帶引數
    s.item5 = (top) => {
    	return {
    		...s.item3,
    		marginTop:top,
    	};    	
    };
    //帶引數 + 預設值
    s.item6 = (top) => {
    	return {
    		...s.item3,
    		marginTop:top ? top : 10,
    	};       	
    }
    return s;
}
const style2 = styles2Creator();
//const style2 = StyleSheet.create(styles2Creator());
console.log(`style2 :`,style2);
複製程式碼

執行一下可以看到 log出來的style2和style1的屬性。

3. styled-components

號稱React 中的 CSS 最佳實踐,使用行內樣式,支援CSS。該第三方也也可用於RN。

react-native init了個RN工程 寫了個簡單的例子:


import React, {Component} from `react`;
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Image
} from `react-native`;
//需要 `npm install --save styled-components`
import styled from "styled-components/native";


//這裡開始styled-components 式的樣式:
//styled-components的格式為: const [自定義變數名] = styled.[相應的系統元件]`css表示式`;

//普通寫法 (不大寫開頭會報錯……
const ListContainerView = styled.View`
    width:360;
    height:280;
    background-color: #F0F2F5;
    
`;

//擴充套件 (backgroundColor 和 background-color都可以
const ItemContainerView = ListContainerView.extend`
    backgroundColor: #66CCFF;
    flexDirection:row;
`;

//帶引數
const LeftImageContainerView = styled.View`
    height:${props => props.primary ? 280 : 180};;
    width:180;
    background-color: #77BB00;
`;

//然後發現一個尷尬的事就是不知道怎麼擴充套件自定義元件 比如JDImage
//帶計算表示式
const LeftImageHeight = 280 - 10 *2;
const LeftImage = styled.Image`
    margin-top:10;
    margin-left:10;
    width:${180 - 10 *2};
    height:${LeftImageHeight};
    background-color: #FFFFFF;
`;

//想要獲取另一個元件樣式LeftImage的高度 不能直接使用 ${LeftImage.height}
//因為 LeftImage返回的是  ƒ StyledNativeComponent()方法……
const RightContainerView = styled.View`
    width:160;
    height:${LeftImageHeight};
    background-color: #FF00CC;
`;


export default class MyApp extends Component {
    render() {
        return (
            <ItemContainerView >
                <LeftImageContainerView primary>
                    {console.log(`LeftImageContainerView.style:`,LeftImageContainerView)}
                    <LeftImage source={{uri:`http://static.runoob.com/images/demo/demo2.jpg`}}/>
                </LeftImageContainerView>
                <RightContainerView></RightContainerView>
            </ItemContainerView>
        );
    }

}

AppRegistry.registerComponent(`MyApp`, () => MyApp);

複製程式碼

優點:

  • react推薦的行內樣式 css in js;
  • 方便前端同學的寫法 可以完全使用CSS的書寫習慣

缺點和原始寫法的差不多,還對本身不是前端開發的人來說帶來額外的學習成本…… 所以還是不推薦……

參考連結:

4.改進寫法二

簡單來講 styled-components 就是生成一個帶樣式的元件,完全可以吸收這種寫法 自己改進RN中 ,而不使用styled-components這個庫

結合 寫法一 和 styled-components的想法,給出一個簡單例子:

import React, {Component} from `react`;
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Image
} from `react-native`;

//寫法二
function stylesCreator() {
    let s = {};
    let width = 360.0, height = 280.0;
    s.ListContainerView = {
        width:  width,
        height: height,
        backgroundColor: `#F0F2F5`,
    };
    s.ItemContainerView = {
        width:  width,
        height: height,
        backgroundColor: `#66CCFF`,
        flexDirection:`row`,
    };
    console.log(`s.ItemContainerView :`,s.ItemContainerView);
    s.LeftImageContainerView = {
        height:height,
        width:width / 2,
        backgroundColor: `#77BB00`,
    };
    s.LeftImage = {
        marginTop:10,
        marginLeft:10,
        width: 180 - 10 *2,
        height: s.LeftImageContainerView.height - 10*2,
        backgroundColor: `#FFFFFF`,
    };

    s.RightContainerView = {
        width: width / 2,
        height: s.LeftImage.height,
        backgroundColor: `#FF00CC`,
    };
    return s;
}
const styles = stylesCreator();
//const styles = StyleSheet.create(stylesCreator());

//然後再結合 styled-components:
// 模擬 styled-components API
const styled = (Component, styler) => (props) => {
    const style = typeof styler === `function` ? styler(props) : styler;
    return <Component {...props} style={[ style, props.style ]} />
}

// styled components
//同樣可以完成元件(帶樣式)的繼承 const RightContainerView = styled(LeftImageContainerView,styles.RightContainerView);

const ListContainerView = styled(View,styles.ListContainerView);
const ItemContainerView = styled(View,styles.ItemContainerView);
const LeftImageContainerView = styled(View,styles.LeftImageContainerView);
const LeftImage = styled(Image,styles.LeftImage);
const RightContainerView = styled(View,styles.RightContainerView);

export default class MyApp extends Component {
    render() {
        return (
            <ItemContainerView >
                <LeftImageContainerView primary>
                    {console.log(`LeftImageContainerView.style:`,LeftImageContainerView)}
                    <LeftImage source={{uri:`http://static.runoob.com/images/demo/demo2.jpg`}}/>
                </LeftImageContainerView>
                <RightContainerView></RightContainerView>
            </ItemContainerView>
        );
    }

}

AppRegistry.registerComponent(`MyApp`, () => MyApp);
複製程式碼

emmm ,無需引入第三方庫 感覺好多了。缺點當然是不支援原CSS寫法。

5. react native 0.45

在0.45版本中執行改進寫法一時,你可能看到style2在控制檯的輸出類似為:

//const style2 = StyleSheet.create(styles2Creator());

  style2 : 
{item1: 12, item2: 13, item3: 14, item4: 15, item5: 16, …}
	item1:12
	item2:13
	item3:14
	item4:15
	item5:16
	item6:17
__proto__:Object
複製程式碼

這是怎麼肥事!我的item物件怎麼變成數字了!

別急,在0.45版本後StyleSheet程式碼有所改變(其實我沒看具體哪個小版本改的 _(:зゝ∠)_), StyleSheet.create改成:

//react native : 0.45.1
  create<S: Styles>(obj: S): StyleSheet<S> {
    const result: StyleSheet<S> = {};
    for (var key in obj) {
      StyleSheetValidation.validateStyle(key, obj);
      result[key] = ReactNativePropRegistry.register(obj[key]);
    }
    return result;
  },
複製程式碼
//react native0.45.1 ReactNativePropRegistry.js
var objects = {};
var uniqueID = 1;
var emptyObject = {};

class ReactNativePropRegistry {
  static register(object: Object): number {
    var id = ++uniqueID;
    if (__DEV__) {
      Object.freeze(object);
    }
    objects[id] = object;
    return id;
  }
//多的就不看了……內容不多各位有興趣自己看  
複製程式碼

通過看ReactNativePropRegistry程式碼,StyleSheet將樣式物件儲存在objects中,並返回uniqueID

比如取回原來的item,就可以這樣做:

import ReactNativePropRegistry from `../node_modules/react-native/Libraries/Renderer/src/renderers/native/ReactNativePropRegistry`;
console.log("ReactNativePropRegistry.getByID(12): ",ReactNativePropRegistry.getByID(12));
console.log("ReactNativePropRegistry.getByID(style2. item1): ",ReactNativePropRegistry.getByID(style2. item1));
複製程式碼

就可以通過 ReactNativePropRegistry.getByID就可以取得樣式物件了。這可能對於之前的改進寫法造成了一點小麻煩,不過還可以用~

還可以用 StyleSheet.flatten(style )取得樣式物件;


其他參考閱讀:

相關文章