使用JavaScript和React编写原生移动应用
React Native应用是真正的移动应用,React Native产出的并不是“网页应用”, 或者说“HTML5应用”,又或者“混合应用”。 最终产品是一个真正的移动应用,从使用感受上和用Objective-C或Java编写的应用相比几乎是无法区分的。 React Native所使用的基础UI组件和原生应用完全一致。 你要做的就是把这些基础组件使用JavaScript和React的方式组合起来。
英文官网:https://reactnative.dev/
中文:https://reactnative.cn/
https://react-native.org.cn/
http://expo.io
模拟测试环境,增强rn开发的能力,提供组件
假设已安装 Node.js 10 LTS或更高版本,则可以使用npm安装Expo CLI命令行实用程序:
npm install -g expo-cli
或者
yarn global add expo-cli
然后运行以下命令,创建一个名为“rn-basics”本地项目:
expo init rn-basics
cd rn-basics
npm start # 也可以使用命令: expo start
此时会启动一个开发服务器。
在iOS或Android手机上安装[Expo](https://docs.expo.io/versions/v36.0.0/get-started/installation/ Expo)客户端应用程序,并连接到与计算机相同的无线网络(Wifi热点)。在Android上,使用Expo应用程序从终端扫描二维码以打开项目。在iOS上,按照屏幕上的说明(一般为使用相机扫描)获取链接。
现在你已经成功运行了应用程序,让我们修改一下代码试试。在文本编辑器中打开 App.js 并编辑一些行。保存更改后,应用程序会自动重新加载。
React Native 与 React类似,但它使用原生(native)组件而不是基于浏览器(web)组件作为构建块。因此,要了解 React Ntive 应用程序的基本结构,您需要了解一些基本的 React 概念,如JSX、组件、状态和属性。如果你已经了解 React,那么你仍然需要学习一些 React Native 特定的东西,比如 原生(Native) 组件。本教程面向所有人群,无论你是否有 React 经验。
编程界的老习惯,先来个 Hello World 尝尝鲜:
import React, {
Component } from 'react';
import {
Text, View } from 'react-native';
export default class HelloWorldApp extends Component {
render() {
return (
<View style={
{
flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Hello, world!</Text>
</View>
);
}
}
如果你感到好奇,这不就是React程序吗?是的,可以直接在web模拟器中运行这段代码。也可以将其粘贴到App.js文件中,以便在本地计算机上创建真正的原生应用程序。
这里的一些内容看来可能不像 JavaScript。别慌。这就是未来。
首先,ES2015(也称为ES6)是对JavaScript的一系列改进,ECMAScript 现在是官方标准的一部分,但还没有得到所有浏览器的支持。React Native ships 支持 ES2015,因此你可以使用这些内容而不必担心兼容性。上述示例中的import、from、class 和 extends 都是ES2015的特性。如果你不熟悉ES2015,你也可以通过阅读本教程中的示例代码来了解它。
在这个代码示例中,另一个不寻常的事情是 这段代码定义了HelloWorldApp,这是一个新组件。当你在构建一个 React 本地应用程序时,你将大量地生成新组件。你在屏幕上看到的任何东西都是某种组件。 大多数组件在创建时都可以使用不同的参数进行自定义。这些创建参数称为props,是properties的缩写。 注意 我们在Greeting组件中将name作为一个属性来定制,这样可以复用这一组件来制作各种不同的“问候语”。上面的例子把Greeting组件写在 JSX 语句中,用法和内置组件并无二致——这正是 React 体系的魅力所在——如果你想搭建一套自己的基础 UI 框架,那就放手做吧! 上面的例子出现了一个新的名为 仅仅使用 我们使用两种数据来控制一个组件:props 和 state。props是在父组件中指定,而且一经指定,在被指定的组件的生命周期中则不再改变。对于需要改变的数据,我们需要使用state。 一般来说,你需要在class中声明一个state对象,然后在需要修改时调用setState方法。 假如我们需要制作一段不停闪烁的文字。文字内容本身在组件创建时就已经指定好了,所以文字内容应该是一个prop。而文字的显示或隐藏的状态(快速的显隐切换就产生了闪烁的效果)则是随着时间变化的,因此这一状态应该写到state中。 实际开发中,我们一般不会在定时器函数(setInterval、setTimeout 等)中来操作 state。典型的场景是在接收到服务器返回的新数据,或者在用户输入数据之后。你也可以使用一些“状态容器”比如 每次调用setState时,BlinkApp 都会重新执行 render 方法重新渲染。这里我们使用定时器来不停调用setState,于是组件就会随着时间变化不停地重新渲染。 State 的工作原理和 React.js 完全一致,所以对于处理 state 的一些更深入的细节,你可以参阅React.Component API。 提示一些初学者应该牢记的要点: TextInput是一个允许用户输入文本的基础组件。它有一个名为onChangeText的属性,此属性接受一个函数,而此函数会在文本变化时被调用。另外还有一个名为onSubmitEditing的属性,会在文本被提交后(用户按下软键盘上的提交键)调用。 假如我们要实现当用户输入时,实时将其以单词为单位翻译为另一种文字。我们假设这另一种文字来自某个吃货星球,只有一个单词: 。所以"Hello there Bob"将会被翻译为""。 移动应用上的用户交互基本靠“摸”。当然,“摸”也是有各种姿势的:在一个按钮上点击,在一个列表上滑动,或是在一个地图上缩放。React Native 提供了可以处理常见触摸手势(例如点击或滑动)的组件, 以及可用于识别更复杂的手势的完整的手势响应系统。 https://reactnative.cn/docs/handling-touches 具体使用哪种组件,取决于你希望给用户什么样的视觉反馈: 一般来说,你可以使用 在 如果你想在处理点击事件的同时不显示任何视觉反馈,则需要使用 某些场景中你可能需要检测用户是否进行了长按操作。可以在上面列出的任意组件中使用 在 React Native 中,你并不需要学习什么特殊的语法来定义样式。我们仍然是使用 JavaScript 来写样式。所有的核心组件都接受名为 style 的属性。这些样式名基本上是遵循了 web 上的 CSS 的命名,只是按照 JS 的语法要求使用了驼峰命名法,例如将 background-color 改为 backgroundColor。 style属性可以是一个普通的 JavaScript 对象。这是最简单的用法,因而在示例代码中很常见。你还可以传入一个数组—— 实际开发中组件的样式会越来越复杂,我们建议使用 常见的做法是按顺序声明和使用style属性,以借鉴 CSS 中的“层叠”做法(即后声明的属性会覆盖先声明的同名属性)。 组件的高度和宽度决定了其在屏幕上显示的尺寸。 最简单的给组件设定尺寸的方式就是在样式中指定固定的 这样给组件设置尺寸也是一种常见的模式,比如要求在不同尺寸的屏幕上都显示成一样的大小。 在组件样式中使用flex可以使其在可利用的空间中动态地扩张或收缩。一般而言我们会使用flex:1来指定某个组件扩张以撑满所有剩余的空间。如果有多个并列的子组件使用了flex:1,则这些子组件会平分父容器中剩余的空间。如果这些并列的子组件的flex值不一样,则谁的值更大,谁占据剩余空间的比例就更大(即占据剩余空间的比等于并列组件间flex值的比)。 组件能够撑满剩余空间的前提是其父容器的尺寸不为零。如果父容器既没有固定的width和height,也没有设定flex,则父容器的尺寸为零。其子组件如果使用了flex,也是无法显示的。 我们在 React Native 中使用 flexbox 规则来指定某个组件的子元素的布局。Flexbox 可以在不同屏幕尺寸上提供一致的布局结构。 一般来说,使用 React Native 中的 Flexbox 的工作原理和 web 上的 CSS 基本一致,当然也存在少许差异。首先是默认值不同:flexDirection的默认值是column而不是row,而flex也只能指定一个数字值。 flex 属性决定元素在主轴上如何填满可用区域。整个区域会根据每个元素设置的flex属性值被分割成多个部分。 在下面的例子中,在设置了 在组件的 style 中指定 布局方向指定层次结构中的子项和文本的布局方向。布局方向也会影响边起点和终点所指的对象。默认情况下,React Native布局使用LTR布局方向。在这种模式下,开始是指左边,结束是指右边。 LTR(默认值)文本和子级,并从左到右排列。应用的边距和填充元素的开头应用于左侧。 从右到左排列的RTL文本和子项。应用的边距和填充元素的开头应用于右侧。 在组件的 style 中指定 justifyContent 可以决定其子元素沿着主轴的排列方式。子元素是应该靠近主轴的起始端还是末尾段分布呢?亦或应该均匀分布?对应的这些可选项有:flex-start、center、flex-end、space-around、space-between 以及 space-evenly。 https://reactnative.cn/docs/using-a-scrollview https://reactnative.cn/docs/flatlist FlatList组件用于显示一个垂直的滚动列表,其中的元素之间结构近似而仅数据不同。 FlatList更适于长列表数据,且元素个数可以增删。和 FlatList组件必须的两个属性是 下面是一个上拉请求的案例 https://reactnative.cn/docs/platform-specific-code React Native 提供了两种方法来区分平台: 技术栈: ReactNative,TypeScript,Mobx,expo 项目搭建:见上面环境搭建流程 在项目环境命令行里安装 tabbar 导航器,详细内容可参见 react-native-tab-navigator 官网, 如果像下图这样引入图片文件,ts不会把图片当作一个模块,所以会出现红色的波浪线提示 ts.config.js rn中是没有css文件的,所以我们需要创建一个js文件来写样式,然后使用的时候导入这个js文件,如果使用的时ts开发的话,js文件引入也是会出现红色的提示,这时候我们可以在 引入js文件和 https://rn.mobile.ant.design/index-cn https://docs.expo.io/versions/v38.0.0/sdk/camera/ react-navigation https://reactnavigation.org/ 注意 :使用这个插件子组件可以拿到路由信息,但是子子组件拿不到路由信息,这时候我们需要把这个路由信息存到一个属性里,可以使用context组件之间共享信息 https://reactnative.cn/docs/flatlist#onrefresh https://reactnative.cn/docs/flatlist#onendreached 去微信官方网址https://open.weixin.qq.com/,注册移动应用,填写相关信息,获取到相关到key 其中需要注意到是,安卓需要先生成release包,然后使用 Gen_Signature_Android2.apk 获取到app的签名 安卓app包名地址:android/app/build.gradle文件中的applicationId 字段 打开安卓获取签名软件,输入包名,会得到一个签名,这就是微信SDK需要的一个东西 这里有篇介绍非常详细的文章,流程介绍的很详细:https://www.ahwgs.cn/wechat-rn-share.html RN具体实现,可以用第三方模块:react-native-wechat, 使用方法参见: https://www.npmjs.com/package/react-native-wechat App.json(https://docs.expo.io/workflow/configuration/)
。这是JSX——一种在JavaScript中嵌入XML的语法。许多框架使用一种专门的模板语言,允许您在标记语言中嵌入代码。在React中,没有使用模板。JSX允许您在代码中编写标记语言。它看起来像web上的HTML,但这里使用的是React组件,而不是像这样的 HTML 标签。在本例中, 是一个内置组件,它显示一些文本,类似于
。
组件
WebView组件
WebView
创建一个原生的 WebView,可以用于访问一个网页。相当于iframeimport {
WebView } from "react-native-webview"
Props
例如,一个基本的React Native 组件 Image。创建图像时,可以使用名为source的属性来控制它显示的图像。import React, {
Component } from 'react'
import {
Image } from 'react-native'
export default class Bananas extends Component {
render() {
let pic = {
uri: 'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'
}
return (
<Image source={
pic} style={
{
width: 193, height: 110}}/>
)
}
}
{pic}
周围的大括号-它们将变量pic
嵌入到JSX
中。您可以将任何JavaScript表达式放在JSX中的大括号中。
你自己的组件也可以使用 props。这允许你创建一个在应用程序中的许多不同位置使用的组件,每个组件的属性可以略有不同,获取值可以在渲染函数中引用This.props。下面是一个例子:import React, {
Component } from 'react';
import {
Text, View } from 'react-native';
class Greeting extends Component {
render() {
return (
<View style={
{
alignItems: 'center'}}>
<Text>Hello {
this.props.name}!</Text>
</View>
);
}
}
export default class LotsOfGreetings extends Component {
render() {
return (
<View style={
{
alignItems: 'center', top: 50}}>
<Greeting name='张三' />
<Greeting name='李四' />
<Greeting name='王五' />
</View>
);
}
}
View
的组件。View
常用作其他组件的容器,来帮助控制布局和样式。props
和基础的Text
、Image
以及 View
组件,你就已经足以编写各式各样的 UI 组件了。要学习如何动态修改你的界面,那就需要进一步学习 State(状态)的概念。State
import React, {
Component } from 'react';
import {
Text, View } from 'react-native';
class Blink extends Component {
// 声明state对象
state = {
isShowingText: true };
componentDidMount() {
// 每1000毫秒对showText状态做一次取反操作
setInterval(() => {
this.setState({
isShowingText: !this.state.isShowingText
});
}, 1000);
}
render() {
// 根据当前showText的值决定是否显示text内容
if (!this.state.isShowingText) {
return null;
}
return (
<Text>{
this.props.text}</Text>
);
}
}
export default class BlinkApp extends Component {
render() {
return (
<View>
<Blink text='I love to blink' />
<Blink text='Yes blinking is so great' />
<Blink text='Why did they ever take this out of HTML' />
<Blink text='Look at me look at me look at me' />
</View>
);
}
}
Redux
来统一管理数据流。
一切界面变化都是状态state变化
state的修改必须通过setState()方法
this.state.likes = 100; // 这样的直接赋值修改无效!
setState 是一个 merge 合并操作,只修改指定属性,不影响其他属性
setState 是异步操作,修改不会马上生效处理文本输入
import React, {
Component, useState } from 'react';
import {
Text, TextInput, View } from 'react-native';
export default function PizzaTranslator() {
const [text, setText] = useState('');
return (
<View style={
{
padding: 10}}>
<TextInput
style={
{
height: 40}}
placeholder="Type here to translate!"
onChangeText={
text => setText(text)}
defaultValue={
text}
/>
<Text style={
{
padding: 10, fontSize: 42}}>
{
text.split(' ').map((word) => word && '').join(' ')}
</Text>
</View>
);
}
处理触摸事件
<Button
onPress={
() => {
Alert.alert("你点击了按钮!");
}}
title="点我!"
/>
import React, {
Component } from 'react';
import {
Alert, Button, StyleSheet, View } from 'react-native';
export default class ButtonBasics extends Component {
_onPressButton() {
Alert.alert('You tapped the button!')
}
render() {
return (
<View style={
styles.container}>
<View style={
styles.buttonContainer}>
<Button
onPress={
this._onPressButton}
title="Press Me"
/>
</View>
<View style={
styles.buttonContainer}>
<Button
onPress={
this._onPressButton}
title="Press Me"
color="#841584"
/>
</View>
<View style={
styles.alternativeLayoutButtonContainer}>
<Button
onPress={
this._onPressButton}
title="This looks great!"
/>
<Button
onPress={
this._onPressButton}
title="OK!"
color="#841584"
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
buttonContainer: {
margin: 20
},
alternativeLayoutButtonContainer: {
margin: 20,
flexDirection: 'row',
justifyContent: 'space-between'
}
})
Touchable 系列组件
这个组件的样式是固定的。所以如果它的外观并不怎么搭配你的设计 , 那就需要使用TouchableOpacity
或是TouchableNativeFeedback
组件来定制自己所需要的按钮,视频教程如何制作一个按钮讲述了完整的过程。或者你也可以在 github.com
网站上搜索'react native button'
来看看社区其他人的作品。
TouchableHighlight
来制作按钮或者链接。注意此组件的背景会在用户手指按下时变暗。Android
上还可以使用TouchableNativeFeedback
,它会在用户手指按下时形成类似墨水涟漪的视觉效果。TouchableOpacity
会在用户手指按下时降低按钮的透明度,而不会改变背景的颜色。TouchableWithoutFeedback
。onLongPress
属性来实现。
import React, {
Component } from 'react';
import {
Alert, Platform, StyleSheet, Text, TouchableHighlight, TouchableOpacity, TouchableNativeFeedback, TouchableWithoutFeedback, View } from 'react-native';
export default class Touchables extends Component {
_onPressButton() {
Alert.alert('You tapped the button!')
}
_onLongPressButton() {
Alert.alert('You long-pressed the button!')
}
render() {
return (
<View style={
styles.container}>
//此组件的背景会在用户手指按下时变暗。
<TouchableHighlight onPress={
this._onPressButton} underlayColor="white">
<View style={
styles.button}>
<Text style={
styles.buttonText}>TouchableHighlight</Text>
</View>
</TouchableHighlight>
//会在用户手指按下时降低按钮的透明度,而不会改变背景的颜色
<TouchableOpacity onPress={
this._onPressButton}>
<View style={
styles.button}>
<Text style={
styles.buttonText}>TouchableOpacity</Text>
</View>
</TouchableOpacity>
//会在用户手指按下时形成类似墨水涟漪的视觉效果。
<TouchableNativeFeedback
onPress={
this._onPressButton}
background={
Platform.OS === 'android' ? TouchableNativeFeedback.SelectableBackground() : ''}>
<View style={
styles.button}>
<Text style={
styles.buttonText}>TouchableNativeFeedback</Text>
</View>
</TouchableNativeFeedback>
//在处理点击事件的同时不显示任何视觉反馈
<TouchableWithoutFeedback
onPress={
this._onPressButton}
>
<View style={
styles.button}>
<Text style={
styles.buttonText}>TouchableWithoutFeedback</Text>
</View>
</TouchableWithoutFeedback>
<TouchableHighlight onPress={
this._onPressButton} onLongPress={
this._onLongPressButton} underlayColor="white">
<View style={
styles.button}>
<Text style={
styles.buttonText}>Touchable with Long Press</Text>
</View>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
paddingTop: 60,
alignItems: 'center'
},
button: {
marginBottom: 30,
width: 260,
alignItems: 'center',
backgroundColor: '#2196F3'
},
buttonText: {
textAlign: 'center',
padding: 20,
color: 'white'
}
})
样式
在数组中位置居后的样式对象比居前的优先级更高,这样你可以间接实现样式的继承。
StyleSheet.create
来集中定义组件的样式。比如像下面这样:import React, {
Component } from 'react'
import {
StyleSheet, Text, View } from 'react-native'
const styles = StyleSheet.create({
bigBlue: {
color: 'blue',
fontWeight: 'bold',
fontSize: 30,
},
red: {
color: 'red',
},
});
export default class LotsOfStyles extends Component {
render() {
return (
<View>
<Text style={
styles.red}>just red</Text>
<Text style={
styles.bigBlue}>just bigBlue</Text>
<Text style={
[styles.bigBlue, styles.red]}>bigBlue, then red</Text>
<Text style={
[styles.red, styles.bigBlue]}>red, then bigBlue</Text>//使用数组所有的样式都可以生效
</View>
);
}
}
宽度(Width) 和 高度(Height)
指定宽高
width
和 height
。React Native 中的尺寸都是无单位的,表示的是与设备像素密度无关的逻辑像素点。import React, {
Component } from 'react'
import {
View } from 'react-native'
export default class FixedDimensionsBasics extends Component {
render() {
return (
<View>
<View style={
{
width: 50, height: 50, backgroundColor: 'powderblue'}} />
<View style={
{
width: 100, height: 100, backgroundColor: 'skyblue'}} />
<View style={
{
width: 150, height: 150, backgroundColor: 'steelblue'}} />
</View>
)
}
}
弹性(Flex)宽高
import React, {
Component } from 'react'
import {
View } from 'react-native'
export default class FlexDimensionsBasics extends Component {
render() {
return (
// 试试去掉父 View 中的`flex: 1`。
// 则父View不再具有尺寸,因此子组件也无法再撑开。
// 然后再用`height: 300` 来代替父 View 的 `flex: 1` 试试看?
<View style={
{
flex: 1}}>
<View style={
{
flex: 1, backgroundColor: 'powderblue'}} />
<View style={
{
flex: 2, backgroundColor: 'skyblue'}} />
<View style={
{
flex: 3, backgroundColor: 'steelblue'}} />
</View>
)
}
}
使用 flexbox 布局
flexDirection
、alignItems
和 justifyContent
三个样式属性就已经能满足大多数布局需求。
Flex
flex: 1
的容器view中,有红色,黄色和绿色三个子 view
。红色 view
设置了 flex: 1
,黄色 view
设置了 flex: 2
,绿色 view
设置了 flex: 3
。1+2+3 = 6
,这意味着红色 view
占据整个区域的 1/6
,黄色 view
占据整个区域的 2/6
,绿色 view
占据整个区域的3/6
。Flex Direction
flexDirection
可以决定布局的主轴。子元素是应该沿着水平轴 (row) 方向排列,还是沿着竖直轴 (column) 方向排列呢?默认值是竖直轴 (column) 方向。import React, {
Component } from 'react'
import {
View } from 'react-native'
export default class FlexDirectionBasics extends Component {
render() {
return (
// 尝试把`flexDirection`改为`column`看看
<View style={
{
flex: 1, flexDirection: 'row'}}>
<View style={
{
width: 50, height: 50, backgroundColor: 'powderblue'}} />
<View style={
{
width: 50, height: 50, backgroundColor: 'skyblue'}} />
<View style={
{
width: 50, height: 50, backgroundColor: 'steelblue'}} />
</View>
)
}
}
Layout Direction
Justify Content
import React, {
Component } from 'react'
import {
View } from 'react-native'
export default class JustifyContentBasics extends Component {
render() {
return (
// 尝试把`justifyContent`改为`center`看看
// 尝试把`flexDirection`改为`row`看看
<View style={
{
flex: 1,
flexDirection: 'column',
justifyContent: 'space-between',
}}>
<View style={
{
width: 50, height: 50, backgroundColor: 'powderblue'}} />
<View style={
{
width: 50, height: 50, backgroundColor: 'skyblue'}} />
<View style={
{
width: 50, height: 50, backgroundColor: 'steelblue'}} />
</View>
)
}
}
滚动视图
ScrollView是一个通用的可滚动的容器,你可以在其中放入多个组件和视图,而且这些组件并不需要是同类型的。ScrollView 不仅可以垂直滚动,还能水平滚动(通过horizontal属性来设置)。长列表
React Native 提供了几个适用于展示长列表数据的组件,一般而言我们会选用FlatList
或是SectionList
。ScrollView
不同的是,FlatList
并不立即渲染所有元素,而是优先渲染屏幕上可见的元素。data
和renderItem
。data
是列表的数据源,而renderItem
则从数据源中逐个解析数据,然后返回一个设定好格式的组件来渲染。import React, {
Component } from 'react';
import {
FlatList, Image, Text, View, StyleSheet,Alert } from 'react-native';
export default class App extends Component {
state = {
data: [],
loaded: false,
limit:10,
page:1,
ismore:true
}
componentDidMount() {
this.fetchData()
}
fetchData() {
fetch(`http://192.168.2.1:9099/coming?_limit=${
this.state.limit}&_page=${
this.state.page}`).then((response) => response.json())
.then((result) => {
console.log(result.length);
this.setState({
data: this.state.data.concat(result),
loaded: true,
ismore:result.length===this.state.limit?true:false
})
})
}
renderLoadingView() {
return (
<View style={
styles.container}>
<Text> loading movies ....</Text>
</View>
)
}
renderMovieItem({
item }) {
return (
<View style={
styles.ItemContainer}>
<Image style={
styles.img} source={
{
uri: item.img.replace('w.h', '100.150') }}></Image>
<View style={
styles.rigthContainer}>
<Text>{
item.nm}</Text>
</View>
</View>
)
}
render() {
if (!this.state.loaded) {
return this.renderLoadingView()
}
return (
<View style={
styles.container}>
<FlatList data={
this.state.data}
renderItem={
this.renderMovieItem}
onEndReachedThreshold={
0.2}
onEndReached={
()=>{
console.log('reach end')
if(!this.state.ismore){
Alert.alert('')
Alert.alert(
'提示',
'没有更多的数据了',
[
{
text: 'OK', onPress: () => console.log('OK Pressed')},
],
{
cancelable: false }
)
return;
}
this.setState({
page:this.state.page +1
},()=>{
this.fetchData()
})
}}
>
</FlatList>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding:5
// alignItems: "center"
},
ItemContainer: {
flex: 1,
flexDirection: "row",
marginTop:5
},
rigthContainer: {
marginLeft:5,
backgroundColor:'#efefef',
flex: 1
},
img: {
width: 100,
height: 150
}
})
websocket
client
startSocket() {
const ws = new WebSocket('ws://192.168.2.1:8081')
ws.onopen = () => {
ws.send('hello')
}
ws.onmessage = (msg) => {
console.log(msg)
alert(msg.data)
}
ws.onerror =()=>{
}
ws.onclose=()=>{
}
}
server
yarn add ws
const websocket = require('ws');
const ws = new websocket.Server({
port: 8081 })
let clients = {
}
let clientname = 0;
ws.on('connection', (client) => {
client.name = ++clientname;
clients[clientname] = client;
client.on('message', (msg) => {
broadcast(client, msg)
})
client.on('close', () => {
delete clients[client.name];
console.log(client.name + '下线了')
})
})
function broadcast(client, msg) {
for (let key in clients) {
clients[key].send(client.name + '说:' + msg)
}
}
特定平台代码
Platform
模块.项目
expo https://docs.expo.io/1、引入 react-native-tab-navigator
注意不要忘了安装声明文件
yarn add react-native-tab-navigator -S
yarn add @types/react-native-tab-navigator
//声明文件2、typescript中导入图片,需要使用声明文件
所以我们需要在根目录创建一个image.d.ts
的声明文件,之后再导入图片就可以以模块导入
3、别名配置
yarn add babel-plugin-root-import -D
//bable.config.js
module.exports = function(api) {
api.cache(true);
return {
...
plugins: [
[
"babel-plugin-root-import",
{
rootPathSuffix: "./",
rootPathPrefix: "@/"
}
]
]
};
};
{
"compilerOptions": {
...
"baseUrl": ".",
"paths": {
"@/*": ["*"]
}
}
}
4、引入样式
tsconfig.json
文件中配置允许js1)使用
StyleSheet
设置cssexpo-device
(判断设备类型,得先安装 yarn add expo-device
)2)使用styled-components
5、组件 Ant-design-mobile-rn 快速上手
6、拍照
7、路由
8、下拉刷新
使用onRefresh
和refreshing
9、分页请求
10、微信分享
打包
expo build:android
expo build:ios
{
"expo": {
"name": "rn-cookbooks",
"slug": "rn-cookbooks",
"platforms": [
"ios",
"android",
"web"
],
"sdkVersion": "37.0.0",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"android": {
"package": "com.qianphone.gp18",
"versionCode": 1
},
"ios": {
"bundleIdentifier": "com.qianphone.gp18"
}
}
}
总结