2019-07-18 13:18:28 csdn100861 阅读数 53
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57874 人正在学习 去看看 李宁

#前端开发技术的调研

标签(空格分隔): 未分类



标签: 移动开发
2018.1.6


名词 链接 描述
Cordova http://cordova.apache.org/ Hybrid App中间件
ios-deploy https://github.com/phonegap/ios-deploy 使用命令行部署iOS项目
PhoneGap https://phonegap.com/ 使用Web快速开发、打包Hybrid APP
Node.js https://nodejs.org/en/ 基于 Chrome V8 引擎的 JavaScript 运行环境。
npm https://www.npmjs.com/ Node.js的包管理器
Git https://git-scm.com/ 分布式版本控制库
ionic http://ionicframework.com/ 基于Angular的H5框架
Angular https://angularjs.org/ 优秀的前端JS框架
Weex https://angularjs.org/ 一个使用 Web 开发体验来开发高性能原生应用的框架
Vue.js https://vuejs.org/ 一套用于构建用户界面的渐进式框架
Rax https://alibaba.github.io/rax/ 一个通用的跨容器的渲染引擎
React.js https://reactjs.org/ 构建用户界面的JavaScript库
Flex (Flexible Box) https://www.w3.org/TR/css-flexbox-1/ 简便、完整、响应式地实现各种页面布局方案
PhoneGap100 http://www.phonegap100.com/ Hybrid App社区

本文旨在了解React Native 、Weex和 Cordova 的差异,评估几种种前端技术实现的优缺点。关于Cordova我们在之前有过介绍,本篇再简单介绍ReactNative和Weex。

React.js

概述

React.js 不是一个框架,它只是一个库。它只提供 UI(view)层面的解决方案。在实际的项目当中,它并不能解决我们所有的问题,需要结合其它的库,例如 Redux、React-router等来协助提供完整的解决方法。
类似的还有 Vue.js

在web开发中,Facebook认为现有的MVC框架无法满足他们的扩展需求,由于他们非常巨大的代码库和庞大的组织,使得MVC很快变得非常复复杂,每当需要添加一项新的功能或特性时,系统的复杂度就成级数增长,致使代码变得脆弱和不可预测,结果导致他们的MVC正在土崩瓦解。所以认为MVC不适合大规模应用,当系统中有很多的模型和相应的视图时,其复杂度就会迅速扩大,非常难以理解和调试,特别是模型和视图间可能存在的双向数据流动。
于是Facebook推出了Flux 和React.js。
其官方表述:

We built React to solve one problem: building large applications with
data that changes over time.(构建那些数据会随时间改变的大型应用)

  • React不是一个MVC框架
  • React不使用模板
  • 响应式更新非常简单
  • HTML5仅仅是个开始

React的目的是为了使前端的V层更具组件化,能更好的复用,它能够使用简单的html标签创建更多的自定义组件标签,内部绑定事件,同时可以让你不再需要来回查找某个 DOM 元素,然后再操作 DOM 去更改UI,变成你只需要操作数据就会改变相应的dom。

所以 React 的核心思想是:封装组件。
各个组件维护自己的状态和 UI,当状态变更,自动重新渲染整个组件。

一个简单的例子:

import React, { Component } from 'React';
import { render } from 'React-dom';

class HelloMessage extends Component {
  render() {
    return <div>Hello {this.props.name}</div>;
  }
}

// 加载组件到 DOM 元素 mountNode
render(<HelloMessage name="John" />, mountNode);

####组件

React 应用都是构建在组件之上。
上面的 HelloMessage 就是一个 React 构建的组件,最后一句 render 会把这个组件显示到页面上的某个元素 mountNode 里面,显示的内容就是 <div>Hello John</div>
props 是组件包含的两个核心概念之一,另一个是 state,一个组件就是通过这两个属性的值在 render 方法里面生成这个组件对应的 HTML 结构。

props
props 就是组件的配置属性,由外部通过 JSX 属性传入设置,一旦初始设置完成,就可以认为 this.props 是不可更改的,只是在调用这个组件的时候传入不同的属性(比如这里的 name)来定制显示这个组件。所以不要轻易更改设置 this.props 里面的值(虽然对于一个 JS 对象你可以做任何事)。

state
state 是组件的当前状态,可以把组件简单看成一个“状态机”,根据状态 state 呈现不同的 UI 展示。
一旦状态(数据)更改,组件就会自动调用 render 重新渲染 UI,这个更改的动作会通过 this.setState 方法来触发。

更多关于组件

JSX

从上面的代码可以看到将 HTML 直接嵌入了 JS 代码里面,这个就是 React 提出的一种叫 JSX 的语法,这应该是最开始接触 React 最不能接受的设定之一,因为前端被“表现和逻辑层分离”这种思想“洗脑”太久了。但实际上组件的 HTML 是组成一个组件不可分割的一部分,能够将 HTML 封装起来才是组件的完全体,前端才能实现真正意义上的组件化。

为什么会有JSX
传统的 MVC 是将模板放在其他地方,比如 <script> 标签或者模板文件,再在 JS 中通过某种手段引用模板。按照这种思路,四处分散的模板片段会带来更多的纠结,纠结模板引擎,纠结模板存放位置,纠结如何引用模板……
React 官方描述说:

We strongly believe that components are the right way to separate
concerns rather than “templates” and “display logic.” We think that
markup and the code that generates it are intimately tied together.
Additionally, display logic is often very complex and using template
languages to express it becomes cumbersome.

React 认为组件才是王道,而组件是和模板紧密关联的,组件模板和组件逻辑分离让问题复杂化了。
所以就有了 JSX 这种语法,就是为了把 HTML 模板直接嵌入到 JS 代码里面,这样就做到了模板和组件关联,但是 JS 不支持这种包含 HTML 的语法,所以需要通过工具将 JSX 编译输出成 JS 代码才能使用。

因为 JSX 最终是输出成 JS 代码来表达的,所以我们可以直接用 React 提供的这些 DOM 构建方法来写模板,比如一个 JSX 写的一个链接:
Hello!
用 JS 代码来写就成这样了:
React.createElement(‘a’, {href: ‘http://facebook.github.io/React/’}, ‘Hello!’)

更多关于JSX

Virtual DOM

当组件状态 state 有更改的时候,React 会自动调用组件的 render 方法重新渲染整个组件的 UI。
传统的web应用,操作DOM一般是直接更新操作的,当然如果真的这样大面积的操作 DOM,性能会是一个很大的问题。所以为了尽可能减少对DOM的操作,React 实现了Virtual DOM。

Virtual DOM是一个轻量级的虚拟的DOM,是React抽象出来的一个对象,组件 DOM 结构就是映射到这个 Virtual DOM 上。
React 在这个 Virtual DOM 上实现了一个 diff 算法,当要重新渲染组件的时候,会通过当前新的DOM表述与之前的作比较,计算出最小的步骤更新真实的DOM。由于 Virtual DOM 是一个纯粹的 JS 数据结构,也不是真的渲染整个 DOM 树所以性能会比原生 DOM 快很多。

更多关于Virtual DOM
####Flux
Unidirectional data flow (单向数据流)是Facebook十分推崇的架构方式。
应用架构的方式,也就是数据如何存放,如何更改数据,如何通知数据更改等等,所以它不是 React 提供的额外的什么新功能,可以看成是使用 React 构建大型应用的一种最佳实践。当应用足够复杂时才能体会到它的好处,虽然在一般应用场景下你可能不会意识到它的存在,也不会影响你开始使用 React。

正因为它是这样一种概念,所以涌现了许多实现,比如Facebook官方的 Flux 和更优雅的 Redux。

简单了解下FB官方Flux实现
React 是 MVC 里面 V 的部分,那么 Flux 就相当于添加 M 和 C 的部分。

一个 Flux 应用主要包含四个部分:

名称 描述
the dispatcher 处理动作分发,维护 Store 之间的依赖关系
the stores 数据和逻辑部分
the views React 组件,这一层可以看作 controller-views,作为视图同时响应用户交互
the actions 提供给 dispatcher 传递数据给 store

针对上面提到的 Flux 这些概念,需要写一个简单的类库来实现衔接这些功能,市面上有很多种实现,这里简单了解Facebook 官方的一个实现 Dispatcher.js

单向数据流

先来了解一下 Flux 的核心“单向数据流“怎么运作的:

Action -> Dispatcher -> Store -> View

更多时候 View 会通过用户交互触发 Action,所以一个简单完整的数据流类似这样:
流

整个流程如下:

  • 首先要有 action,通过定义一些 action creator 方法根据需要创建 Action 提供给 dispatcher
  • View 层通过用户交互(比如 onClick)会触发 Action
  • Dispatcher 会分发触发的 Action 给所有注册的 Store 的回调函数
  • Store 回调函数根据接收的 Action 更新自身数据之后会触发一个 change 事件通知 View 数据更改了
  • View 会监听这个 change 事件,拿到对应的新数据并调用 setState 更新组件 UI

所有的状态都由 Store 来维护,通过 Action 传递数据,构成了如上所述的单向数据流循环,所以应用中的各部分分工就相当明确,高度解耦了。
这种单向数据流使得整个系统都是透明可预测的。

利用单向数据流的React应用程序体系结构:
flux

更多关于Flux

更多关于Redux

React Native

React Native其实就是Native版的React,用javascript直接开发原生APP

React 在前端取得突破性成功以后,JavaScript 布道者们开始试图一统三端。移动平台能够运行 JavaScript 代码,并且JavaScript不仅可以传递配置信息,还可以表达逻辑信息的优点。
于是2015年,Facebook推出了基于 JavaScript 的开源框架 React Native。
这是一个面向前端开发者的框架,它结合了 Web 应用和 Native 应用的优势,在 JavaScript 中用 React 抽象操作系统原生的 UI 组件,代替 DOM 元素渲染,它的宗旨是让前端开发者像用 React 写网页那样,用 React Native 写移动端应用。
不同于跨平台语言的 Write once, Run anywhere!,React Native追求的是Learn once,Write anywhere!,希望前端开发者学习完 React 后,能够用同样的语法、工具等分别开发安卓和 iOS 平台的应用,而不用再去学习两个平台不同的技术。

React Native 印象

当创建完React Native 工程后,app.js文件就是开发者的主要实现文件。

这是一个Hello world的React Native 程序中app.js的内容。

import React, { Component } from 'React';
import { AppRegistry, Text, View } from 'React-Native';

class Greeting extends Component {
  render() {
    return (
      <Text>Hello {this.props.name}!</Text>
    );
  }
}

export default class LotsOfGreetings extends Component {
  render() {
    return (
      <View style={{alignItems: 'center'}}>
        <Greeting name='Rexxar' />
        <Greeting name='Jaina' />
        <Greeting name='Valeera' />
      </View>
    );
  }
}

// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => LotsOfGreetings);

正如上面所介绍的React.js ,React Native 和前者十分类似,比如PropsState
事实上,对于React-Native开发,仅仅有基础前端开发的知识是不够的,你还需要了解和掌握的有:

  • Node.js的基础
  • JSX语法基础
  • Flexbox的布局
  • 一些iOS或android技术

React Native工作原理

因为是用JavaScript来写原生应用,所以要明确的是,即使使用了 React Native,我们依然需要 UIKit 等框架,最终执行的是 Objective-C 代码。总React Native 只不过是以 JavaScript 的形式告诉 Objective-C 该执行什么代码。
所以React Native 能够运行起来,全靠 Objective-C 和 JavaScript 的交互。

React Native用iOS自带的JavaScriptCore作为JS的解析引擎,但并没有用到JavaScriptCore提供的一些可以让JS与OC交互的特性,而是自己实现了一套机制,这套机制可以通用于所有JS引擎上。
React Native详细设计比较复杂,这里简单了解下是如何调用原生代码的。

####React Native如何匹配原生API
先看一下React Native启动流程(iOS)

启动

1.创建RCTRootView -> 设置窗口根控制器的View,把RN的View添加到窗口上显示。
2.创建RCTBridge -> 桥接对象,管理JS和OC交互。
3.创建RCTBatchedBridge -> 批量桥架对象,JS和OC交互具体实现。
4.执行[RCTBatchedBridge loadSource] -> 加载JS源码
5.执行[RCTBatchedBridge initModulesWithDispatchGroup] -> 创建OC模块表
6.执行[RCTJSCExecutor injectJSONText] -> 往JS中插入OC模块表
7.执行完JS代码,回调OC,调用OC中的组件
8.完成UI渲染

可以知道React Native会在一开始生成OC模块配置表,然后把这个模块配置表传入JS中。JS调用OC模块方法时,通过配置表把模块方法转为模块ID和方法ID传给OC,OC通过模块配置表找到对应的方法执行之。

####通讯原理
下面我们简单了解一下iOS端通讯的原理。

通讯

1.JS端调用某个OC模块暴露出来的方法。
2.把上一步的调用分解为ModuleName,MethodName,arguments,再扔给MessageQueue处理。
在初始化时模块配置表上的每一个模块都生成了对应的remoteModule对象,对象里也生成了跟模块配置表里一一对应的方法,这些方法里可以拿到自身的模块名,方法名,并对callback进行一些处理,再移交给MessageQueue。
3.在这一步把JS的callback函数缓存在MessageQueue的一个成员变量里,用CallbackID代表callback。在通过保存在MessageQueue的模块配置表把上一步传进来的ModuleName和MethodName转为ModuleID和MethodID。
4.把上述步骤得到的ModuleID,MethodId,CallbackID和其他参数argus传给OC。把ModuleID,MethodID等数据加到一个队列里,等OC过来调JS的任意方法时,再把这个队列返回给OC,此时OC再执行这个队列里要调用的方法。
5.OC接收到消息,通过模块配置表拿到对应的模块和方法。
6.RCTModuleMethod对JS传过来的每一个参数进行处理。
RCTModuleMethod可以拿到OC要调用的目标方法的每个参数类型,处理JS类型到目标类型的转换,所有JS传过来的数字都是NSNumber,这里会转成对应的int/long/double等类型,更重要的是会为block类型参数的生成一个block。参数组装完毕后,通过NSInvocation动态调用相应的OC模块方法。
7.OC模块方法调用完,执行block回调。
8.调用到第6步说明的RCTModuleMethod生成的block。
9.block里带着CallbackID和block传过来的参数去调JS里MessageQueue的方法invokeCallbackAndReturnFlushedQueue。
10.MessageQueue通过CallbackID找到相应的JS callback方法。
11.调用callback方法,并把OC带过来的参数一起传过去,完成回调。
整个流程就是这样,简单概括下:JS函数调用转ModuleID/MethodID -> callback转CallbackID -> OC根据ID拿到方法 -> 处理参数 -> 调用OC方法 -> 回调CallbackID -> JS通过CallbackID拿到callback执行。

Weex和Vue.js

###Weex

Weex的官方网站
Weex 是阿里巴巴2016年6月开源的跨平台开发方案。能以 web 的开发体验构建高性能、可扩展的 native 应用,Weex 与 Vue 合作,使用 Vue 作为上层框架,并遵循 W3C 标准实现了统一的 JSEngine 和 DOM API,可以使用其他框架驱动 Weex,打造三端一致的 native 应用。同React Native一样,有人也将WEEX叫做Vue Native。
所以免不了被拿来同React Native“一决高下”的命运。React Native宣称「Learn Once, Write Anywhere」,而Weex宣称「Write Once, Run Everywhere」

对于推出该技术的原因,官方介绍如下

  1. 今天在技术社区有大量的 web 开发者,Weex 可以赋能更多的 web 开发者构建高性能和高体验的移动应用。
  2. Web 开发本身具有非常强的高效率和灵活性,这和 Weex 想解决的移动端动态性问题不谋而合。
  3. Web 标准和开发体验是很多顶尖而优秀的科技公司共同讨论和建设的结果,本身的设计和理念都有极高的
  4. 品质保障,同时 Weex 也希望可以借此机会努力为标准贡献一点自己的微薄之力。
  5. Web 是一种标准化的技术,标准本身就是一种力量,基于标准、尊重标准、贴近标准都意味着拥有更多的可能性。
  6. Web 今天的生态和社区是非常繁荣的,有很多成熟的工具、库、工程体系、最佳实践可以使用、引入和借鉴。

其特点是:

  • web 开发体验在各端当中是相同的。包括语法设计和工程链路。

  • Weex 的组件、模块设计都是 iOS、Android、Web 的开发者共同讨论出来的,有一定的通用性和普遍性。

  • Weex 开发同一份代码,可以在不同的端上分别执行,避免了多端的重复研发成本。

在同构这条路上,Weex比React Native做得更彻底,他“几乎”做到了,“你来使用vue写一个webapp,我顺便给你编译成了ios和android的原生app”

可以认为WEEX其实是阿里巴巴团队提高生产效率的产物,在淘宝这类要求多端统一迭代快速的部门,三端约定一种便于统一的规范,在加上时间的发酵,渐渐的就有了此类脚手架的雏形,同时在脸书ReactNative开源带来的极大轰动后,也有KPI推动的嫌疑

原理
在 Weex 代码中,您可以使用 <template>,<style> 和 <script> 标签编写页面或组件,然后将它们转换为 JS bundle 以进行部署。当服务器返回给客户端 JS bundle 时,JS bundle 会被客户端的 JavaScript 引擎处理,并管理渲染 native 视图,调用原生 API 和用户交互。

原理图示

###Vue.js
官网网站

Vue.js是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。
Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

Weex在迭代的过程中选择了于Vue2.0握手,因为该版本的Vue加入了 Virtual-DOM 和预编译器的设计,使得该框架在运行时能够脱离 HTML 和 CSS 解析,只依赖 JavaScript,Vue在和WEEX合作后,便获得了使用JS预编译原生的组件UI的能力。

和React.js的异同

1.Vue使用模板
Vue应用的默认选项是把markup放在HTML文件中。数据绑定表达式采用的是和Angular相似的mustache语法,而指令(特殊的HTML属性)用来向模板添加功能。

相比之下,React应用不使用模板,它要求开发者借助JSX在JavaScript中创建DOM。

对于来自标准Web开发方式的新开发者,模板更容易理解。但是一些资深开发者也喜欢模板,因为模板可以更好的把布局和功能分割开来,还可以使用Pug之类的模板引擎。

但是使用模板的代价是不得不学习所有的HTML扩展语法,而渲染函数只需要会标准的HTML和JavaScript。而且比起模板,渲染函数更加容易调试和测试。不过在Vue2.0中提供了使用模板或者渲染函数的选项。

2.Vue相对简单和易用
一个简单的Vue项目可以不需要转译直接运行在浏览器中,所以使用Vue可以像使用jQuery一样简单。当然这对于React来说在技术上也是可行的,但是典型的React代码是重度依赖于JSX和诸如class之类的ES6特性的。
Vue的简单在程序设计的时候体现更深,
React中是通过比较当前state和前一个state来决定何时在DOM中进行重渲染以及渲染的内容,因此需要不可变(immutable)的state。
Vue中的数据是可变(mutated)的,所以同样的操作看起来更加简洁。

3.Vue更加轻量级
当应用程序的状态改变时,React和Vue都将构建一个虚拟DOM并同步到真实DOM中。 两者都有各自的方法优化这个过程。
Vue的渲染系统比React的更快。
页面大小方面Vue再次领先,实现同样的功能,目前的版本压缩后只有25.6KB。React需要React DOM(37.4KB)和React with Addon库(11.4KB),共计44.8KB,几乎是Vue的两倍大。双倍的体积并不能带来双倍的功能。

4.React更适合大型应用

因为基于模板的应用程序第一眼看上去更加好理解,而且能很快跑起来。但是这些好处会阻碍应用扩展到更大的规模。模板容易出现比较难注意到的运行时错误,同时也很难去测试,重构和分解。

相比之下,Javascript模板可以组织成具有很好的分解性和代码组件,组件的可重用性和可测试性更好。Vue也有组件系统和渲染函数,但是React的渲染系统可配置性更强,还有诸如浅(shallow)渲染的特性,和React的测试工具结合起来使用,使代码的可测试性和可维护性更好。
React的immutable应用状态可能写起来不够简洁,但它在大型应用中意义非凡,因为透明度和可测试性在大型项目中变得至关重要。

5.React 生态系统更加成熟完善

React是目前最受欢迎的前端框架。它在NPM上每个月的下载量远超过Vue。相比Vue有更多的文章,教程和更多Stack Overflow的解答,同事还有更多的工具和插件可以在项目中使用。

这两个框架都是开源的,但是React诞生于Facebook,Facebook承诺会持续维护React。相比之下,Vue是独立开发者尤雨溪的作品,也有一些公司资助Vue,但是规模和Facebook有些距离。

更多内容

React Native、Weex和Cordova对比

跨平台特性

Cordova > React Native(Weex) > Native
名称 跨平台特性 描述
Cordova 基本覆盖主流移动平台:write once,run any where。 不涉及系统级开发的,基本是一次开发 处处运行,如果涉及到系统级设备调用以及项目配置(如 iOS的 plist文件),同时平台上也没有满足需求的插件,则需要自己手动编写Cordova插件
React Native 可以用JS编写iOS和android。Learn once, write anywhere。 统一用JS进行各端开发,但是需要针对iOS 和 android 开发两套代码。同时复杂实现仍然需要Native混编
Weex 可以用JS编写iOS和android。Write Once, Run Everywhere。 统一用JS进行各端开发,杂实现仍然需要Native混编
Native 无法跨平台 -

开发方式

名称 开发方式 描述
Cordova html + angularjs + css                 与网页开发类似,但是基本上都会使用H5框架(比如ionic),一般系统调用可以使用插件解决,特定需求需要编写原生代码插件
React Native React + css 简单UI全程使用JS开发,部分情况下需要使用与Native混合的方式,可以用flexbox布局
没有统一的UI组件,iOS组件较多,android组件较少。
需要各自编写JS文件的情况较多,简单空间和逻辑层可以共用,基本上iOS和android是两套代码。
Weex Vue.js 或 Rax 、CSS Vue.js 或 Rax编写UI,可以用flexbox布局,大部分代码可复用
Native java + oc或swift 不同语言开发 以及适配。

###功能支持

名称 功能支持 描述
Cordova 编写Cordova插件可达到全部支持 UI交互 由Web实现,系统级的交互由插件实现,基本涵盖了大部分系统功能,如摄像头,设备信息,电池,网络等,不排除潜在不支持的问题
React Native 与Native 混编 可达到全部支持 android高级组件可能需要自己实现,系统级的功能可通过安装第三方插件或者与Native混编的方式实现 ,基本上可以完全实现
Weex 与Native 混编 部分支持 生态环境有待改善,与原生交互较复杂
Native 全部支持 完全能实现

###性能对比

Native > React Native(Weex) > Cordova 
名称 功能支持 描述
Cordova 本质上还是网页,所以三者之中性能最差,在iOS上体验不错 由于各平台的webView引擎不同,所以体验有差异,android问题较突出。可以添加crosswalk插件优化体验,但是会导致app打包偏大。程序运行内存占用较大
React Native 接近原生性能 JS 到 Native 需要经过两层桥接,性能与原生差别不大
Weex 接近原生性能 渲染略优于React Native
Native 性能最好 在原生环境有多种优化方式,达到最佳性能

###优劣对比 | 名称 | 优势 |劣势| | ------------- |--------------------------------|-------------| | Cordova |1、iOS 和 android 基本上可以共用代码,纯web思维,开发速度快,简单方便,一次编码,到处运行,如果熟悉web开发,则开发难度较低。
2、文档很全,系统级支持封装较好。H5框架UI组件可以统一使用。
3、可实现在线更新 允许加载动态加载web JS| 1、性能相对较差,内存占用高,开发插件难度较大。
2、web技术无法解决一切问题,对于比较耗性能的地方无法利用Native的思维实现优势互补,如高体验的交互,动画等。| | React Native |1、虽然不能做到一处编码到处运行,但是基本上即使是两套代码,也是相同的JSx语法,使用JS进行开发。
2、用户体验高于html,贴近原生开发。开发效率较高
3、flexbox 布局,宣称比Native的自适应布局更加简单高效
4、可实现在线更新 | 1、对开发人员要求较高,仅会web技术搞不定,仅会Native更搞不定。当官方封装的控件、api无法满足需求时 就必然需要Native去扩展,扩展性仍然远远不如web,也远远不如直接写Native code。
2、官方声称:learn once, write anywhere,并不是run anywhere。事实上针对不同的平台会需要写多套代码,目前官方的api来看有SliderIOS,SwitchIOS..等控件,之后势必会出现SliderAndroid,SwitchAndroid..
3、学习曲线偏高| | Weex |1、轻量级,上手容易,vue更接近常用的web开发方式
2、SDK接入的形式,不需要复杂的开发环境
3、可以选择使用Vue.js 或 Rax
4、学习成本比React Native 低|1、生态环境、社区比活跃不高, 插件不如React Native丰富
2、目前阿里系的使用场景最多,出发点在于针对阿里系的优化,真正使用场景有待验证
3、开源管理一般,官网甚至有打不开的网页,有KPI项目 半途而废的隐患 | | Native| 最好的体验以及功能实现。完善成熟的开发文档以及demo。|学习成本高。需要两个团队分开开发 很难有iOS,android双平台高手。|

###更新 & 维护

名称 优势 劣势
Cordova 随时更新,适合做营销页面,但是重要的部分还是Native。 Web代码 + iOS/Android平台支持
React Native React Native部分可以热更新,bug及时修复,业务组件颗粒度小,不用把握全局即可修改业务代码 需要一个开发团队,前端 + iOS/Android工程师
Weex 热更新方便 仍然需要前端+ iOS/Android工程师配合
Native 随版本更新,尤其iOS审核严格,需要测试过关,否则影响用户。 iOS/Android开发周期长,两个开发团队。

###使用的APP
Cordova


cordova

React Native


使用React Native 的知名APP
React Native 开源APP

使用APP

Weex

谁在使用

应用

###目前技术储备

名称 前端 移动端
iOS ×
android ×
Node.js × ×
Angular.js ×
Weex × ×
Vue.js ×
JSX ×
Rax × ×
React.js × ×
Flex (Flexible Box) × ×

现在支付APP

APP中的展示页面比较合适使用Cordova或者React Native 来实现。不过目前APP已经上线稳定运行,React Native 学习成本较高,如果有新的规划,偏展示的页面可以考虑嵌入Cordova模块。

支付插件

由于支付插件已经取消了网关页面,且在iOS开发中Apple禁止SDK中使用JS动态更新,出于安全考虑 React Native 和Cordova没有太多使用场景。
不过由于不少Hybrid APP都在使用Cordova,所以可以开发现在支付插件的Cordova插件。

web

React.js 2013年5月开源,作为Facebook大量应用和推广的技术,在github上已经收获了80000+ star。Facebook也承诺会持续投入成本维护该技术,经过4年多的发展已经是十分成熟的JS库,拥有大量的开发者和丰富的资料。在web项目中十分值得推广。

2017-04-21 14:25:56 wgl_happy 阅读数 805
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57874 人正在学习 去看看 李宁

一个用于显示文本的React组件,并且它也支持嵌套、样式,以及触摸处理。在下面的例子里,嵌套的标题和正文文字会继承来自styles.baseTextfontFamily字体样式,不过标题上还附加了它自己额外的样式。标题和文本会在顶部依次堆叠,并且被代码中内嵌的换行符分隔开。

renderText: function() {
  return (
    <Text style={styles.baseText}>
      <Text style={styles.titleText} onPress={this.onPressTitle}>
        {this.state.titleText + '\n\n'}
      </Text>
      <Text numberOfLines={5}>
        {this.state.bodyText}
      </Text>
    </Text>
  );
},
...
var styles = StyleSheet.create({
  baseText: {
    fontFamily: 'Cochin',
  },
  titleText: {
    fontSize: 20,
    fontWeight: 'bold',
  },
};

截图

属性

adjustsFontSizeToFit bool #

指定字体是否随着给定样式的限制而自动缩放。

allowFontScaling bool #

控制字体是否要根据系统的“字体大小”辅助选项来进行缩放。

iosminimumFontScale bool #

当adjustsFontSizeToFit开启时,指定最小的缩放比(即不能低于这个值)。可设定的值为0.01 - 1.0

numberOfLines number #

用来当文本过长的时候裁剪文本。包括折叠产生的换行在内,总的行数不会超过这个属性的限制。

onLayout function #

当挂载或者布局变化以后调用,参数为如下的内容:

{nativeEvent: {layout: {x, y, width, height}}}

onLongPress function #

当文本被长按以后调用此回调函数。

onPress function #

当文本被点击以后调用此回调函数。

selectable function #

决定用户是否可以长按选择文本,以便复制和粘贴。

style style #

color color
fontFamily string
fontSize number
fontStyle enum('normal', 'italic')
fontWeight enum('normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900')

指定字体的粗细。大多数字体都支持'normal'和'bold'值。并非所有字体都支持所有的数字值。如果某个值不支持,则会自动选择最接近的值。

lineHeight number
textAlign enum('auto', 'left', 'right', 'center', 'justify')

指定文本的对齐方式。其中'justify'值仅iOS支持,在Android上会变为left

textDecorationLine enum('none', 'underline', 'line-through', 'underline line-through')
textShadowColor color
textShadowOffset {width: number, height: number}
textShadowRadius number
androidincludeFontPadding bool

Android在默认情况下会为文字额外保留一些padding,以便留出空间摆放上标或是下标的文字。对于某些字体来说,这些额外的padding可能会导致文字难以垂直居中。如果你把textAlignVertical设置为center之后,文字看起来依然不在正中间,那么可以尝试将本属性设置为false.

androidtextAlignVertical enum('auto', 'top', 'bottom', 'center')
iosfontVariant [enum('small-caps', 'oldstyle-nums', 'lining-nums', 'tabular-nums', 'proportional-nums')]
iosletterSpacing number
iostextDecorationColor color
iostextDecorationStyle enum('solid', 'double', 'dotted', 'dashed')
ioswritingDirection enum('auto', 'ltr', 'rtl')

testID string #

用来在端到端测试中标记这个视图。

iossuppressHighlighting bool #

当为true时,如果文本被按下,则没有任何视觉效果。默认情况下,文本被按下时会有一个灰色的、椭圆形的高光。

嵌套文本

在iOS当中,显示一个格式化文本的方法就是使用NSAttributedString:提供你想显示的文本内容,并且使用范围标注来指定一些格式。这种用法非常繁琐。在React Native中,我们决定采用和Web一致的设计,这样你可以把相同格式的文本嵌套包裹起来:

<Text style={{fontWeight: 'bold'}}>
  I am bold
  <Text style={{color: 'red'}}>
    and red
  </Text>
</Text>

而实际上在框架内部,这会生成一个扁平结构的NSAttributedString,包含以下的信息:

"I am bold and red"
0-9: bold
9-17: bold, red

容器

<Text>元素在布局上不同于其它组件:在Text内部的元素不再使用flexbox布局,而是采用文本布局。这意味着<Text>内部的元素不再是一个个矩形,而可能会在行末进行折叠。

<Text>
  <Text>First part and </Text>
  <Text>second part</Text>
</Text>
// Text container: all the text flows as if it was one
// |First part |
// |and second |
// |part       |

<View>
  <Text>First part and </Text>
  <Text>second part</Text>
</View>
// View container: each text is its own block
// |First part |
// |and        |
// |second part|

样式继承限制

在Web上,要想指定整个文档的字体和大小,我们只需要写:

/* 这段代码是CSS, *不是*React Native */
html {
  font-family: 'lucida grande', tahoma, verdana, arial, sans-serif;
  font-size: 11px;
  color: #141823;
}

当浏览器尝试渲染一个文本节点的时候,它会在树中一路向上查询,直到根节点,来找到一个具备font-size属性的元素。这个系统一个不好的地方在于任何节点都可能会有font-size属性,包括<div>标签。这个设计为了方便而设计,但实际上语义上并不太正确。

在React Native中,我们把这个问题设计的更加严谨:你必须把你的文本节点放在<Text>组件内。你不能直接在<View>下放置一段文本。

// 错误的做法:会导致一个错误。<View>下不能直接放一段文本。
<View>
  一些文本
</View>

// 正确的做法
<View>
  <Text>
    一些文本
  </Text>
</View>

并且你也不能直接设置一整颗子树的默认样式。使用一个一致的文本和尺寸的推荐方式是创建一个包含相关样式的组件MyAppText,然后在你的App中反复使用它。你还可以创建更多特殊的组件譬如MyAppHeaderText来表达不同样式的文本。

<View>
  <MyAppText>这个组件包含了一个默认的字体样式,用于整个应用的文本</MyAppText>
  <MyAppHeaderText>这个组件包含了用于标题的样式</MyAppHeaderText>
</View>

React Native实际上还是有一部分样式继承的实现,不过仅限于文本标签的子树。在下面的代码里,第二部分会在加粗的同时又显示为红色:

<Text style={{fontWeight: 'bold'}}>
  I am bold
  <Text style={{color: 'red'}}>
    and red
  </Text>
</Text>

我们相信这种看起来不太舒服的给文本添加样式的方法反而会帮助我们生产更好的App:

  • (对开发者来说) React组件在概念上被设计为强隔离性的:你应当可以在应用的任何位置放置一个组件,而且只要属性相同,其外观和表现都将完全相同。文本如果能够继承外面的样式属性,将会打破这种隔离性。

  • (对实现者来说) React Native的实现也被简化了。我们不需要在每个元素上都添加一个fontFamily字段,并且我们也不需要隐含地在显示文本的时候向上遍历树。唯一的样式继承在原生Text组件中编码,也不会影响到其它组件或者系统本身。

例子

iOS

'use strict';

var React = require('react');
var ReactNative = require('react-native');
var {
  Image,
  StyleSheet,
  Text,
  View,
} = ReactNative;

var Entity = React.createClass({
  render: function() {
    return (
      <Text style={{fontWeight: '500', color: '#527fe4'}}>
        {this.props.children}
      </Text>
    );
  }
});

var AttributeToggler = React.createClass({
  getInitialState: function() {
    return {fontWeight: 'bold', fontSize: 15};
  },
  toggleWeight: function() {
    this.setState({
      fontWeight: this.state.fontWeight === 'bold' ? 'normal' : 'bold'
    });
  },
  increaseSize: function() {
    this.setState({
      fontSize: this.state.fontSize + 1
    });
  },
  render: function() {
    var curStyle = {fontWeight: this.state.fontWeight, fontSize: this.state.fontSize};
    return (
      <View>
        <Text style={curStyle}>
          Tap the controls below to change attributes.
        </Text>
        <Text>
          <Text>See how it will even work on <Text style={curStyle}>this nested text</Text></Text>
        </Text>
        <Text
          style={{backgroundColor: '#ffaaaa', marginTop: 5}}
          onPress={this.toggleWeight}>
          Toggle Weight
        </Text>
        <Text
          style={{backgroundColor: '#aaaaff', marginTop: 5}}
          onPress={this.increaseSize}>
          Increase Size
        </Text>
      </View>
    );
  }
});

exports.title = '<Text>';
exports.description = 'Base component for rendering styled text.';
exports.displayName = 'TextExample';
exports.examples = [
{
  title: 'Wrap',
  render: function() {
    return (
      <Text>
        The text should wrap if it goes on multiple lines. See, this is going to
        the next line.
      </Text>
    );
  },
}, {
  title: 'Padding',
  render: function() {
    return (
      <Text style={{padding: 10}}>
        This text is indented by 10px padding on all sides.
      </Text>
    );
  },
}, {
  title: 'Font Family',
  render: function() {
    return (
      <View>
        <Text style={{fontFamily: 'Cochin'}}>
          Cochin
        </Text>
        <Text style={{fontFamily: 'Cochin', fontWeight: 'bold'}}>
          Cochin bold
        </Text>
        <Text style={{fontFamily: 'Helvetica'}}>
          Helvetica
        </Text>
        <Text style={{fontFamily: 'Helvetica', fontWeight: 'bold'}}>
          Helvetica bold
        </Text>
        <Text style={{fontFamily: 'Verdana'}}>
          Verdana
        </Text>
        <Text style={{fontFamily: 'Verdana', fontWeight: 'bold'}}>
          Verdana bold
        </Text>
      </View>
    );
  },
}, {
  title: 'Font Size',
  render: function() {
    return (
      <View>
        <Text style={{fontSize: 23}}>
          Size 23
        </Text>
        <Text style={{fontSize: 8}}>
          Size 8
        </Text>
      </View>
    );
  },
}, {
  title: 'Color',
  render: function() {
    return (
      <View>
        <Text style={{color: 'red'}}>
          Red color
        </Text>
        <Text style={{color: 'blue'}}>
          Blue color
        </Text>
      </View>
    );
  },
}, {
  title: 'Font Weight',
  render: function() {
    return (
      <View>
        <Text style={{fontSize: 20, fontWeight: '100'}}>
          Move fast and be ultralight
        </Text>
        <Text style={{fontSize: 20, fontWeight: '200'}}>
          Move fast and be light
        </Text>
        <Text style={{fontSize: 20, fontWeight: 'normal'}}>
          Move fast and be normal
        </Text>
        <Text style={{fontSize: 20, fontWeight: 'bold'}}>
          Move fast and be bold
        </Text>
        <Text style={{fontSize: 20, fontWeight: '900'}}>
          Move fast and be ultrabold
        </Text>
      </View>
    );
  },
},  {
  title: 'Font Style',
  render: function() {
    return (
      <View>
        <Text style={{fontStyle: 'normal'}}>
          Normal text
        </Text>
        <Text style={{fontStyle: 'italic'}}>
          Italic text
        </Text>
      </View>
    );
  },
}, {
  title: 'Text Decoration',
  render: function() {
    return (
      <View>
        <Text style={{textDecorationLine: 'underline', textDecorationStyle: 'solid'}}>
          Solid underline
        </Text>
        <Text style={{textDecorationLine: 'underline', textDecorationStyle: 'double', textDecorationColor: '#ff0000'}}>
          Double underline with custom color
        </Text>
        <Text style={{textDecorationLine: 'underline', textDecorationStyle: 'dashed', textDecorationColor: '#9CDC40'}}>
          Dashed underline with custom color
        </Text>
        <Text style={{textDecorationLine: 'underline', textDecorationStyle: 'dotted', textDecorationColor: 'blue'}}>
          Dotted underline with custom color
        </Text>
        <Text style={{textDecorationLine: 'none'}}>
          None textDecoration
        </Text>
        <Text style={{textDecorationLine: 'line-through', textDecorationStyle: 'solid'}}>
          Solid line-through
        </Text>
        <Text style={{textDecorationLine: 'line-through', textDecorationStyle: 'double', textDecorationColor: '#ff0000'}}>
          Double line-through with custom color
        </Text>
        <Text style={{textDecorationLine: 'line-through', textDecorationStyle: 'dashed', textDecorationColor: '#9CDC40'}}>
          Dashed line-through with custom color
        </Text>
        <Text style={{textDecorationLine: 'line-through', textDecorationStyle: 'dotted', textDecorationColor: 'blue'}}>
          Dotted line-through with custom color
        </Text>
        <Text style={{textDecorationLine: 'underline line-through'}}>
          Both underline and line-through
        </Text>
      </View>
    );
  },
}, {
  title: 'Nested',
  description: 'Nested text components will inherit the styles of their ' +
    'parents (only backgroundColor is inherited from non-Text parents).  ' +
    '<Text> only supports other <Text> and raw text (strings) as children.',
  render: function() {
    return (
      <View>
        <Text>
          (Normal text,
          <Text style={{fontWeight: 'bold'}}>
            (and bold
            <Text style={{fontSize: 11, color: '#527fe4'}}>
              (and tiny inherited bold blue)
            </Text>
            )
          </Text>
          )
        </Text>
        <Text style={{opacity:0.7}}>
          (opacity
            <Text>
              (is inherited
                <Text style={{opacity:0.7}}>
                  (and accumulated
                    <Text style={{backgroundColor:'#ffaaaa'}}>
                      (and also applies to the background)
                    </Text>
                  )
                </Text>
              )
            </Text>
          )
        </Text>
        <Text style={{fontSize: 12}}>
          <Entity>Entity Name</Entity>
        </Text>
      </View>
    );
  },
}, {
  title: 'Text Align',
  render: function() {
    return (
      <View>
        <Text>
          auto (default) - english LTR
        </Text>
        <Text>
          أحب اللغة العربية auto (default) - arabic RTL
        </Text>
        <Text style={{textAlign: 'left'}}>
          left left left left left left left left left left left left left left left
        </Text>
        <Text style={{textAlign: 'center'}}>
          center center center center center center center center center center center
        </Text>
        <Text style={{textAlign: 'right'}}>
          right right right right right right right right right right right right right
        </Text>
        <Text style={{textAlign: 'justify'}}>
          justify: this text component{"'"}s contents are laid out with "textAlign: justify"
          and as you can see all of the lines except the last one span the
          available width of the parent container.
        </Text>
      </View>
    );
  },
}, {
  title: 'Letter Spacing',
  render: function() {
    return (
      <View>
        <Text style={{letterSpacing: 0}}>
          letterSpacing = 0
        </Text>
        <Text style={{letterSpacing: 2, marginTop: 5}}>
          letterSpacing = 2
        </Text>
        <Text style={{letterSpacing: 9, marginTop: 5}}>
          letterSpacing = 9
        </Text>
        <Text style={{letterSpacing: -1, marginTop: 5}}>
          letterSpacing = -1
        </Text>
      </View>
    );
  },
}, {
  title: 'Spaces',
  render: function() {
    return (
      <Text>
        A {'generated'} {' '} {'string'} and    some &nbsp;&nbsp;&nbsp; spaces
      </Text>
    );
  },
}, {
  title: 'Line Height',
  render: function() {
    return (
      <Text>
        <Text style={{lineHeight: 35}}>
          A lot of space between the lines of this long passage that should
          wrap once.
        </Text>
      </Text>
    );
  },
}, {
  title: 'Empty Text',
  description: 'It\'s ok to have Text with zero or null children.',
  render: function() {
    return (
      <Text />
    );
  },
}, {
  title: 'Toggling Attributes',
  render: function(): ReactElement {
    return <AttributeToggler />;
  },
}, {
  title: 'backgroundColor attribute',
  description: 'backgroundColor is inherited from all types of views.',
  render: function() {
    return (
      <Text style={{backgroundColor: 'yellow'}}>
        Yellow container background,
        <Text style={{backgroundColor: '#ffaaaa'}}>
          {' '}red background,
          <Text style={{backgroundColor: '#aaaaff'}}>
            {' '}blue background,
            <Text>
              {' '}inherited blue background,
              <Text style={{backgroundColor: '#aaffaa'}}>
                {' '}nested green background.
              </Text>
            </Text>
          </Text>
        </Text>
      </Text>
    );
  },
}, {
  title: 'numberOfLines attribute',
  render: function() {
    return (
      <View>
        <Text numberOfLines={1}>
          Maximum of one line, no matter how much I write here. If I keep writing, it{"'"}ll just truncate after one line.
        </Text>
        <Text numberOfLines={2} style={{marginTop: 20}}>
          Maximum of two lines, no matter how much I write here. If I keep writing, it{"'"}ll just truncate after two lines.
        </Text>
        <Text style={{marginTop: 20}}>
          No maximum lines specified, no matter how much I write here. If I keep writing, it{"'"}ll just keep going and going.
        </Text>
      </View>
    );
  },
}, {
  title: 'Text highlighting (tap the link to see highlight)',
  render: function() {
    return (
      <View>
        <Text>Lorem ipsum dolor sit amet, <Text suppressHighlighting={false} style={{backgroundColor: 'white', textDecorationLine: 'underline', color: 'blue'}} onPress={() => null}>consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud</Text> exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</Text>
      </View>
    );
  },
}, {
  title: 'allowFontScaling attribute',
  render: function() {
    return (
      <View>
        <Text>
          By default, text will respect Text Size accessibility setting on iOS.
          It means that all font sizes will be increased or descreased depending on the value of Text Size setting in
          {" "}<Text style={{fontWeight: 'bold'}}>Settings.app - Display & Brightness - Text Size</Text>
        </Text>
        <Text style={{marginTop: 10}}>
          You can disable scaling for your Text component by passing {"\""}allowFontScaling={"{"}false{"}\""} prop.
        </Text>
        <Text allowFontScaling={false} style={{marginTop: 20}}>
          This text will not scale.
        </Text>
      </View>
    );
  },
}, {
  title: 'Inline images',
  render: function() {
    return (
      <View>
        <Text>
          This text contains an inline image <Image source={require('./flux.png')} style={{width: 30, height: 11, resizeMode: 'cover'}}/>. Neat, huh?
        </Text>
      </View>
    );
  },
}, {
  title: 'Text shadow',
  render: function() {
    return (
      <View>
        <Text style={{fontSize: 20, textShadowOffset: {width: 2, height: 2}, textShadowRadius: 1, textShadowColor: '#00cccc'}}>
          Demo text shadow
        </Text>
      </View>
    );
  },
}];

var styles = StyleSheet.create({
  backgroundColorText: {
    margin: 5,
    marginBottom: 0,
    backgroundColor: 'rgba(100, 100, 100, 0.3)'
  },
});

Android

'use strict';

var React = require('react');
var ReactNative = require('react-native');
var {
  Image,
  StyleSheet,
  Text,
  View,
} = ReactNative;
var UIExplorerBlock = require('./UIExplorerBlock');
var UIExplorerPage = require('./UIExplorerPage');

var Entity = React.createClass({
  render: function() {
    return (
      <Text style={{fontWeight: 'bold', color: '#527fe4'}}>
        {this.props.children}
      </Text>
    );
  }
});

var AttributeToggler = React.createClass({
  getInitialState: function() {
    return {fontWeight: 'bold', fontSize: 15};
  },
  toggleWeight: function() {
    this.setState({
      fontWeight: this.state.fontWeight === 'bold' ? 'normal' : 'bold'
    });
  },
  increaseSize: function() {
    this.setState({
      fontSize: this.state.fontSize + 1
    });
  },
  render: function() {
    var curStyle = {fontWeight: this.state.fontWeight, fontSize: this.state.fontSize};
    return (
      <View>
        <Text style={curStyle}>
          Tap the controls below to change attributes.
        </Text>
        <Text>
          <Text>See how it will even work on <Text style={curStyle}>this nested text</Text></Text>
        </Text>
        <Text>
          <Text onPress={this.toggleWeight}>Toggle Weight</Text>
          {' (with highlight onPress)'}
        </Text>
        <Text onPress={this.increaseSize} suppressHighlighting={true}>
          Increase Size (suppressHighlighting true)
        </Text>
      </View>
    );
  }
});

var TextExample = React.createClass({
  statics: {
    title: '<Text>',
    description: 'Base component for rendering styled text.',
  },
  render: function() {
    return (
      <UIExplorerPage title="<Text>">
        <UIExplorerBlock title="Wrap">
          <Text>
            The text should wrap if it goes on multiple lines.
            See, this is going to the next line.
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Padding">
          <Text style={{padding: 10}}>
            This text is indented by 10px padding on all sides.
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Font Family">
          <Text style={{fontFamily: 'sans-serif'}}>
            Sans-Serif
          </Text>
          <Text style={{fontFamily: 'sans-serif', fontWeight: 'bold'}}>
            Sans-Serif Bold
          </Text>
          <Text style={{fontFamily: 'serif'}}>
            Serif
          </Text>
          <Text style={{fontFamily: 'serif', fontWeight: 'bold'}}>
            Serif Bold
          </Text>
          <Text style={{fontFamily: 'monospace'}}>
            Monospace
          </Text>
          <Text style={{fontFamily: 'monospace', fontWeight: 'bold'}}>
            Monospace Bold (After 5.0)
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Android Material Design fonts">
          <View style={{flexDirection: 'row', alignItems: 'flex-start'}}>
            <View style={{flex: 1}}>
              <Text style={{fontFamily: 'sans-serif'}}>
                Roboto Regular
              </Text>
              <Text style={{fontFamily: 'sans-serif', fontStyle: 'italic'}}>
                Roboto Italic
              </Text>
              <Text style={{fontFamily: 'sans-serif', fontWeight: 'bold'}}>
                Roboto Bold
              </Text>
              <Text style={{fontFamily: 'sans-serif', fontStyle: 'italic', fontWeight: 'bold'}}>
                Roboto Bold Italic
              </Text>
              <Text style={{fontFamily: 'sans-serif-light'}}>
                Roboto Light
              </Text>
              <Text style={{fontFamily: 'sans-serif-light', fontStyle: 'italic'}}>
                Roboto Light Italic
              </Text>
              <Text style={{fontFamily: 'sans-serif-thin'}}>
                Roboto Thin (After 4.2)
              </Text>
              <Text style={{fontFamily: 'sans-serif-thin', fontStyle: 'italic'}}>
                Roboto Thin Italic (After 4.2)
              </Text>
              <Text style={{fontFamily: 'sans-serif-condensed'}}>
                Roboto Condensed
              </Text>
              <Text style={{fontFamily: 'sans-serif-condensed', fontStyle: 'italic'}}>
                Roboto Condensed Italic
              </Text>
              <Text style={{fontFamily: 'sans-serif-condensed', fontWeight: 'bold'}}>
                Roboto Condensed Bold
              </Text>
              <Text style={{
                  fontFamily: 'sans-serif-condensed',
                  fontStyle: 'italic',
                  fontWeight: 'bold'}}>
                Roboto Condensed Bold Italic
              </Text>
              <Text style={{fontFamily: 'sans-serif-medium'}}>
                Roboto Medium (After 5.0)
              </Text>
              <Text style={{fontFamily: 'sans-serif-medium', fontStyle: 'italic'}}>
                Roboto Medium Italic (After 5.0)
              </Text>
            </View>
          </View>
        </UIExplorerBlock>
        <UIExplorerBlock title="Custom Fonts">
          <View style={{flexDirection: 'row', alignItems: 'flex-start'}}>
            <View style={{flex: 1}}>
              <Text style={{fontFamily: 'notoserif'}}>
                NotoSerif Regular
              </Text>
              <Text style={{fontFamily: 'notoserif', fontStyle: 'italic', fontWeight: 'bold'}}>
                NotoSerif Bold Italic
              </Text>
              <Text style={{fontFamily: 'notoserif', fontStyle: 'italic'}}>
                NotoSerif Italic (Missing Font file)
              </Text>
            </View>
          </View>
        </UIExplorerBlock>

        <UIExplorerBlock title="Font Size">
          <Text style={{fontSize: 23}}>
            Size 23
          </Text>
          <Text style={{fontSize: 8}}>
            Size 8
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Color">
          <Text style={{color: 'red'}}>
            Red color
          </Text>
          <Text style={{color: 'blue'}}>
            Blue color
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Font Weight">
          <Text style={{fontWeight: 'bold'}}>
            Move fast and be bold
          </Text>
          <Text style={{fontWeight: 'normal'}}>
            Move fast and be bold
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Font Style">
          <Text style={{fontStyle: 'italic'}}>
            Move fast and be bold
          </Text>
          <Text style={{fontStyle: 'normal'}}>
            Move fast and be bold
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Font Style and Weight">
          <Text style={{fontStyle: 'italic', fontWeight: 'bold'}}>
            Move fast and be bold
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Text Decoration">
          <Text style={{textDecorationLine: 'underline'}}>
            Solid underline
          </Text>
          <Text style={{textDecorationLine: 'none'}}>
            None textDecoration
          </Text>
          <Text style={{textDecorationLine: 'line-through', textDecorationStyle: 'solid'}}>
            Solid line-through
          </Text>
          <Text style={{textDecorationLine: 'underline line-through'}}>
            Both underline and line-through
          </Text>
          <Text>
            Mixed text with <Text style={{textDecorationLine: 'underline'}}>underline</Text> and <Text style={{textDecorationLine: 'line-through'}}>line-through</Text> text nodes
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Nested">
          <Text onPress={() => console.log('1st')}>
            (Normal text,
            <Text style={{fontWeight: 'bold'}} onPress={() => console.log('2nd')}>
              (and bold
              <Text style={{fontStyle: 'italic', fontSize: 11, color: '#527fe4'}} onPress={() => console.log('3rd')}>
                (and tiny bold italic blue
                <Text style={{fontWeight: 'normal', fontStyle: 'normal'}} onPress={() => console.log('4th')}>
                  (and tiny normal blue)
                </Text>
                )
              </Text>
              )
            </Text>
            )
          </Text>
          <Text style={{fontFamily: 'serif'}} onPress={() => console.log('1st')}>
            (Serif
            <Text style={{fontStyle: 'italic', fontWeight: 'bold'}} onPress={() => console.log('2nd')}>
              (Serif Bold Italic
              <Text
                style={{fontFamily: 'monospace', fontStyle: 'normal', fontWeight: 'normal'}}
                onPress={() => console.log('3rd')}>
                (Monospace Normal
                <Text
                  style={{fontFamily: 'sans-serif', fontWeight: 'bold'}}
                  onPress={() => console.log('4th')}>
                  (Sans-Serif Bold
                  <Text style={{fontWeight: 'normal'}} onPress={() => console.log('5th')}>
                    (and Sans-Serif Normal)
                  </Text>
                  )
                </Text>
                )
              </Text>
              )
            </Text>
            )
          </Text>
          <Text style={{fontSize: 12}}>
            <Entity>Entity Name</Entity>
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Text Align">
          <Text>
            auto (default) - english LTR
          </Text>
          <Text>
            أحب اللغة العربية auto (default) - arabic RTL
          </Text>
          <Text style={{textAlign: 'left'}}>
            left left left left left left left left left left left left left left left
          </Text>
          <Text style={{textAlign: 'center'}}>
            center center center center center center center center center center center
          </Text>
          <Text style={{textAlign: 'right'}}>
            right right right right right right right right right right right right right
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Unicode">
          <View style={{flex: 1}}>
            <View style={{flexDirection: 'row'}}>
              <Text style={{backgroundColor: 'red'}}>
                星际争霸是世界上最好的游戏。
              </Text>
            </View>
            <View style={{flex: 1}}>
              <Text style={{backgroundColor: 'red'}}>
                星际争霸是世界上最好的游戏。
              </Text>
            </View>
            <View style={{flex: 1, alignItems: 'center'}}>
              <Text style={{backgroundColor: 'red'}}>
                星际争霸是世界上最好的游戏。
              </Text>
            </View>
            <View style={{flex: 1}}>
              <Text style={{backgroundColor: 'red'}}>
                星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。
              </Text>
            </View>
          </View>
        </UIExplorerBlock>
        <UIExplorerBlock title="Spaces">
          <Text>
            A {'generated'} {' '} {'string'} and    some &nbsp;&nbsp;&nbsp; spaces
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Line Height">
          <Text style={{lineHeight: 35}}>
            Holisticly formulate inexpensive ideas before best-of-breed benefits. <Text style={{fontSize: 20}}>Continually</Text> expedite magnetic potentialities rather than client-focused interfaces.
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Empty Text">
          <Text />
        </UIExplorerBlock>
        <UIExplorerBlock title="Toggling Attributes">
          <AttributeToggler />
        </UIExplorerBlock>
        <UIExplorerBlock title="backgroundColor attribute">
          <Text style={{backgroundColor: '#ffaaaa'}}>
            Red background,
            <Text style={{backgroundColor: '#aaaaff'}}>
              {' '}blue background,
              <Text>
                {' '}inherited blue background,
                <Text style={{backgroundColor: '#aaffaa'}}>
                  {' '}nested green background.
                </Text>
              </Text>
            </Text>
          </Text>
          <Text style={{backgroundColor: 'rgba(100, 100, 100, 0.3)'}}>
            Same alpha as background,
            <Text>
              Inherited alpha from background,
              <Text style={{backgroundColor: 'rgba(100, 100, 100, 0.3)'}}>
                Reapply alpha
              </Text>
            </Text>
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="containerBackgroundColor attribute">
          <View style={{flexDirection: 'row', height: 85}}>
            <View style={{backgroundColor: '#ffaaaa', width: 150}} />
            <View style={{backgroundColor: '#aaaaff', width: 150}} />
          </View>
          <Text style={[styles.backgroundColorText, {top: -80}]}>
            Default containerBackgroundColor (inherited) + backgroundColor wash
          </Text>
          <Text style={[styles.backgroundColorText, {top: -70, backgroundColor: 'transparent'}]}>
            {"containerBackgroundColor: 'transparent' + backgroundColor wash"}
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="numberOfLines attribute">
          <Text numberOfLines={1}>
            Maximum of one line no matter now much I write here. If I keep writing it{"'"}ll just truncate after one line
          </Text>
          <Text numberOfLines={2} style={{marginTop: 20}}>
            Maximum of two lines no matter now much I write here. If I keep writing it{"'"}ll just truncate after two lines
          </Text>
          <Text style={{marginTop: 20}}>
            No maximum lines specified no matter now much I write here. If I keep writing it{"'"}ll just keep going and going
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Inline images">
          <Text>
            This text contains an inline image <Image source={require('./flux.png')}/>. Neat, huh?
          </Text>
        </UIExplorerBlock>
        <UIExplorerBlock title="Text shadow">
          <Text style={{fontSize: 20, textShadowOffset: {width: 2, height: 2}, textShadowRadius: 1, textShadowColor: '#00cccc'}}>
            Demo text shadow
          </Text>
        </UIExplorerBlock>
      </UIExplorerPage>
    );
  }
});

var styles = StyleSheet.create({
  backgroundColorText: {
    left: 5,
    backgroundColor: 'rgba(100, 100, 100, 0.3)'
  },
});

module.exports = TextExample;
2016-07-29 10:44:06 KevinsCSDN 阅读数 3020
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57874 人正在学习 去看看 李宁

参考文档:http://facebook.github.io/react-native/docs/style.html

  1. style是一个props
  2. style的键值命名格式遵循CSS风格,除了名字使用驼峰法则而不是使用分隔符。例如背景色:backgoundColor,不是background-color
  3. 可以传递style数组,最后一个style有优先权,因而可以使用它继承styles
  4. 为了组件的扩展性,在一个作用域中使用StyleSheet.create定义多个style通常更加明晰。

实例如下:

import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, View } from 'react-native';

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>
    );
  }
}

const styles = StyleSheet.create({
  bigblue: {
    color: 'blue',
    fontWeight: 'bold',
    fontSize: 30,
  },
  red: {
    color: 'red',
  },
});

AppRegistry.registerComponent('LotsOfStyles', () => LotsOfStyles);
2019-03-11 19:39:17 duoduo_11011 阅读数 187
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57874 人正在学习 去看看 李宁

高性能的分组(section)列表组件,支持下面这些常用的功能:

  • 完全跨平台。
  • 行组件显示或隐藏时可配置回调事件。
  • 支持单独的头部组件。
  • 支持单独的尾部组件。
  • 支持自定义行间分隔线。
  • 支持分组的头部组件。
  • 支持分组的分隔线。
  • 支持多种数据源结构
  • 支持下拉刷新。
  • 支持上拉加载。

属性

名称 类型 必填 说明
sections array of Sections 用来渲染的数据,类似于 FlatList 中的 data 属性。
initialNumToRender number 指定一开始渲染的元素数量,最好刚刚够填满一个屏幕,这样保证了用最短的时间给用户呈现可见的内容。
keyExtractor (item: Item, index: number) => string 此函数用于为给定的item生成一个不重复的key。Key的作用是使React能够区分同类元素的不同个体,以便在刷新时能够确定其变化的位置,减少重新渲染的开销。
renderItem function 从data中挨个取出数据并渲染到列表中。
ItemSeparatorComponent component 行与行之间的分隔线组件。不会出现在第一行之前和最后一行之后。
ListEmptyComponent component, function, element 列表为空时渲染该组件。可以是 React Component, 也可以是一个 render 函数,或者渲染好的 element。
ListFooterComponent component, function, element 尾部组件。可以是 React Component, 也可以是一个 render 函数,或者渲染好的 element。
ListHeaderComponent component, function, element 头部组件。可以是 React Component, 也可以是一个 render 函数,或者渲染好的 element。
columnWrapperStyle style object 如果设置了多列布局(即将numColumns值设为大于 1 的整数),则可以额外指定此样式作用在每行容器上。
extraData any 如果有除data以外的数据用在列表中(不论是用在renderItem还是头部或者尾部组件中),请在此属性中指定。同时此数据在修改时也需要先修改其引用地址(比如先复制到一个新的 Object 或者数组中),然后再修改其值,否则界面很可能不会刷新。
getItemLayout function getItemLayout是一个可选的优化,用于避免动态测量内容尺寸的开销,不过前提是你可以提前知道内容的高度。
horizontal boolean 设置为 true 则变为水平布局模式。
inverted boolean 翻转滚动方向。实质是将 scale 变换设置为-1。
onEndReached function 当列表被滚动到距离内容最底部不足onEndReachedThreshold的距离时调用。
onRefresh function 如果设置了此选项,则会在列表头部添加一个标准的RefreshControl控件,以便实现“下拉刷新”的功能。
refreshing boolean 在等待加载新数据时将此属性设为 true,列表就会显示出一个正在加载的符号。

实例

1. 逻辑代码


import React, {Component} from 'react';
import {
  StyleSheet, 
  SectionList,
  Text,
  View
} from 'react-native';

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      turnOn: true,
      turnOff: false
    }
  }
  render() {
    return (
      <View style = {styles.container}>
        <View style={styles.title_view}>
          <Text style={styles.title_text}>
          SectionList
          </Text>
        </View>
        <View style={styles.list}>
          <SectionList
            sections = {[
              {title:'A',data:['阿年','阿诚']},
              {title:'B',data:['白白','白马','步青云']},
              {title:'C',data:['菜菜','超','chen']},
              {title:'D',data:['大叔','东子']},
              {title:'E',data:['EDIFIER']}
            ]}
            renderItem={({item}) =>
              <Text style={styles.item}>{item}</Text>
            }
            renderSectionHeader = {({section})=>
              <Text style={styles.sectionHeader}>{section.title}</Text>
            }
            keyExtractor = {(item,index) => index}
          />
        </View>
      </View> 
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5FCFF',
  },
  title_view:{
    flexDirection:'row',
    height:50,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor:'#27b5ee',
  },
  title_text: {
    fontSize:20,
    color:'white'
  },
  sectionHeader: {
    paddingTop: 2,
    paddingLeft: 10,
    paddingRight: 10,
    paddingBottom: 2,
    fontSize: 14,
    fontWeight: 'bold',
    backgroundColor: 'rgba(247,247,247,1.0)',
  },
  item: {
    padding: 10,
    fontSize: 18,
    height: 44,
  },
});

2. 效果图
sectionList_demo.jpg

2018-12-19 09:38:31 kimi985566 阅读数 628
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57874 人正在学习 去看看 李宁

React Native 列表的总结

FlatListSectionList都是React Native中高性能的列表组件。这些新的列表组件在性能方面都有了极大的提升, 其中最主要的一个是无论列表有多少行,它的内存使用都是常数级的。他们有着共同的特点:

  • 完全跨平台。
  • 行组件显示或隐藏时可配置回调事件。
  • 支持单独的头部组件。
  • 支持单独的尾部组件。
  • 支持自定义行间分隔线。
  • 支持下拉刷新。
  • 支持上拉加载。

实质两者都是基于VirtualizedList组件的封装,因此需要注意:

  • 当某行滑出渲染区域之外后,其内部状态将不会保留。请确保你在行组件以外的地方保留了数据。
  • 为了优化内存占用同时保持滑动的流畅,列表内容会在屏幕外异步绘制。这意味着如果用户滑动的速度超过渲染的速度,则会先看到空白的内容。这是为了优化不得不作出的妥协,而官方也在设法持续改进。
  • 本组件继承自PureComponent而非通常的Component,这意味着如果其props在浅比较中是相等的,则不会重新渲染。所以请先检查你的renderItem函数所依赖的props数据(包括data属性以及可能用到的父组件的state),如果是一个引用类型(Object或者数组都是引用类型),则需要先修改其引用地址(比如先复制到一个新的Object或者数组中),然后再修改其值,否则界面很可能不会刷新。
  • 默认情况下每行都需要提供一个不重复的key属性。你也可以提供一个keyExtractor函数来生成key。

当然,他们也有着一些不同的特性,下面来主要说明一下。

FlatList

FlatList是一个高性能的简单列表组件,用于显示一个垂直的滚动列表,其中的元素之间结构近似而仅数据不同。

除了上述的特性外,FlatList还有:

  • 支持水平布局模式。
  • 支持跳转到指定行(ScrollToIndex)

FlatList更适于长列表数据,且元素个数可以增删。和ScrollView不同的是,FlatList并不立即渲染所有元素,而是优先渲染屏幕上可见的元素。

FlatList组件必须的两个属性是datarenderItemdata是列表的数据源,而renderItem则从数据源中逐个解析数据,然后返回一个设定好格式的组件来渲染。

简单的例子:

<FlatList
  data={[{key: 'a'}, {key: 'b'}]}
  renderItem={({item}) => <Text>{item.key}</Text>}
/>

FlatList主要属性

属性 说明
data 为了简化起见,data属性目前只支持普通数组。如果需要使用其他特殊数据结构,例如immutable数组,请直接使用更底层的VirtualizedList组件。
getItemLayout getItemLayout是一个可选的优化,用于避免动态测量内容尺寸的开销,不过前提是你可以提前知道内容的高度。如果你的行高是固定的,getItemLayout用起来就既高效又简单,类似下面这样:getItemLayout={(data, index) => ( {length: 行高, offset: 行高 * index, index} )}
keyExtractor 此函数用于为给定的item生成一个不重复的key。Key的作用是使React能够区分同类元素的不同个体,以便在刷新时能够确定其变化的位置,减少重新渲染的开销。若不指定此函数,则默认抽取item.key作为key值。若item.key也不存在,则使用数组下标。
renderItem 根据行数据data渲染每一行的组件。

FlatList主要的方法

方法 说明 代码
scrollTo() 滚动到指定的x, y偏移处。第三个参数为是否启用平滑滚动动画。 scrollTo(([y]: number), object, ([x]: number), ([animated]: boolean));
scrollToEnd() 滚动到视图底部(水平方向的视图则滚动到最右边)。加上动画参数scrollToEnd({animated: true})则启用平滑滚动动画,或是调用scrollToEnd({animated: false})来立即跳转。如果不使用参数,则animated选项默认启用。 scrollToEnd(([options]: object));
flashScrollIndicators() 短暂地显示滚动指示器。 flashScrollIndicators();

实例:电影列表

以下是获取豆瓣电影数据并展示成列表的实例。

import React, {Component} from "react";

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

const REQUEST_URL =
    "http://api.douban.com/v2/movie/top250?count=50";

export class SampleAppMovies extends Component {

    static navigationOptions = {
        title: '电影列表页      ',
        headerStyle: {
            backgroundColor: '#8bc9ff',
        }
    };

    constructor(props) {
        super(props);
        this.state = {
            data: [],
            loaded: false
        };
        // 在ES6中,如果在自定义的函数里使用了this关键字,则需要对其进行“绑定”操作,否则this的指向会变为空
        // 像下面这行代码一样,在constructor中使用bind是其中一种做法(还有一些其他做法,如使用箭头函数等)
        this.fetchData = this.fetchData.bind(this);
    }

    componentDidMount() {
        this.fetchData();
    }

    fetchData() {
        fetch(REQUEST_URL)
            .then(response => response.json())
            .then(responseData => {
                this.setState({
                    data: this.state.data.concat(responseData.subjects),
                    loaded: true
                });
            });
    }

    static renderLoadingView() {
        return (
            <View style={styles.container}>
                <ActivityIndicator size="large" color="#8bc9ff"/>
            </View>
        );
    }

    static renderMovie({item}) {
        // { item }是一种“解构”写法,请阅读ES2015语法的相关文档
        // item也是FlatList中固定的参数名,请阅读FlatList的相关文档
        return (
            <View style={styles.container}>
                <Image
                    source={{uri: item.images.medium}}
                    style={styles.thumbnail}/>
                <View style={styles.rightContainer}>
                    <Text style={styles.title}>{item.title}</Text>
                    <Text style={styles.year}>{item.year}</Text>
                    <Text style={styles.introduce}>{"评分:"}
                        <Text
                            style={styles.ratingNum}>{item.rating.average === 0 ? "暂无评价" : item.rating.average}
                        </Text>
                    </Text>
                    <Text numberOfLines={1} style={styles.introduce}>{"导演:"}
                        <Text style={styles.info}>{item.directors[0].name}</Text>
                    </Text>
                    <Text numberOfLines={1} style={styles.introduce}>{"演员:"}
                        <Text style={styles.info}>{item.casts[0].name + " "}</Text>
                        <Text style={styles.info}>{item.casts[1].name + " "}</Text>
                        <Text style={styles.info}>{item.casts[2].name}</Text>
                    </Text>
                </View>
            </View>
        );
    }

    render() {
        if (!this.state.loaded) {
            return SampleAppMovies.renderLoadingView();
        }

        return (
            <FlatList
                data={this.state.data}
                ItemSeparatorComponent={ItemDivideComponent}
                renderItem={SampleAppMovies.renderMovie}
                style={styles.list}
                keyExtractor={(item, index) => item.id}
            />
        );
    }
}

class ItemDivideComponent extends Component {
    render() {
        return (
            <View style={{height: 0.5, backgroundColor: 'gray'}}/>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        flexDirection: "row",
        justifyContent: "center",
        backgroundColor: "#F5FCFF",
        padding: 5
    },
    rightContainer: {
        flex: 1,
        marginLeft: 10,
        flexDirection: 'column',
    },
    title: {
        color: '#000',
        fontWeight: 'bold',
        fontSize: 20,
        marginBottom: 9,
        justifyContent: 'flex-start',
    },
    year: {
        fontSize: 15,
        marginBottom: 5,
    },
    introduce: {
        flex: 1,
        fontSize: 14,
        color: '#000',
        marginBottom: 5,
    },
    ratingNum: {
        flex: 1,
        fontSize: 20,
        fontWeight: 'bold',
        color: '#ffad24'
    },
    info: {
        fontSize: 16,
        color: '#000',
        marginRight: 3,
        marginBottom: 5,
    },
    thumbnail: {
        width: 90,
        height: 145
    },
    list: {
        backgroundColor: "#FFF"
    }
});

电影列表

SectionList

如果要渲染的是一组需要分组的数据,也许还带有分组标签的,那么SectionList将是个不错的选择。

SectionList高性能的分组(section)列表组件。除了最一开始说明的共性外,它还有:

  • 支持分组的头部组件。
  • 支持分组的分隔线。
  • 支持多种数据源结构。

简单的例子:

<SectionList
  renderItem={({item}) => <ListItem title={item.title} />}
  renderSectionHeader={({section}) => <H1 title={section.key} />}
  sections={[ // homogeneous rendering between sections
    {data: [...], key: ...},
    {data: [...], key: ...},
    {data: [...], key: ...},
  ]}
/>

<SectionList
  sections={[ // heterogeneous rendering between sections
    {data: [...], key: ..., renderItem: ...},
    {data: [...], key: ..., renderItem: ...},
    {data: [...], key: ..., renderItem: ...},
  ]}
/>

SectionList主要属性

属性 说明
sections 用来渲染的数据,类似于FlatList中的data属性。
renderItem 用来渲染每一个section中的每一个列表项的默认渲染器。可以在section级别上进行覆盖重写。必须返回一个react组件。
keyExtractor 此函数用于为给定的item生成一个不重复的key。Key的作用是使React能够区分同类元素的不同个体,以便在刷新时能够确定其变化的位置,减少重新渲染的开销。若不指定此函数,则默认抽取item.key作为key值。若item.key也不存在,则使用数组下标。注意这只设置了每行(item)的key,对于每个组(section)仍然需要另外设置key。

SectionList主要的方法

方法 说明 代码
scrollToLocation() 将可视区内位于特定sectionIndex 或 itemIndex (section内)位置的列表项,滚动到可视区的制定位置。 scrollToLocation(params);parms在下面说明
recordInteraction() 主动通知列表发生了一个事件,以使列表重新计算可视区域。比如说当waitForInteractions 为 true 并且用户没有滚动列表时,就可以调用这个方法。不过一般来说,当用户点击了一个列表项,或发生了一个导航动作时,我们就可以调用这个方法。 recordInteraction();
flashScrollIndicators() 短暂地显示滚动指示器。 flashScrollIndicators();

Valid params keys are:

  • animated (boolean) - Whether the list should do an animation while scrolling. Defaults to true.
  • itemIndex (number) - Index within section for the item to scroll to. Required.
  • sectionIndex (number) - Index for section that contains the item to scroll to. Required.
  • viewOffset (number) - 一个以像素为单位,到最终位置偏移距离的固定值,比如为了弥补粘接的header所占据的空间。
  • viewPosition (number) - A value of 0 places the item specified by index at the top, 1 at the bottom, and 0.5 centered in the middle.

对于scrollToLocation(params);还有需要注意的地方: 如果没有设置getItemLayout或是onScrollToIndexFailed,就不能滚动到位于外部渲染区的位置。

SectionList注意点

对于计算滑动到那个点,可以使用scrollToLocation

this.sectionList.scrollToLocation({
  sectionIndex: 2,
  itemIndex: 2,
viewOffset: 30,
})
....
<SectionList
    ref={ref => this.sectionList = ref}
/>

但是如果要调用scrollToLocation的时候很可能页面还没渲染好,RN并不知道需要滚动到哪个位置,这个时候需要配合getItemLayout来使用。如果在sectionList中使用了该属性,RN会调用此方法计算列表中各项的显示位置,从而提前得知怎么处理滚动。

getItemLayout={(data, index) => ({
    index,
    offset: OFFSET_FROM_TOP,
    length: ITEM_HEIGHT.
    })
  }

不过对于SectionList计算滚动到那个点的位置是比较困难的,要计算section头部高度,也要计算item的高度,同时如果存在下画线也需要考虑在内,这就如果滑动时可能会出现偏移。

在这里可以使用库rn-section-list-get-item-layout来帮助我们解决问题:

...
constructor(props) {
    super(props)

    this.getItemLayout = sectionListGetItemLayout({
      // The height of the row with rowData at the given sectionIndex and rowIndex
      getItemHeight: (rowData, sectionIndex, rowIndex) => sectionIndex === 0 ? 100 : 50,

      // These four properties are optional
      getSeparatorHeight: () => 1 / PixelRatio.get(), // The height of your separators
      getSectionHeaderHeight: () => 20, // The height of your section headers
      getSectionFooterHeight: () => 10, // The height of your section footers
      listHeaderHeight: 40, // The height of your list header
    })
  }
...

实例:城市选择列表

import React, {Component} from 'react';
import {SectionList, StyleSheet, Text, ToastAndroid, TouchableOpacity, View} from 'react-native';
import sectionListGetItemLayout from 'react-native-section-list-get-item-layout'
import _ from 'lodash';
import cityData from '../json/city.json'

const ITEM_HEIGHT = 45;

//城市字母
const letters = _
    .range('A'.charCodeAt(0), 'Z'.charCodeAt(0) + 1)
    .map(n => String.fromCharCode(n).substr(0));

_.pull(letters, 'O', 'V');

//城市的数组
let city = [];

export class CitySelectList extends Component {

    static navigationOptions = {
        title: '列表页      ',
        headerStyle: {
            backgroundColor: '#8bc9ff',
        }
    };

    constructor(props) {
        super(props);

        this.getItemLayout = sectionListGetItemLayout({
            getItemHeight: (rowData, sectionIndex, rowIndex) => ITEM_HEIGHT,
            getSeparatorHeight: () => 0,
            getSectionHeaderHeight: () => ITEM_HEIGHT,
            getSectionFooterHeight: () => 0,
            listHeaderHeight: 0
        })
    }

    componentWillMount() {
        //把城市放到对应的字母中
        for (let j = 0; j < letters.length; j++) {

            let each = [];

            for (let i = 0; i < cityData.CITIES.length; i++) {
                if (letters[j] === cityData.CITIES[i].name_en.substr(0, 1)) {
                    each.push(cityData.CITIES[i].name);
                }
            }

            let _city = {};
            _city.key = letters[j];
            _city.data = each;

            city.push(_city)
        }

        //同步城市信息
        this.setState({
            data: city
        })
    }

    //滑动到
    scrollTo(index) {
        this.sectionListRef.scrollToLocation({
            animated: true,
            sectionIndex: index,
            itemIndex: 0,
            viewPosition: 0,
            viewOffset: ITEM_HEIGHT
        });
    }

    //右侧城市首字母列表
    renderLetters(letter, index) {
        return (
            <TouchableOpacity key={index} activeOpacity={0.6} onPress={() => {
                this.scrollTo(index)
            }}>
                <Text style={styles.letterText}>{letter}</Text>
            </TouchableOpacity>
        )
    }

    //城市Item
    renderCityItem(item) {
        return (
            <TouchableOpacity activeOpacity={0.6} onPress={() => {
                ToastAndroid.show(item, ToastAndroid.SHORT)
            }}>
                <Text style={styles.item}>{item}</Text>
            </TouchableOpacity>
        )
    }

    render() {
        return (
            <View style={styles.container}>
                <SectionList
                    sections={this.state.data}
                    initialNumToRender={383}
                    ref={ref => (this.sectionListRef = ref)}
                    renderItem={({item}) => this.renderCityItem(item)}
                    renderSectionHeader={({section}) => <Text style={styles.sectionHeader}>{section.key}</Text>}
                    keyExtractor={(item, index) => index}
                    refreshing={false}
                    getItemLayout={this.getItemLayout}
                    stickySectionHeadersEnabled={true}
                    showsVerticalScrollIndicator={false}
                />
                <View style={styles.letters}>
                    {letters.map((letter, index) => this.renderLetters(letter, index))}
                </View>
            </View>
        )
            ;
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1
    },
    sectionHeader: {
        paddingTop: 5,
        paddingLeft: 10,
        paddingRight: 10,
        paddingBottom: 5,
        fontSize: 18,
        fontWeight: 'bold',
        color: '#000',
        height: ITEM_HEIGHT,
        backgroundColor: '#8bc9ff',
    },
    item: {
        padding: 10,
        fontSize: 18,
        height: ITEM_HEIGHT,
    }, letters: {
        position: 'absolute',
        top: 0,
        bottom: 0,
        right: 10,
        backgroundColor: 'transparent',
        justifyContent: 'center',
        alignItems: 'center',
    }, letterText: {
        padding: 2,
        fontSize: 13,
    }
});

城市列表

没有更多推荐了,返回首页