自从android和iOS两大移动平台的确立以来,人们就一直在不懈地探索各种跨平台技术,希望于能用一个团队,一套代码高效地横跨两大平台,节省人力成本的同时,快速地完成产品的开发迭代,在快速发展和激烈竞争的移动互联网环境中占得优势。
如何跨平台
所有的跨平台技术方案概括起来无外乎是就是两个方面:
-
UI跨平台
包括控件、布局、动画、手势等的渲染和交互。 -
非UI平台API调用
桥接调用平台系统能力。如传感器、摄像头、设备信息、网络状态等。
技术划分
跨平台技术的核心在于UI跨平台。根据实现UI跨平台方式的不同,目前的跨平台技术可以分为下面三类:
H5应用或Hybrid应用
-
原理:基于H5技术,通过WebView渲染UI,通过JS桥接和原生交互
-
优势:开发效率、动态性、包大小
-
劣势:性能和用户体验
-
使用情况:非常广泛,个别深度使用(优化性能)
用Web技术开发原生应用
-
原理:基于类HTML+CSS+JS开发界面和业务逻辑,并转换为原生视图进行渲染。
-
框架:React Native、Weex
-
优势:前端技术栈、开发效率、动态性
-
劣势:长列表滑动性能、跨端一致性
-
使用情况:比较广泛,国内国外,大厂小厂均有
Flutter
-
原理:基于跨平台高性能2D渲染引擎Skia绘制界面。DART在Debug模式下JIT编译,支持热重载;在Release模式下AOT编译成ARM汇编代码,执行效率高,调用原生API效率高;支持isolate多线程执行
-
优势:精美、接近原生的性能(闲鱼:平均52帧/秒,300毫秒页面加载时间)、跨端一致性
-
劣势:成熟度(框架本身,社区生态),包大小
-
使用情况:大厂都在研究和尝试
闲鱼、腾讯、京东、美团
React Native实例
React Native主要包括以下要点:
-
JavaScript
let var const 数字、布尔、字符串、undefined、null、对象 Array、Date、Error 闭包 箭头函数 -
React
JSX语法 JavaScript里嵌XML,用于描述UI。JSX中的JS表达式要{}包起来。 组件(React.Component) state状态 props属性 生命周期方法 constructor componentWillMount render componentDidMount componentWillUnmount 常用UI组件 View Image Text FlatList 声明式UI 样式分离 Flexbox布局 -
状态管理 redux
-
网络请求 Fetch API (Promise或async、await)
-
依赖管理
NPM install Pod install -
第三方组件
https://github.com/react-native-community https://react.parts/?collection=React+Native https://github.com/ant-design/ant-design-mobile-rn/ https://github.com/Meituan-Dianping/beeshell -
调试\热重载
IDE CLI Chrome Shake -
性能优化
https://reactnative.cn/docs/performance/ https://blog.csdn.net/sinat_17775997/article/details/80852485 https://mp.weixin.qq.com/s/Z1GUJW3qBqDGH1jnGt5qAg
下面我们结合一个简单的RN实例代码学习和了解RN的一些基本概念和知识。
例子代码:RNDemo
这个例子非常简单,只有一个页面,它从网络请求数据,然后在列表里显示数据。
// 应用入口
AppRegistry.registerComponent(appName, () => App);
export default class App extends React.Component {
// 构造函数:初始化属性和变量
constructor(props) {
super(props);
this.state = {
data: [],
loaded: false
};
// 在ES6中,如果在自定义的函数里使用了this关键字,则需要对其进行“绑定”操作,否则this的指向会变为空
this.fetchData = this.fetchData.bind(this);
this.renderMovie = this.renderMovie.bind(this);
}
// 组件已挂载:请求数据
componentDidMount() {
this.fetchData();
}
// 绘制方法:返回JSX视图结构
render() {
if (!this.state.loaded) {
return this.renderLoadingView();
}
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<FlatList
data={this.state.data}
renderItem={this.renderMovie}
style={{ backgroundColor: "#F5FCFF" }}
keyExtractor={item => item.id}
/>
</SafeAreaView>
</>
);
}
// 异步网络请求
async fetchData() {
try {
let response = await fetch(REQUEST_URL);
let responseData = await response.json();
this.setState({
data: this.state.data.concat(responseData.movies),
loaded: true
});
} catch (error) {
console.error(error);
}
}
}
//样式
var styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
backgroundColor: "#F5FCFF",
paddingHorizontal: 15,
paddingVertical: 10
},
});
调用this.setState方法会触发render()方法重绘。
Flutter实例
Flutter主要包括以下要点:
-
Dart
var final const static 可选位置参数[]、可选命名参数{} 箭头函数 Class 命名构造函数 工厂构造函数factory 重定向构造方法: Map List Set 级连 .. Future async await -
Widget
无状态StatelessWidget 有状态StatefullWidget & State 生命周期方法 initState build dispose -
万物皆Widget
Widget非常轻量,仅包含不可变的配置信息。
基础Widget Button Text Image TextField 布局Widget Row Column Center Padding Container Expanded 滚动Widget ListView GridView PageView 装饰Widget BoxDecoration Opacity https://flutter.cn/docs/development/ui/widgets https://flutter.cn/docs/reference/widgets https://github.com/alibaba/flutter-go -
两套UI风格 Material Cupertino
对应android默认的物化风格和iOS系统的原生风格。
-
声明式UI
-
网络请求 dio
-
依赖管理
包含原生调用的package称为plugin。
package plugin flutter pub get -
第三方包
-
调试\热重载
IDE CLI DevTool -
性能优化
Flutter本身已具备较好的性能,性能方面需要关注的点不多。
下面我们结合一个简单的Flutter实例代码学习和了解Flutter的一些基本概念和知识。
例子代码:FlutterDemo
这个例子非常简单,只有一个页面,它从网络请求数据,然后在列表里显示数据。
// 应用入口
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// 构建方法:这里返回应用根Widget
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: FutureBuilderPage(),
);
}
}
class FutureBuilderPage extends StatefulWidget {
// 创建状态对象
@override
_FutureBuilderPageState createState() => _FutureBuilderPageState();
}
class _FutureBuilderPageState extends State<FutureBuilderPage> {
Future future;
// 初始化方法
@override
void initState() {
super.initState();
future = getListData(); // 请求网络数据
}
// 构建方法:返回Widget树
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('知识体系')),
body: buildFutureBuilder(),
);
}
// 使用FutureBuilder处理异步网络请求
FutureBuilder<List<Data>> buildFutureBuilder() {
return FutureBuilder<List<Data>>(
future: future,
builder: (context, snapshot) {
//在这里根据快照的状态,返回相应的widget
if (snapshot.connectionState == ConnectionState.done) {// 请求完成
if (snapshot.hasError) {// 请求失败
return Center(
child: Text("ERROR"),
);
} else if (snapshot.hasData) {// 请求成功,数据用列表显示
List<Data> list = snapshot.data;
return RefreshIndicator(
child: buildListView(context, list), onRefresh: refresh);
}
}
// 正在加载
return Center(
child: CircularProgressIndicator(),
);
});
}
buildListView(BuildContext context, List<Data> list) {
return ListView.builder(// 构建长列表
itemCount: list.length,
itemBuilder: (context, index) {
Data item = list[index];
StringBuffer str = new StringBuffer();
for (Children children in item.children) {
str.write(children.name + " ");
}
return Column(
children: [
ListTile(
title: Text(item.name),
subtitle: Text(str.toString()),
trailing: IconButton(
icon: Icon(
Icons.navigate_next,
color: Colors.grey,
),
onPressed: () {})
),
Divider()
]
);
});
}
//获取网络数据,利用dio库进行网络请求,拿到数据后利用json_serializable解析
//并将列表的数据包装在一个future中
Future<List<Data>> getListData() async {
var dio = new Dio();
Response response = await dio.get("http://www.wanandroid.com/tree/json");
Map<String, dynamic> map = response.data;
Entity entity = Entity.fromJson(map);
return entity.data;
}
//刷新数据,重新设置future就行了
Future refresh() async {
setState(() {
future = getListData();
});
}
}
对比
-
学习成本、接入成本
React Native对于前端人员来说学习成本是比较低的。Flutter个人感觉学习成本比RN略高一点,自带的Widget比较多。 接入成本我觉得两者差不多。
-
框架成熟度
目前来说,两者都是比较稳定的。但从GitHub上issues的数量上看React Native要更稳定,毕竟RN出现的较早。不过Flutter的迭代速度明显更快,毕竟Google的实力更强,相信Flutter会很快反超。
-
社区成熟度、行业采用度
这方面也是React Native更有优势一些,同样得益于它时间上出现的更早。两三年前作为唯一靠谱的跨平台方案,它得到了很多大厂的认可和采用,这些大厂也在RN生态的建设上投入了比较多的精力。有人统计中国市场上排名前100的应用有十几个使用了RN或Weex。而Flutter虽然时间较晚,但发展很快,目前,开发中常用的一些模块都已有成熟的第三方packages。大小公司也都投入了很大的热情去研究和尝试。
-
开发体验
开发体验上个人觉得两者差不多。都支持热重载。提供的IDE插件,命令行工具,可视化DevTool也很相似。
-
一致性
React Native由于采用原生视图渲染,我们常常需要去适配和桥接两个平台的差异性。且RN不同平台JS引擎的不一致,也埋下了一些隐患。而Flutter则是在更底层抹平了平台差异,使用统一的Skia引擎渲染,所以从原理上来说Flutter的跨端一致性会更好。而实际体验中Flutter也确实更有优势。
-
动态性
动态性是React Native最大的优势,这也是很多电商、外卖大厂深度使用RN的主要原因。而Flutter仅在Android上支持动态化。
-
包大小
这里有一个对比数据。我们可以看到,同等条件下:
RN的包iOS比Android小很多。因为iOS内置了JavaScriptCore。
Flutter包Android比iOS小很多。因为Android内置了Skia引擎。
-
内存/CPU/FPS
Flutter在数据和实际体验方面都优于RN。详情可以查看下面的链接: www.yuque.com/xytech/flut…
-
初始化时长,页面渲染时长
两者直接的对比比较少,但从两者与原生的对比文章来看,Flutter这方面的性能更优。实际体验也是如此。
详细的对比信息可以参考下面的链接:
https://juejin.cn/post/6844903795965558792#heading-22
https://juejin.cn/post/6844903872130088974#heading-10
https://www.yuque.com/xytech/flutter/blquhk
总结
本文结合简单的实例代码对React Native和Flutter进行了学习和对比,让大家对目前两个主流的跨平台方案有一个初步的认识。其目的是希望给大家的技术选型提供一点帮助。具体到各自的项目,大家还是需要结合根据业务场景、人力情况、需求来决定是否使用跨平台方案,使用哪个跨平台方案。
- 对于UI和交互简单,性能要求不高,但动态性很重要的应用可以用H5或Hybrid的方式。
- 对于UI和交互较复杂,有一定的性能要求,动态性很重要的应用可以用React Native(或Weex)。
- 对于复杂的UI和交互,性能要求高,但动态性要求不是首要的应用可以用Flutter。
Flutter无疑站在了更高的起点上,但它也未必一统江湖。合适的才是最好的。