初探react-native(当前仅基于ios)

一. 环境搭建(当前为mac)

需要安装的依赖有:Node(v10版本以上),WatchMan,React Native以及Xcode。

Node,Watchman

官方推荐的是用HomeBrew来安装WatchMan

brew install watchman

Node建议更改npm镜像

npm config set registry https://registry.npm.taobao.org --global

npm config set disturl https://npm.taobao.org/dist --global

Yarn、React Native 的命令行工具(react-native-cli)

React Native 的命令行工具用于执行创建、初始化、更新项目、运行打包服务(packager)等任务

npm install -g yarn react-native-cli

安装完 yarn 后同理也要更改镜像源:

yarn config set registry https://registry.npm.taobao.org --global

yarn config set disturl https://npm.taobao.org/dist --global

Xcode

mac的话可以去app store一键进行安装

初始化一个Demo

使用 React Native 命令行工具来创建一个名为"AwesomeProject"的新项目,init 命令默认会创建最新的版本。

react-native init AwesomeProject

在这个阶段可能会在CocoaPods安装相关依赖时卡住,因为其仓库不在国内,可以尝试更换成国内镜像,更换之后直接切换当前目录,启动项目。

cd AwesomeProject

react-native run-ios

如若遇到以下错误,可以尝试切换设备型号


image
react-native run-ios --simulator="iPhone 11"

如下所示,我们的环境就已经搭建好了。


image

二. react-native布局

react-native使用的布局模式是FlexBox,不过值得注意的是它跟我们之前所接触的css3的FlexBox还是有所不同的。
  • flexDirection : react-native默认为 'column',而在css3中默认为 'row'。
  • alignItems : react-native中默认为 'stretch',而在css3中默认为 'flex-start'。
  • react-native中flex不接受多参数,不像css3中类似这种:flex: 2 2 20%。
  • 不支持的属性: align-content,flex-basis,order,flex-flow,flex-shrink。
其他的就给一张图参考下吧
Flex.jpg

三. navigation 导航器

1. 相关依赖安装

进入当前初始化好的react-native项目,我们首先得安装react-navigation,以及navigation所依赖的第三方库,并将其关联至react-native。这边我是通过yarn包管理器进行安装的:

yarn add react-navigation

yarn add react-native-gesture-handler

react-native link react-native-gesture-handler 

// 我这里ReactNative版本是0.6x的,所以最后一步link操作略不同,如下所示:
cd ios 

pod install

cd..

2. 路由创建及切换

在ReactNative中使用createStackNavigator()来配置路由,这个方法接收一个路由对象参数。假设我们对Home唯一的路由配置是页面组件,我们不需要使用{screen:HomeScreen}配置格式,可以直接使用页面组件。通过initialRouteName我们可以设置默认路由。而createAppContainer()这个函数返回的是一个React组件,并将createStackNavigator创建的 React 组件 作为参数,可以直接从App.js 导出,用作我们应用程序的根组件。

import {createAppContainer} from 'react-navigation';

import {createStackNavigator} from 'react-navigation-stack';

const AppNavigator = createStackNavigator(
  {
    Home: HomeScreen,
    Details: DetailsScreen,
  },
  {
    initialRouteName: 'Home',
  },
);

export default createAppContainer(AppNavigator);
路由间的切换使用this.props.navigation.navigate('XXX'),如下所示:
<Button
      title="跳转详情页"
      onPress={() => this.props.navigation.navigate('Details')}
 />
  • this.props.navigation:navigation通过prop 传递给每个在 stack navigator 中定义的屏幕组件。
  • navigate('Details'):navigator()里面所传参数就是用户定义的路由名,支持第二个参数作为跳转路由携带的参数。
  • this.props.navigation.getParam(): 通过这个方法可以读取参数。
点击跳转至与当前所处路由相同,参数不同的业务场景:

因为当前机制识别跳转路由与当前路由相同时,不会做任何操作,我们需要使用push来进行实现。

this.props.navigation.push('Details', {
      itemId: Math.floor(Math.random() * 100),
})

3. 导航头

导航头Bar配置

通过navigationOptions这个静态属性对其进行配置,可以返回一个静态对象,也可以通过返回方法动态实现。

  • 静态方式
static navigationOptions = {
    title: 'Home',
  };
  • 动态方式
static navigationOptions = ({navigation}) => {
    return {
      title: navigation.getParam('otherParam', 'zwzwzw'),
    };
  };
  render() {
    return (
      <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
        <Text>Home Screen</Text>
        <Button
          title="点击修改title"
          onPress={() => {
            this.props.navigation.setParams({otherParam: '帅爆了!'});
          }}
        />
      </View>
    );
  }

头部样式:

  • 全部路由共享的默认样式
const AppNavigator = createStackNavigator(
  {
    Home: HomeScreen,
    Details: DetailsScreen,
  },
  {
    initialRouteName: 'Home', 
    defaultNavigationOptions: {
      headerStyle: {
        backgroundColor: '#f4511e', // header外层view的样式,这里设置的为bar的背景色
      },
      headerTintColor: '#fff', // 返回按钮和标题都使用这个属性作为它们的颜色
      headerTitleStyle: {
        fontWeight: 'bold', // 如果我们想为标题定制fontFamily,fontWeight和其他Text样式属性,我们可以用它来完成。
      },
    },
  },
);
  • 覆盖默认样式
static navigationOptions = ({navigation, navigationOptions}) => {
    const {params} = navigation.state;
    return {
      title: params ? params.otherParam : 'detail页面',
      headerStyle: {
        backgroundColor: navigationOptions.headerTintColor,
      },
      headerTintColor: navigationOptions.headerStyle.backgroundColor,
    };
  };
  • 使用自定义样式(参考官方文档案例,引用图片组件)
class LogoTitle extends React.Component {
  render() {
    return (
      <Image
        source={require('./spiro.png')}
        style={{ width: 30, height: 30 }}
      />
    );
  }
}

class HomeScreen extends React.Component {
  static navigationOptions = {
    // headerTitle instead of title
    headerTitle: () => <LogoTitle />,
  };
}
  • 标题按钮

IOS 上的后退按钮使用的标题字符串, 或 null 禁用标签。 默认为上一个场景的 headerTitle

从下图可以发现返回按钮为上一个页面的title,所以我们得对其覆盖。


image.png
StackNavigator({
  A: {
    screen: AScreen,
    navigationOptions: () => ({
      title: `zwzwzw`,
      headerBackTitle: null, //对其重写或自定义
    }),
  },
  B: {
    screen: BScreen,
    navigationOptions: () => ({
      title: `anything you want here`,
    }),
  },
});

至于头与页面之间的交互可以参考官方文档计数器案例。

modal堆栈

我理解的modal堆栈就是重新使用createStackNavigator()方法创建一个新的stackNavigator,然后将之前的页面stackNavigator嵌套进去,如下所示:

const RootStack = createStackNavigator(
  {
    Main: {
      screen: MainStack,
    },
    MyModal: {
      screen: ModalScreen,
    },
  },
  {
    mode: 'modal', // modal的进入样式,ios默认为modal,由下而上,card则为左右切换样式。
    headerMode: 'none', // 头部样式,这样写的话是全屏模式。
  }
);

modal的样式很像苹果手机的激活提示,由下而上弹出请激活。

列表组件

1.FlatList

这是一个简单的高性能列表组件,直接上个简单的例子,熟悉一些基础的组件属性:

const CITY_NAME = ['益阳', '株洲', '长沙', '湘潭', '宁乡'];
class DetailsScreen extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      loading: false,
      data: CITY_NAME,
    };
  }
  static navigationOptions = ({navigation, navigationOptions}) => {
    const {params} = navigation.state;
    return {
      title: params ? params.otherParam : 'detail页面',
      headerStyle: {
        backgroundColor: navigationOptions.headerTintColor,
      },
      headerTintColor: navigationOptions.headerStyle.backgroundColor,
    };
  };

  _renderItem(data) {
    return (
      <View style={styles.item}>
        <Text style={styles.text}>{data.item}</Text>
      </View>
    );
  }

  loadData(flag) {
    if (flag) {
      this.setState({
        loading: true,
      });
    }

    setTimeout(() => {
      let arr = [];
      if (flag) {
        for (let i = this.state.data.length - 1; i >= 0; i--) {
          arr.push(this.state.data[i]);
        }
      } else {
        arr = this.state.data.concat(CITY_NAME);
      }

      this.setState({
        data: arr,
        loading: false,
      });
    }, 2000);
  }

  genIndicator() {
    return (
      <View style={styles.indicatorContainer}>
        <ActivityIndicator
          style={styles.indicator}
          size={'large'}
          color={'red'}
          animating={true}
        />
        <Text>正在加载更多...</Text>
      </View>
    );
  }

  render() {
    const {navigation} = this.props;
    return (
      <View style={{flex: 1}}>
        <FlatList
          data={this.state.data}
          renderItem={data => this._renderItem(data)}
          refreshControl={                            //下拉刷新
            <RefreshControl
              title={'加载中...'}                      
              tintColor={'orange'}                   // 苹果专属
              refreshing={this.state.loading}          
              onRefresh={() => {                    //刷新状态控制
                this.loadData(true);
              }}
            />
          }
          ListFooterComponent={() => this.genIndicator()}      //底部加载更多样式实现
          onEndReached={() => this.loadData()}                      //触底后进行的操作
        />
      </View>
    );
  }
}
基本样式
image.png

image.png

2. SectionList

sectionList的使用方式跟FlatList差不多,区别在于接受的数据源的属性以及数据源的类型不同。

  • sectionList的数据源实例
const CITY_NAME = [
  {data: ['益阳', '株洲', '长沙', '湘潭', '宁乡'], title: '湖南'},
  {data: ['虎门', '广州', '河源'], title: '广东'},
];
  • 接收数据源的属性
sections={this.state.data}
  • title渲染属性
renderSectionHeader={data => this._renderHeader(data)} // 组标题渲染

// 以下为方法
  _renderHeader({section}) {
    return (
      <View style={styles.sectionHeader}>
        <Text style={styles.sectionText}>{section.title}</Text>
      </View>
    );
  }
  • 分割线属性
 ItemSeparatorComponent={() => <View style={styles.separator} />} // 分割线样式

// 样式设置
 separator: {
    height: 1,
    backgroundColor: 'gray',
    flex: 1,
  },
下面是粗糙成品(有点丑哈哈哈)
image.png

本来研究下了swipeableList的,不过0.61的react-native库源码中没有相关引入,只能空闲时候研究下原因然后下次笔记补上~

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

推荐阅读更多精彩内容