React Native 总结(一)

作者: SongKX懒 | 来源:发表于2016-11-15 18:07 被阅读3049次

    React Native总结

    不过多描述原理和优缺点,可自行查阅官方手册

    一、注意点

    1.关于style

    style用于标注组件的布局属性,一般情况下有两种写法形式:

    内连形式一:

    <View style={{flex:1,backgroundColor:'red'}}/>
    

    抽象形式二:

    const styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
      })
      <View style={styles.container}/>
    

    两种形式都可以用于添加组件布局属性,但一般情况下,在组件所属目录下,创建名为styles.js的文件夹,使用第二种形式在文件内统一创建style,并对外export。

    这样做的好处一方面是使代码清晰整洁,不冗余,方便复用和修改,另一方面是防止每次组件render时,频繁的进行style创建。

    还有一种情况是内连和抽象形式连用形成包裹形式:

    <View style={[styles.container,this.props.style]}/>
    

    这种形式在进行自定义组件的时候十分常见,为style赋值一个数组,第一个数组对象一般设置为组件的固有布局属性或默认布局属性,第二个数组对象为组件使用时,外部对组件布局属性的自定义。如果两个布局属性存在同样的关键字时,以第二的布局属性为准。

    2.关于ref

    组件的ref字段一般被用来获取组件的对象,一般书写形式如下:

        compoentDidMount(){
        this.refs.view // 获取到view组件对象
        this._scroll.scrollTo({y:10,x:10})//获取到ScrollView对象,并执行scrollTo()
        }
        ....
        <View ref='view'></View>
        <TextInput ref={(input)=>{
            //此处可以对TextInput组件进行操作,比如focus()
        }}/>
        <ScrollView ref={(scroll)=>this._scroll = scroll}/>
    

    可以尝试打印一下通过ref获取到的组件对象,例如打印scrollview的组件对象,可以找到组件对象可以调用的方法、组件的属性和其子组件与父组件信息。
    ref仅可以在组件被render到界面后,才可以调用,否则会返回undefined;

    3.关于image

    关于Image组件,目前版本,在iOS和Android平台上行为略微不一致,当不给Image设置source或source不可用时,仅设置布局属性时,iOS端会显示根据布局属性生成无图组件,进行占位;Android端则不会生成无图组件进行占位,即便设置了明确的长高属性。

    4.关于onLayout和NativeMethodsMixin

    每个组件都可以设置onLayout属性来获取组件的位置信息

    <View onLayout={(e)=>{e.nativeEvent即是组件的位置信息}/>
    

    但通过onLayout获取到的位置信息只是相对于父组件的位置坐标,而不是相对于屏幕位置的绝对坐标。
    通过对NativeMethodsMixin方法的调用,可以帮助我们直接对组件进行操作,其中一方面功能就是获取组件在最底层视图上显示的尺寸。

    this.refs.view.measure((x,y,width,height,pageX,pageY)=>{
            console.log(x,y,width,pageX,pageY,height);
        })
    

    但是,这个方法需要预留反应时间,目前版本中,在componentDidMount中不可以调用此方法,虽然不会报错,但返回结果全部为0;正如官方文档所说,如果想更快的获取组件尺寸,请使用onLayout;
    NativeMethodsMixin还有两个比较常用的方法:measureInWindow和measureLayout。当设计模式为在原生应用中嵌入React Native窗口时,可以使用measureInWindow获得组件相对于屏幕的坐标信息,参数是一个callBack,用法参照measure;measureLayout可以用来获取组件和目标组件之间的相对坐标。参数有三个:第一个为目标视图的节点,第二个参数为成功时的回调,第三个参数为失败时的回调。获取组件节点使用findNodeHandle,从0.27版本后,该方法引用方式变为

    import {findNodeHandle} from 'react-native';
    

    对于onLayout还有一点疑问,如果调用如下代码

    <View onLayout={()=>log}>
        <View onLayout={()=>log}>
            <View onLayout={()=>log}>
            </View>
        </View>
    </View>
    

    即对一个多层级的组件都设置onLayout属性时,Android端外层onLayout的触发会比内层快一点,而iOS端onLayout的触发则是不固定的,同时触发时快时慢。虽然相差时间都是毫秒级别,目前没有发现影响,但不排除特定状态下会产生bug。

    5.关于Immuatble.js

    Immutable.js 是facebook的一个js库,目的就是为JavaScript添加不可变的数据类型。React Native中在使用state控制视图时,可以通过使用Immutable.js来进行state判断,确定是否刷新视图。
    Immutable的语法不过多介绍,此处只介绍Immutable的is方法。

    is方法用来比较两个Immutable类型的数据是否相等,此处的比较为值比较。与之对应的Object.is和 === 则是进行地址比较。

     let test1 = Immutable.fromJS({a:{b:1}});
     let test2 = Immutable.fromJS({a:{b:2}});
     let temp = test2.setIn(['a','b'],1);//将键b的值改为1
     console.log(Immutable.is(test1,temp));//打印结果为true
     let test3 = {a:1,b:2};
     let test4 = {a:1,b:2};
     console.log(Object.is(test3,test4));//打印结果为false
     console.log(test3===test4);//打印结果为false
    

    通过使用Immutable.js,可以在shouldComponentUpdate中,完成对states和props的判断。其他用法详见文档和链接
    <a>http://facebook.github.io/immutable-js/docs/#/<a/>
    <a>https://zhuanlan.zhihu.com/p/20295971</a>

    6.关于视图渲染刷新

    6.1 过度渲染

    React Native组件的渲染都在render函数中执行并return,即每触发一次render函数都要重新渲染一次组件。而每次对state的修改则会触发一次render,将整个组件重新渲染。这就将导致一个问题,即过度渲染。有时候我们频繁的进行state操作时,频繁的触发render,导致页面卡顿,性能下降,动画掉帧等一系列问题。

    6.2 setNativeProps

    setNativeProps其实是对组件底层进行操作,而不是在RN框架层面进行组件的设置,所以调用setNativeProps方法可以规避render等组件相关方法的触发,但是官方建议,能用state和shouldcomponentUpdate解决时就不用setNativeProps,因为使用setNativeProps可能会导致代码结构不是很清晰或state混乱。所以setNaiveprops一般被用于进行动画设置和增强视觉效果方面。用法如下:

    this.refs.view.setNativeProps({backgroundColor:'red'})
    //将view背景改为红色
    

    6.3 shouldComponentUpdate

    在shouldComponentUpdate方法中我们可以判断组件下一组props和state的变化来决定是否触发render,重新渲染组件。此处可以使用上面所提到的Immuatble.js库。
    具体写法

    shouldComponentUpdate(nextProps,nextState){
    const thisProps = this.props || {}, thisState = this.state || {};
    if (Object.keys(thisProps).length !== Object.keys(nextProps).length ||
        Object.keys(thisState).length !== Object.keys(nextState).length) {
      return true;
    }
    for (const key in nextProps) {
      if ((thisProps[key] !== nextProps[key] || !is(Immutable.fromJS(thisProps[key]), Immutable.fromJS(nextProps[key])))&&typeof(thisProps[key])!="function") {
        return true;
      }
    }
    for (const key in nextState) {
      if (thisState[key] !== nextState[key] || !is(thisState[key], nextState[key])) {
        return true;
      }
    }
    return false;
    }
    

    基本的shouldComponentUpdate的写法如上所示,根据组件需求进行微调即可。但需要注意的是,当state或者props中存在function时,此时比较state和props,总会返回false,即previousState(props)和nextState(props)始终不相等,所以一般情况下比较两者时,屏蔽function类型。

    6.4 组件化

    组件化的好处一方面是为了进行组件的复用,还有一方面是进行组件是否重新渲染的判断。例如一个组件有7个层级,当组件重新渲染时,就要把7个层级都渲染一遍,而且因为7个层级组件都写在了一个render里,不仅代码臃肿,而且无法进行区别判断是否渲染。所以尽量将代码抽离,实现组件化。

    7.关于Promise、async和await

    Promise对象是ES6新增的,常作为异步结果的返回值,例如:本地数据存储或fetch网络请求。async函数同样是ES6新增的,需要同await联合使用。所以一般情况下同时使用两者去进行异步处理,以网络请求为例

    async someHandle(url){
        try{
        let result = await fetch(url);
        
        console.log(result)
        
        return result;
        }catch(err){
        console.log(err);
        }
    }
    

    但是aysnc函数有一个特点是假如fetch等异步操作返回reject,那么async函数将直接返回reject状态,而不会往下继续执行。即假如fetch若返回reject,将不会执行下面的打印。
    可以在async函数内加上try...catch,来收集异步操作的错误信息。

    8.关于Redux

    Redux是一个state管理框架,只存在一个store来存储所有的state,只可以通过触发action,action触发reducer,去修改store里的state,来确保state的状态改变行为可控。

    8.0 必备框架

    react-redux 、redux 、redux-thunk即可,全部可以通过

    npm install --save 框架名 
    

    安装

    8.1 action

    action相当于一个触发动作,告诉reducer去修改state。
    一般的action形式:

    someHandle(value){
        
        return{
            type:someType,
            value,
        }
    }
    

    action函数必须返回一个包含type的对象,其他字段和对象结构可以自行设计,返回type的目的是为了在reducer进行响应的时候进行判断,具体如何哪个state。

    8.2 reducer

    reducer函数是通过action触发的,state的修改是在该函数内进行的。
    一般的reducer形式:

    const initialState = Map({
        number:1,
        opacity:0,
        offset:0
    })
    export default function someHandle (state= initialState, action={}){
        switch (action.type) {
            case SOMETYPE:
            return   //此处返回操作过的state
            default:
            return state;
            }
    }
    

    注意reducer中,不可以直接修改state,需要根据操作,生成新state,并返回,reducer需要保证数据的唯一和不可变,所以此处的state可以使用Immutable.js进行数据类型的定义。

    8.3 redux-thunk

    redux-thunk是一个redux的中间件,可以使某一action可以出发另一个action,目的是为了可以在action函数中进行异步操作,例如进行网络请求获取json数据并处理。

    someHandle(){
        return (dispatch,getState){
            
            //一些异步处理
            
            dispatch({       
                type:someType,
                ...
            })//触发reducer
        }
    }
    

    该框架不是实现redux的必须框架,中间件可以自行定义。

    8.4 其他

    使用redux结构管理state不一定总是有益的,在项目需求简单时,接入该结构完全没有必要。而且在自定义组件是也不需要引入该结构,具体情况还需要按照项目需求进行处理。
    redux在React Native中的工作其实是控制每个组件的props,通过改变组件的props,达到组件渲染和状态的可控,所以redux的state并不代表RN框架的state,也不是对state的取代,而是另一种形式的状态机。两者各有利弊,结合使用效果更佳。
    demo地址:<a>https://pan.baidu.com/s/1eS81cXG<a/>(非原创,但是原地址找不到了。。。)

    相关文章

      网友评论

      本文标题:React Native 总结(一)

      本文链接:https://www.haomeiwen.com/subject/pollpttx.html