内容简介:循例都要介绍下React-Native,下面简称RN。 RN是让你使用Javascript编写的原生移动应用。它在设计原理上和React一致,通过声明式的组件机制来搭建丰富多彩的用户界面。其实文档上面写得很清楚,很友好的分了开发平台跟目标平台,基本上按着上面做就可以。我用的是自己的小米Note3真机开发的。按着官网的实例一步步做。安装很简单
循例都要介绍下React-Native,下面简称RN。 RN是让你使用Javascript编写的原生移动应用。它在设计原理上和React一致,通过声明式的组件机制来搭建丰富多彩的用户界面。
本文分为以下几点
- 搭建RN环境
- 封装一些公共方法,请求,本地存储
- 使用typescript
- 使用redux状态管理工具
- 使用iconfont
- BackHandler
- 页面效果
- 安卓打包APK
1. 搭建RN环境
- 安装
其实文档上面写得很清楚,很友好的分了开发平台跟目标平台,基本上按着上面做就可以。我用的是自己的小米Note3真机开发的。按着官网的实例一步步做。安装很简单
- 运行
react-native run-android
- 遇到的问题
- 找不到
ANDROID_HOME
环境变量
在当前终端下执行一次 source ~/.bash_profile
,问题解决。
- 成功运行时
- 调试
- react-native log-android
在终端会输出你console.log 出来的数据,不会影响程序的运行速度。
- Debug JS Remotely
摇晃手机弹出开发者选项菜单,选择Debug JS Remotely,浏览器会自动打开调试页面 http://localhost:8081/debugger-ui,Chrome 中并不能直接看到 App 的用户界面结构,而只能提供 console 的输出。
- React Developer Tools
这个插件可以看到页界面的插件布局,以及props等属性,但是这个貌似不能看console.log的内容。
- React Native Debugger 文档
由于我是在真机上面调试,所以需要配置 setupDevtools.js
reactDevTools.connectToDevTools({ isAppActive, host:'你电脑的ip地址', // Read the optional global variable for backward compatibility. // It was added in https://github.com/facebook/react-native/commit/bf2b435322e89d0aeee8792b1c6e04656c2719a0. port: window.__REACT_DEVTOOLS_PORT__, resolveRNStyle: require('flattenStyle'), }); 复制代码
接着,执行上面第二种的操作,打开 Debug JS Remotely
。这个调试比较爽,既有能console.log的也有UI布局的,但是我的会影响程序运行,会有卡顿情况。
- 拔掉数据线开发
如果你不想一直插着数据线,可以通过网络对你的程序进行调试。第一次运行react-native run-android时需要连着数据线,之后可以进入 Dev Settings
-> Debud server host & port for device
填上 [你开发电脑的ip]:8081
,在下一次重新运行的时候可以用 react-native start
运行。
- Hot Reload
局部刷新
- Live Reload
整个应用重新运行一次
2. 目录结构
- api: 相关的功能模块接口放在一个文件下面,例如订车相关的功能就放在aboutBookCar.js里面。
- assets: 放一些图片或者字体等静态资源。
- common: 放公共的方法。
- components: 放置通用组件,这里分功能组件跟UI组件。
- redux: redux相关。
- styles: 公共样式。
- types: ts声明文件。
- views: 放置各个主页面。
3. 各个主要模块
- http模块
// http.js 处理请求,储存token import { AsyncStorage } from 'react-native' import { login } from '../api/login' import axios from 'axios' async function checkStatus(response) { // loading // 如果http状态码正常,则直接返回数据 if (response) { if (response.data.status === 1) { // 成功 return response.data } else if (response.data.status === 2) { await setToken() return { status: 0, msg: '重新登录' } // 重新登录 } else if (response.data.status === 3) { // 数据格式解析异常 } else { // 异常状态下,把错误信息返回去 return { status: -404, msg: '网络异常' } } } } // 存放token到storage async function setToken() { const res = await login() AsyncStorage.setItem('token', res.token) return res.token } export const Post = async (url, params = {}) => { params = { ...params, lang: 'cn' } // 当不是登录接口时,从缓存中获取token,若不存在就调用setToken方法 if (url !== '登录接口') { const storageData = await AsyncStorage.getItem('token') params['token'] = storageData ? storageData : null if (!params.token) { params['token'] = await setToken() } } return axios .post(url, params) .then(async response => { const res = await checkStatus(response) return res.data }) .catch(function(error) { console.log(error) }) } 复制代码
- storage模块
// storage.js ,使用RN自带的AsyncStorage模块,用来储存token import {AsyncStorage} from 'react-native' // 保存数据 export const storeData = async (key, param) => { try { await AsyncStorage.setItem(key, JSON.stringify(param)) } catch (error) { // Error saving data } } // 读取数据 export const retrieveData = async key => { try { const value = await AsyncStorage.getItem(key) if (value !== null) { return value } } catch (error) { // Error retrieving data return null } } 复制代码
- 导航
导航使用React Navigation,由于需要用到抽屉导航跟普通的路由跳转,这里需要用到 stack navigator
和 Drawer navigation
结合。
- 创建导航
import React, { Component } from 'react' import { createStackNavigator, createDrawerNavigator } from 'react-navigation' import BookCar from 'views/BookCar' import UserInfo from 'views/UserInfo' import SelectPosition from 'views/SelectPosition' import ReturnPosition from 'views/ReturnPosition' import PositionDetail from 'views/PositionDetail' import BookingCarPage from 'views/BookingCarPage' // 侧面栏 import Journey from 'views/Journey/index' import ChargingRule from 'views/ChargingRule/index' import Recharge from 'views/Recharge/index' import Wallet from 'views/Wallet/index' // 抽屉内容的组件 import DrawerScreen from 'components/Ui/CustomDrawer/index' // 所有页面 const AllPage = createStackNavigator( //设置导航要展示的页面 { BookCar: { screen: BookCar }, UserInfo: { screen: UserInfo }, SelectPosition: { screen: SelectPosition }, ReturnPosition: { screen: ReturnPosition }, PositionDetail: { screen: PositionDetail }, BookingCarPage: { screen: BookingCarPage }, Journey: { screen: Journey }, ChargingRule: { screen: ChargingRule }, Recharge: { screen: Recharge }, Wallet: { screen: Wallet } }, //设置navigationOptions属性对象 { mode: 'card', //设置mode属性, headerMode: 'none' // 去掉头部 } ) // 结合抽屉跟所有页面 const DrawerNavigator = createDrawerNavigator( { Home: { screen: AllPage // 所有页面 } }, { contentComponent: DrawerScreen, // 用来呈现抽屉内容的组件 drawerWidth: 250 } ) export default class Navigator extends Component { constructor(props) { super(props) } render() { return <DrawerNavigator /> } } 复制代码
- 应用导航
// App.tsx import * as React from 'react' import Navigator from './src/components/Function/Navigator' export default class App extends React.Component { render() { return ( <Navigator /> ) } } 复制代码
4. typescript
之前做的项目用到typescript,感觉很不错,所以接入typescript。
-
install npm install react-native-typescript-transformer typescript -D
-
配置tsconfig.json
{ "compilerOptions": { "target": "es6", "module": "esnext", "moduleResolution": "node", "allowSyntheticDefaultImports": true, "rootDirs": ["./src"], "baseUrl": "./src", "jsx": "preserve", "alwaysStrict": true, "noUnusedLocals": true, "importHelpers": true, "experimentalDecorators": true, "lib": ["es7", "dom"], "skipLibCheck": true, "typeRoots": ["node", "node_modules/@types"], "outDir": "./lib" }, "exclude": ["node_modules"], "include": ["src/**/*"] } 复制代码
- 配置 the react native packager 在项目根目录创建文件
rn-cli.config.js
module.exports = { getTransformModulePath() { return require.resolve('react-native-typescript-transformer'); }, getSourceExts() { return ['ts', 'tsx']; } } 复制代码
- 加上react react-native react-navigation 的声明文件
npm install @types/react @types/react-native @types/react-navigation -D 复制代码
- 为了可以使用绝对路径,可以在需要引用的目录下创建package.json文件
例如:在api目录下创建package.json文件
{ "name": "api" } 复制代码
就可以使用 api/xxx
作为路径
- 运行验证
tsc 复制代码
5.redux
- install
npm install react-redux redux redux-actions redux-thunk -S 复制代码
- 修改App.tsx文件
import * as React from 'react' import { createStore, applyMiddleware, combineReducers } from 'redux' import { Provider } from 'react-redux' import thunk from 'redux-thunk' import Navigator from './src/components/Function/Navigator' import * as reducers from './src/redux/reducers' const createStoreWithMiddleware = applyMiddleware(thunk)(createStore) const reducer = combineReducers(reducers) const store = createStoreWithMiddleware(reducer) export default class App extends React.Component { render() { return ( <Provider store={store}> <Navigator /> </Provider> ) } } 复制代码
- actions
下面将车辆列表储存在store为例, 在 redux
目录下创建 actions
文件夹, actions
下包含 actionTypes.ts
和 CarAction.ts
actionTypes.ts
// 声明一下action类型 export const SET_CARLIST = 'SET_CARLIST' 复制代码
CarAction.ts
import * as types from './actionTypes' // action类型 // action方法 export function setCarList(carList) { return { type: types.SET_CARLIST, carList } } 复制代码
- reducers
在 redux
目录下创建 reducers
文件夹, reducers
下包含 index.ts
和 CarReducer.ts
index.ts
import CarReducer from './CarReducer' export { CarReducer } 复制代码
CarReducer.ts
import * as types from '../actions/actionTypes' const initialState = { carList: [], } export default function counter(state = initialState, action) { switch (action.type) { case types.SET_CARLIST: return { ...state, carList: action.carList } default: return state } } 复制代码
- 使用,现在已经创建好基本操作,下面就是获取store跟操作action
import { connect } from 'react-redux' import { bindActionCreators } from 'redux' import * as CarAction from '../../redux/actions/CarAction' import * as UserInfo from '../../redux/actions/UserInfo' ... // 由于使用的ts,我们可以先定义好interface interface IStoreProps { // 方法 actions?: { setCarList: (v: CarStore.ICarItem[]) => void } // 数据 state?: { carList: CarStore.ICarItem[] } } class BookCar extends React.Component<IStoreProps> { ... // 可以通过 this.props.actions.setCarList()来执行action方法 // 同样,可以通过 this.props.state.carList来获取数据 } const StateToPoprs = state => ({ state: { ...state.CarReducer } }) const dispatchToProps = dispatch => ({ actions: bindActionCreators({ ...CarAction }, dispatch) }) export default connect( StateToPoprs, dispatchToProps )(BookCar) 复制代码
6. 使用iconfont
- install
npm install react-native-vector-icons --save 复制代码
- 配置
android/app/build.gradle
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle" project.ext.vectoricons = [ iconFontNames: [ 'iconfont.ttf'] // Name of the font files you want to copy ] 复制代码
- 获取.ttf文件
可以从iconfont上面获取,可以在上面新建一个项目,然后将需要的图标放到项目里面,点击下载至本地。
- 文件处理
下载完毕后,将 iconfont.ttf
和 iconfont.css
文件放在 src/assets/fonts/
目录下,同时将 iconfont.ttf
文件放在 android/app/src/main/assets/fonts/目录下
// iconfont.css ... .icon-jifei:before { content: "\e602"; } .icon-quan:before { content: "\e603"; } ... 复制代码
我们需要得到一个json文件,内容如下
{ "icon-jifei": 58882, "icon-quan": 58883, ... } 复制代码
其实就是将 iconfont.css
里面的样式名跟conten的值的十进制提取出来,手动转换比较麻烦,我们增加一个自动转换的脚本。 在工程根目录下新建 tools/getIconfontJson/getIconfontJson.js
const path = require('path') const oldPath = path.join('./src/assets/fonts/iconfont.css') const newPath = path.join('./src/assets/fonts/iconfont.json') var gen = (module.exports = function() { const readline = require('readline') const fs = require('fs') const fRead = fs.createReadStream(oldPath) const fWrite = fs.createWriteStream(newPath, { flags: 'w+', defaultEncoding: 'utf8' }) const objReadLine = readline.createInterface({ input: fRead }) var ret = {} objReadLine.on('line', line => { line = line && line.trim() if (!line.includes(':before') || !line.includes('content')) return var keyMatch = line.match(/\.(.*?):/) var valueMatch = line.match(/content:.*?\\(.*?);/) var key = keyMatch && keyMatch[1] var value = valueMatch && valueMatch[1] value = parseInt(value, 16) key && value && (ret[key] = value) }) objReadLine.on('close', () => { console.log('readline close') fWrite.write(JSON.stringify(ret), 'utf8') }) }) gen() 复制代码
运行脚本之后会在 assets/fonts/
目录下生成iconfont.json文件。
最后,我们在 components
目录下创建一个公共组件 IconFont
// IconFont/index.tsx import { createIconSet } from 'react-native-vector-icons' const glyphMap = require('assets/fonts/iconfont.json') const IconFont = createIconSet(glyphMap, 'iconfont', 'iconfont.ttf') export default IconFont 复制代码
- usage
import Icon from 'components/Ui/IconFont/index' ... export default class CarList extends React.Component { ... render() { <Icon name="icon-jifei" size={50} color="red" /> } } 复制代码
7.BackHandler
- 监听设备上后退事件,当我选择了车辆点的时候弹出底部框,此时,点击后退按钮的时候应该是低部框消失。
- 当切换到别的页面的时候,应该销毁之前的监听事件。
... handleBackPress = () => {} // 监听事件 backHandlerListen = () => { BackHandler.addEventListener('hardwareBackPress', this.handleBackPress) } // 销毁监听 destroyBackHandlerListen = () => { BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress) } // 重新进入这个页面触发 didFocus = () => { this.props.navigation.addListener('didFocus', payload => { this.backHandlerListen() }) } // 这个页面失去焦点触发 didBlur = () => { this.props.navigation.addListener('didBlur', payload => { this.destroyBackHandlerListen() }) } componentDidMount() { this.backHandlerListen() this.didFocus() this.didBlur() } ... 复制代码
8. 页面效果
页面暂时比较粗糙,demo性质的效果,实现主要租车功能。
9. 安卓打包APK文档
- 使用
keytool
生成秘钥
keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000 复制代码
- 在工程目录下
android/gradle.properties
加上以下配置
// *****是刚才生成秘钥时候填写的密码 MYAPP_RELEASE_STORE_FILE=my-release-key.keystore MYAPP_RELEASE_KEY_ALIAS=my-key-alias MYAPP_RELEASE_STORE_PASSWORD=***** MYAPP_RELEASE_KEY_PASSWORD=***** 复制代码
- 配置bulid.gradle
... android { ... defaultConfig { ... } signingConfigs { release { if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) { storeFile file(MYAPP_RELEASE_STORE_FILE) storePassword MYAPP_RELEASE_STORE_PASSWORD keyAlias MYAPP_RELEASE_KEY_ALIAS keyPassword MYAPP_RELEASE_KEY_PASSWORD } } } buildTypes { release { ... signingConfig signingConfigs.release } } } ... 复制代码
- 生成APK包
$ cd android $ ./gradlew assembleRelease 复制代码
生成的apk文件位于 生成的 APK 文件位于android/app/build/outputs/apk/app-release.apk
, 可以直接将apk放到手机安装 5. 遇到报错
// error Couldn't follow symbolic link' when testing release build for Android // fix rm -rf node_modules && npm install 复制代码
以上所述就是小编给大家介绍的《ReactNative仿某租车软件》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 某租车系统Java代码审计之后台注入漏洞
- 上海首批纯电动出租车9月亮相 一键报警等功能成亮点
- “软件吃掉软件”:程序员未来会消失吗?
- 百度软件中心版 PuTTY 被曝恶意捆绑软件
- 软件复用导致的软件依赖问题 - research!rsc
- 『互联网架构』软件架构-软件系统设计(一)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Writing Windows VxDs and Device Drivers, Second Edition
Karen Hazzah / CMP / 1996-01-12 / USD 54.95
Software developer and author Karen Hazzah expands her original treatise on device drivers in the second edition of "Writing Windows VxDs and Device Drivers." The book and companion disk include the a......一起来看看 《Writing Windows VxDs and Device Drivers, Second Edition》 这本书的介绍吧!