從一個React Native Demo開始(內(nèi)附Demo源碼)

先看demo

image

寫在最前面

從開始學RN到現(xiàn)在大概有2個星期天左右了,這里先記錄一下,也算個小階段總結(jié)。就目前感覺,RN的優(yōu)勢和劣勢都很明顯;

  • 優(yōu)勢
    • RN是混合開發(fā)一份代碼多端使用
    • 代碼與前端相似,Web轉(zhuǎn)RN比較輕松
  • 劣勢
    • RN組件由多個第三方維護,更新不可控,會有停更不兼容的風險
    • 會由于RN版本,組件版本,Xcode版本的不同而隨機組合成各種坑(這點很令人煩躁,大部分時間都浪費在這)
    • 多端兼容適配成本大,而且會隨著項目規(guī)模而增大難度,到一定程度開發(fā)成本會比原生的高,如Airbnb宣布放棄使用RN,回歸原生技術(shù)

正文

一 環(huán)境安裝

官網(wǎng)說的很詳細,按照官網(wǎng)的步驟基本沒問題,就不多贅述

官網(wǎng)地址: https://reactnative.cn/docs/getting-started.html

二 熟悉RN

創(chuàng)建Q項目,并用iOS模擬器運行起來

 react-native init q
 cd q
 react-native run-ios

項目內(nèi)容如下:

  • android:Android工程文件
  • ios: iOS工程文件
  • node_modules: React-native組件依賴存放的文件夾
  • package.json: 依賴配置json, 類似于cocoPods中的“Podfile”
  • index.js: 項目入口
    ...
image

先看index.js, import 是引入文件,AppRegistry.registerComponent(appName, () => App);整個的意思就是將工程目錄的App.js注冊成組件并引入,所以一開始顯示的即App.js里面的內(nèi)容

import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';

AppRegistry.registerComponent(appName, () => App);

App.js文件里面大致可以分成三部分

  • 引入組件
  • 搭建UI
  • 樣式

有過前端開發(fā)經(jīng)驗的朋友對View,Text,ScrollView這些并不陌生,在React-native中,所有組件都要單獨引入,所有組件及作用可看官方文檔

import React from 'react';
import {
  SafeAreaView,
  StyleSheet,
  ScrollView,
  View,
  Text,
  StatusBar,
} from 'react-native';

import {
  Header,
  LearnMoreLinks,
  Colors,
  DebugInstructions,
  ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';

這里部分內(nèi)容是畫UI,基本上和html沒差多少,都是用各種組件的堆砌。學過web或者小程序之類的看起來會很簡單,沒學過的話,建議選去學學最基本的html + css

const App: () => React$Node = () => {
  return (
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <ScrollView
        .....
        .....
         </SafeAreaView>
    </>
  );
};
export default App;

這里是各種樣式,大部分都是沿用css的,看到這里大概感覺到React-native其實就是前段代碼換個殼,對于有前段知識的人學起來應(yīng)該會很輕松,沒有相關(guān)知識的話建議還是先去學學基礎(chǔ)的再來搞React-native

const styles = StyleSheet.create({
  scrollView: {
    backgroundColor: Colors.lighter,
  },
   .....
   .....
});

三 嘗試Demo

有這些了解后,可以試著做一個簡單的列表頁面

image

1.導入組件,需要的組件大概有這些,重點是FlatList列表組件

import React, { Component } from "react";

import { Image, FlatList, StyleSheet, Text, View } from "react-native";

2.導出默認類App擴展組件,包括住下面的其他代碼

export default class App extends Component {
}

3.創(chuàng)建個長度是8的data數(shù)組,后面可給FlatList賦值用

constructor(props) {
    super(props);
    this.state = {
      data: [{}, {}, {}, {}, {}, {}, {}, {}],
    };
}

4.RN的render()函數(shù)實際上跟iOS的ViewDidLoad()函數(shù)相似,返回的就是具體的內(nèi)容,內(nèi)容很固定,只能是一個組件,這里我返回的是FlatList,字段說明如下

  • data:需要給予一個數(shù)組,數(shù)組長度與列表item對應(yīng)
  • style:列表樣式
  • renderItem:Item渲染,這里是直接調(diào)用renderMovie渲染
  • keyExtractor:設(shè)置每個item唯一的key,類似于索引
render() {
    return (
      <FlatList
        data={this.state.data}
        style={styles.list}
        renderItem={this.renderMovie.bind(this)}  
        keyExtractor={item => item.id}
      />
    );
 }

5.通過renderMovie函數(shù)返回item的內(nèi)容,這里可以任意發(fā)揮

renderMovie({ item }) {
    return (
        <View style={styles.container}>
          <Image
            source={{ uri: 'https://gss3.bdstatic.com/7Po3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign=e4d6ea2325dda3cc0be4bf2639d25e3c/b64543a98226cffcb1f7cc0eb2014a90f703eaa9.jpg' }}
            style={styles.thumbnail}
          />
          <View style={styles.rightContainer}>
            <View style={styles.titleWithout}>
              <Text style={styles.title}>羅小黑戰(zhàn)記</Text>
              <Text style={styles.tip}>中國巨屏</Text>
            </View>
            <Text style={styles.score}>貓眼評分<Text style={styles.grade}> 9.8 </Text></Text>
            <Text style={styles.starring}>主演:羅小黑,羅小白</Text>
            <Text style={styles.cinema}>今天129加音樂反映124場</Text>
          </View>
          <Text style={styles.buy}>購買</Text>
        </View>
    );
  } 

6.最后是樣式,其實這些隨意發(fā)揮即可

這樣簡單的一個頁面就完成了,完整代碼如下,可以直接copy替代原有內(nèi)容運行即可看到效果

import React, { Component } from "react";

import { Image, FlatList, StyleSheet, Text, View, TouchableOpacity } from "react-native";


export default class App extends Component {

  constructor(props) {
    super(props);
    this.state = {
      data: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
    };
  }

  render() {
    return (
      <FlatList
        data={this.state.data}
        style={styles.list}
        renderItem={this.renderMovie.bind(this)}  
        keyExtractor={item => item.id}
      />
    );
  }

  renderMovie({ item }) {
    return (
        <View style={styles.container}>
          <Image
            source={{ uri: 'https://gss3.bdstatic.com/7Po3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign=e4d6ea2325dda3cc0be4bf2639d25e3c/b64543a98226cffcb1f7cc0eb2014a90f703eaa9.jpg' }}
            style={styles.thumbnail}
          />
          <View style={styles.rightContainer}>
            <View style={styles.titleWithout}>
              <Text style={styles.title}>羅小黑戰(zhàn)記</Text>
              <Text style={styles.tip}>中國巨屏</Text>
            </View>
            <Text style={styles.score}>貓眼評分<Text style={styles.grade}> 9.8 </Text></Text>
            <Text style={styles.starring}>主演:羅小黑,羅小白</Text>
            <Text style={styles.cinema}>今天129加音樂反映124場</Text>
          </View>
          <Text style={styles.buy}>購買</Text>
        </View>
    );
  }
}

var styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: "row",
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#fff"
  },
  header: {
    height: 20,
    marginTop: 44,
  },
  rightContainer: {
    flex: 1,
    paddingLeft: 18,
  },
  titleWithout: {
    flexDirection: "row",
    fontWeight: "bold",
    alignItems: "center",
  },
  title: {
    fontSize: 14,
    marginTop: 4,
    lineHeight: 0,
  },
  tip: {
    backgroundColor: "#999",
    fontSize: 8,
    textAlign: "center",
    color: "#fff",
    height: 14,
    width: 40,
    lineHeight: 14,
    borderRadius: 2,
    marginLeft: 4,
    marginTop: 4,
  },
  score: {
    paddingTop: 8,
    fontSize: 12,
    color: "#666",
  },
  starring: {
    paddingTop: 4,
    fontSize: 12,
    color: "#666",
  },
  cinema: {
    paddingTop: 4,
    fontSize: 12,
    color: "#666",
  },
  buy: {
    fontSize: 12,
    // color:'#333',
    width: 46,
    height: 24,
    lineHeight: 24,
    textAlign: "center",
    backgroundColor: "#D44145",
    color: "#fff",
    borderRadius: 12,
    marginRight: 20,

  },
  grade: {
    color: "#DF8D7A",
  },
  year: {
    textAlign: "center"
  },
  thumbnail: {
    width: 68,
    height: 94,
    marginLeft: 20,
    marginTop: 10,
    marginBottom: 10,

  },
  list: {
    paddingTop:40,
    backgroundColor: "#F5FCFF"
  },
  headerOutline: {
    backgroundColor: "#fff",
    marginTop: 44,
  },
  headerInside: {
    backgroundColor: "#f5f5f5",
    flexDirection: "row",
    justifyContent: 'space-between',
    marginLeft: 20,
    marginRight: 20,
    marginBottom: 6,
    paddingTop: 10,
    paddingBottom: 4,
    paddingLeft: 10,
    paddingRight: 10,
  },
  trendIcon: {
    width: 10,
    height: 14,
    marginLeft: 10,
    marginTop: -1,
  },
  trendText: {
    height: 22,
    color: '#333',
    fontWeight: "bold",
  },
  trendR: {
    color: '#333',
    fontSize: 10,
    fontWeight: "bold",
    height: 22,
  },
  trendRText: {

  },
  trendMoney: {
    color: '#D24349',
  },
});

二 Navigation與Tabbar

如圖,最終目的是創(chuàng)建一個帶Navigation,Tabbar的demo,可分為三個步驟

  1. 安裝組件
  2. 創(chuàng)建tabbar上的兩個跟根頁面和一個詳情頁面
  3. 修改index.js入口
image

1.先安裝react-navigation組件

注:這里有個天坑,react-navigation4與3差距很大,現(xiàn)在網(wǎng)上的教程基本使用的都是react-navigation 3,這里我也是兜兜轉(zhuǎn)轉(zhuǎn)搞了許久才意識到的,大家都是初學者,建議安裝版本3

yarn add react-navigation@3.5.1
yarn add react-native-gesture-handler

這里可能會報這個錯


Error: Requiring unknown module "447". If you are sure the module exists, try restarting Metro. You may also want to run `yarn` or `npm install`.

這個錯原因很多,可以嘗試

npm install
react-native run-ios

或者

cd ios
pod install
cd ..
react-native run-ios

2.創(chuàng)建detailsScreen.js,settingScreen.js,navigation.js文件

detailsScreen.js內(nèi)容

import React from 'react';
import {
    View,
    Text,
    Button,
    Image,
} from 'react-native';

export default class detailsScreen extends React.Component {

   
    render() {
        return (
            <View style={{flex:1, alignItems:'center',justifyContent: 'center'}}><Text>詳情頁</Text></View>
        );
    }
}

settingScreen.js內(nèi)容

import React from 'react';
import {
    View,
    Text,
    Button,
    Image,
} from 'react-native';

export default class settingScreen extends React.Component {

   
    render() {
        return (
            <View style={{flex:1, alignItems:'center',justifyContent: 'center'}}><Text>設(shè)置頁</Text></View>

        );
    }
}

navigation.js內(nèi)容需要分步講解一下,首先引入所有需要的組件與頁面

import React from 'react';
import { Text } from 'react-native'

import HomeScreen from "./App";        
import DetailsScreen from "./detailScreen";
import SettingScreen from "./settingScreen";

import {
    createStackNavigator,
    createAppContainer,
    createBottomTabNavigator
} from 'react-navigation';

這里是聲明HomeStack,SettingsStack兩個組件;

createStackNavigator 提供APP屏幕之間切換的能力,它是以棧的形式還管理屏幕之間的切換,新切換到的屏幕會放在棧的頂部。

const HomeStack = createStackNavigator({
    Home: { screen: HomeScreen, }
})
const SettingsStack = createStackNavigator({
    Settings: { screen: SettingScreen },
})

這里聲明TabNavigator

createBottomTabNavigator(RouteConfigs, BottomTabNavigatorConfig)相當于iOS里面的TabBarController,屏幕下方的標簽欄

  • RouteConfigs(必選):路由配置對象是從路由名稱到路由配置的映射,告訴導航器該路由呈現(xiàn)什么。
  • BottomTabNavigatorConfig(可選):配置導航器的路由(如:默認首屏,navigationOptions,paths等)樣式(如,轉(zhuǎn)場模式mode、頭部模式等)。
const TabNavigator = createBottomTabNavigator(
    {
      Home: { screen: HomeStack },
      Settings: { screen: SettingsStack }
    },
    {
      navigationOptions: () => ({
        header: null
      }),
      defaultNavigationOptions: ({ navigation }) => ({
        tabBarLabel: ({ tintColor}) => {
          const { routeName } = navigation.state
          switch (routeName) {
            case 'Home':
              return <Text style={{ color: tintColor, fontSize: 12 }}>{'首頁'}</Text>
            case 'Settings':
              return <Text style={{ color: tintColor, fontSize: 12 }}>{'設(shè)置'}</Text>
          }
        },
        tabBarIcon: ({ focused, tintColor }) => {
            let urld 
            const { routeName } = navigation.state
            switch (routeName) {
                case 'Home':
                    return <Image source={{ uri: 'https://static.easyicon.net/preview/119/1191814.gif' }} style={[{height: 20, width: 20}]}/>    
                case 'Settings':
                    return <Image source={{ uri: 'https://static.easyicon.net/preview/121/1215319.gif' }} style={[{height: 20, width: 20}]}/>    
            }
        }
      }),
      tabBarOptions: {
        inactiveTintColor: 'gray',
      }
    }
)

最后設(shè)置路由并返回

const AppStack = createStackNavigator({
    Tabs: TabNavigator,
    Details: { screen: DetailsScreen },
  }, {
    defaultNavigationOptions: () => ({
    })
  })
export default createAppContainer(AppStack)

完整代碼如下

import React from 'react';
import { Text,Image} from 'react-native'

import HomeScreen from "./App";     
import DetailsScreen from "./detailScreen";
import SettingScreen from "./settingScreen";

import {
    createStackNavigator,
    createAppContainer,
    createBottomTabNavigator
} from 'react-navigation';

const HomeStack = createStackNavigator({
    Home: { screen: HomeScreen, }
})
const SettingsStack = createStackNavigator({
    Settings: { screen: SettingScreen },
})

const TabNavigator = createBottomTabNavigator(
    {
      Home: { screen: HomeStack },
      Settings: { screen: SettingsStack }
    },
    {
      navigationOptions: () => ({
        header: null
      }),
      defaultNavigationOptions: ({ navigation }) => ({
        tabBarLabel: ({ tintColor}) => {
          const { routeName } = navigation.state
          switch (routeName) {
            case 'Home':
              return <Text style={{ color: tintColor, fontSize: 12 }}>{'首頁'}</Text>
            case 'Settings':
              return <Text style={{ color: tintColor, fontSize: 12 }}>{'設(shè)置'}</Text>
          }
        },
        tabBarIcon: ({ focused, tintColor }) => {
            let urld 
            const { routeName } = navigation.state
            switch (routeName) {
                case 'Home':
                    return <Image source={{ uri: 'https://static.easyicon.net/preview/119/1191814.gif' }} style={[{height: 20, width: 20}]}/>    
                case 'Settings':
                    return <Image source={{ uri: 'https://static.easyicon.net/preview/121/1215319.gif' }} style={[{height: 20, width: 20}]}/>    
            }
        }
      }),
      tabBarOptions: {
        inactiveTintColor: 'gray',
      }
    }
)

const AppStack = createStackNavigator({
    Tabs: TabNavigator,
    Details:DetailsScreen,
  }, {
    defaultNavigationOptions: () => ({
    })
  })


export default createAppContainer(AppStack)


3.修改index.js入口

這里僅僅只是把入口改為navigation.js

import {AppRegistry} from 'react-native';
import Nav from './navigation.js';
import {name as appName} from './app.json';

AppRegistry.registerComponent(appName, () => Nav);

保存基本就能看到App的架子大概形成了

image

接下來要設(shè)置一下點擊事件,讓demo可以跳轉(zhuǎn)

先回到App.js頁面 設(shè)置首頁導航欄標題

static navigationOptions = ({ navigation }) => {
    const { params = {} } = navigation.state
    const onPressRightButtonFunc = params.openPublisher || function () { }
    return {
      title: '首頁',
    }
}

引入TouchableOpacity設(shè)置點擊事件

import {  TouchableOpacity } from "react-native";
 
 ...
 ...
 renderMovie({ item }) {
    const navigate = this.props.navigation;
    return (
      <TouchableOpacity activeOpacity={0.5} onPress={() => navigate.navigate('Details')} > //'Details'是之前在navigation.js聲明好的了
      ... //這里是之前item的UI代碼
      </TouchableOpacity>

到這里基本已經(jīng)完成了這個demo,其他的都是一些重復的UI工作也不贅述了,這是稍微優(yōu)化過的代碼和詳情頁,看不懂的可以根據(jù)根據(jù)這源碼來。

這里我的源碼是將基本組件都下好,下載運行即可,因為比較大先上傳到百度云。

鏈接: https://pan.baidu.com/s/1854tyx1R_Bjz4A57xvxn1g 提取碼: kgmb

網(wǎng)上的其他demo對新人都很不友好,需要安裝各個組件再運行起來,各種報錯容易勸退新人

后記

初衷是想讓新手快速的入門制作一個demo,后面發(fā)現(xiàn)還是需要一點web經(jīng)驗的,內(nèi)容有點多,說得沒那么細致的地方請見諒。后續(xù)會一直持續(xù)更新這個demo;

如果覺得對這篇文章對您有一點幫助的話,歡迎關(guān)注,戳這里 → 蘆葦科技

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

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

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