原创:有趣知识点摸索型文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容
目录
- 一、ReactNative
- 二、Weex
- 三、为什么选择用Flutter
- 四、Flutter开发语言Dart
一、ReactNative
从零入门ReactNative的建议
- 需要有
html
、js
、css
等前端编程语言 - 然后,先去理解
React
基本使用 - 搭建
ReactNative
开发环境,选用iOS/Android
先把项目运行起来。 - 学习
ReactNative
基本开发流程。 - 写页面必须先搞懂
flex
布局方式。 - 学习
ReactNative
相关组件。 - 深入学习
ReactNative
的原理等高级知识。 - 阅读源码,真正的搞懂它。
1、相关命令
搭建环境
依赖 Node
环境
brew install node
node -v 查看node版本
sudo npm cache clean -f 清除缓存
sudo n stable 升级到稳定版本
sudo npm install -g n 按照node
安装 watchman
。其是 facebook
的一个开源项目,它开源用来监视文件并且记录文件的改动情况,当文件变更它可以触发一些操作,例如执行一些命令等等。
brew install watchman
创建项目
npx react-native init xxx_name_xxx
通过代码启动工程或者直接使用 Xcode 启动工程编译调试
yarn iOS
react-native
相关命令
react-native init MyAppName --version 0.64.0
react-native --version 查看版本
npx react-native info 命令查看当前的版本
2、工程目录
- 工程入口代码文件
index.js
,类似 iOS 中的main.m
文件。 - App组件定义
App.js
,当然也可以新建文件定义组件,类似 iOS 中的AppDelegate.m
文件。 - iOS 和 Android 两个文件夹是原生相关的工程。
-
package.json
工程的配置文件,定义脚本命令、包依赖、测试等相关 - 相关依赖库
node_modules
-
App.json
RN
模块的配置。其中name
配置字符串,必须和RCTRootView
初始化initWithBridge:moduleName:initialProperties:
用到的moduleName
一样。
{ "name": "HelloFirstRNDemo",
"displayName": "HelloFirstRNDemo"}
3、开发基本流程
ReactNative 代码
1、在App.js
中,导入 RN
相关的组件,其中包括系统组件与自定义组件的导入
import React from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
Text,
useColorScheme,
View,
} from 'react-native';
2、在App.js
中,自定义组件,即搭建页面。
class HelloWorld extends React.Component {
render() {
return (
<SafeAreaView>
<Text style={styles.title}> hello my world! </Text>
<ScrollView>
<Section> section 1</Section>
</SafeAreaView>
)
}
}
3、在App.js
中,定义样式,推荐统一定义样式,RN
使用布局方式为Flex
布局。
const styles = StyleSheet.create({
title: {
fontSize: 42,
},
});
4、在index.js
中,组件的注册。
import {AppRegistry} from 'react-native';
import {HelloWorld} from './App';
import {name as appName} from './app.json';
// 第一个参数:应用名称,第二个参数:组件名。
AppRegistry.registerComponent(appName, () => HelloWorld);
原生端(iOS为例)
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
// 其中 self 为实现 RCTBridgeDelegate 的实例,必须实现方法如下:
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
NSURL *url = nil;
#if DEBUG
url = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
url = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
return url;
}
初始化ReactNative
代码的加载容器类RCTRootView
。
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"FirstRNDemo"
initialProperties:nil];
二、Weex
1、概述
阿里巴巴在Qcon大会上宣布跨平台移动开发工具 Weex
开放内测邀请。Weex
能够完美兼顾性能与动态性,让移动开发者通过简捷的前端语法写出 Native
级别的性能体验,并支持 iOS、安卓、YunOS 及 Web 等多端部署。
近一年来,ReactNative
和 Weex
这些跨平台技术对 Native
开发者来说,冲击是巨大的。Native
在开发App的时候存在一些弊端,比如客户端需要频繁更新,iOS更新时间还要受到审核的牵制;iOS、Android 和前端同时开发同一个需求,在人员成本上消耗大;Hybrid
的性能和 Native
相比又差了一点。ReactNative
和 Weex
的出现,就是为了解决这些痛点的。
Weex
从出生那天起,仿佛就是和 ReactNative
是一对。ReactNative
宣称 “Learn once, write anywhere”,而 Weex
宣称“Write Once, Run Everywhere”。Weex
从出生那天起,就被给予了一统三端的厚望。ReactNative
可以支持 iOS、Android,而 Weex
可以支持iOS、Android、HTML5。一统三端就解决了前言里面说的第二个痛点,同时开发浪费人员成本的问题。
Native
移动开发者只需要在本地导入 Weex
的 SDK,就可以通过 HTML/CSS/JavaScript
网页的这套编程语言来开发 Native
级别的 Weex
界面。这意味着可以直接用现有 Web
开发的编辑器和 IDE
的代码补全、提示、检查等功能。从而也给前端人员开发 Native
端,较低的开发成本和学习成本。
Weex
是一种轻量级、可扩展、高性能框架。集成也很方便,可以直接在 HTML5
页面嵌入,也可嵌在原生UI中。由于和 ReactNative
一样,都会调用 Native
端的原生控件,所以在性能上比 Hybrid
高出一个层次。这就解决了前言里面所说的第三个痛点,性能问题。
Weex
非常轻量,体积小巧,语法简单,方便接入和上手。ReactNative
官方只允许将 ReactNative
基础js
库和业务JS
一起打成一个 JS bundle
,没有提供分包的功能,所以如果想节约流量就必须制作分包打包工具。而 Weex
默认打的 JS bundle
只包含业务 JS
代码,体积小很多,基础 JS
库包含在 Weex SDK
中,这一点 Weex
与 Facebook 的 React Native
相比,Weex
更加轻量,体积小巧。把 Weex
生成的 JS bundle
轻松部署到服务器端,然后 Push
到客户端,或者客户端请求新的资源即可完成发布。如此快速的迭代就解决了前言里面说的第一个痛点,发布无法控制时间。
Weex
中 Native
组件和 API 都可以横向扩展,业务方可去中心化横向灵活化定制组件和功能模块。并且还可以直接复用 Web
前端的工程化管理和监控性能等工具。
2、工作原理
上图是官方给的一张原理图,Weex
是如何把JS打包成JS Bundle
的原理本篇文章暂时不涉及。本篇文章会详细分析Weex
是如何在Native
端工作的。笔者把Native
端的原理再次细分,如下图:
Weex
可以通过自己设计的DSL
,书写.we
文件或者.vue
文件来开发界面,整个页面书写分成了3段,template
、style
、script
,借鉴了成熟的 MVVM
的思想。
Weex
在性能方面,为了尽可能的提升客户端的性能,DSL
的Transformer
全部都放在了服务器端实现,Weex
会在服务器端将XML + CSS + JavaScript
代码全部都转换成JS Bundle
。服务器将JS Bundle
部署到Server
上和CDN
上。
Weex
和React Native
不同的是,Weex
把JS Framework
内置在SDK
里面,用来解析从服务器上下载的JS Bundle
,这样也减少了每个JS Bundle
的体积,不再有React Native
需要分包的问题。客户端请求完JS Bundle
以后,传给JS Framework
,JS Framework
解析完成以后会输出Json
格式的Virtual DOM
,客户端Native
只需要专心负责 Virtual DOM
的解析和布局、UI 渲染。然而这一套解析,布局,渲染的逻辑SDK基本实现了。
最后Weex
支持三端一致,服务器上的一份JS Bundle
,通过解析,实现iOS/Android/HTML5
三端的一致性。
三、为什么选择用Flutter
目前跨端主流的框架Flutter
、Hybird
、RN
、Weex
。其中除了Flutter
, 其他三个都属于Web
型,即HTML5
+ CSS
+ JavaScript
实现。
React-Native、Weex 核心是通过 Javascript 开发,执行时需要 Javascript 解释器,UI是通过原生控件渲染。Flutter 与用于构建移动应用程序的其它大多数框架不同,因为 Flutter 既不使用WebView,也不使用操作系统的原生控件。 相反,Flutter 使用自己的高性能渲染引擎来绘制widget
。Flutter 使用C、C ++、Dart 和 Skia
(2D渲染引擎)构建。在 iOS上,Flutter 引擎的C/C ++ 代码使用 LLVM 编译,任何 Dart 代码都是 AOT 编译为本地代码的,Flutter 应用程序使用本机指令集运行(不涉及解释器)。而在 Android 下,Flutter 引擎的 C/C ++ 代码是用 Android 的NDK 编译的,任何 Dart 代码都是 AOT 编译成本地代码的,Flutter 应用程序依然使用本机指令集运行(不涉及解释器)。因此,Flutter 能达到原生应用一样的性能。
Web 型方案的优点:
- 开发迅速
- 复用前端生态
- 支持热更
Web 型方案的缺点:
- 一些复杂的交互无法带来很好的体验,比如手势、视频播放。
- 在不打离线包的前提下,进入页面会有白屏。
- iOS 与Android 双端渲染效果存在差异。
跨端大生态
- JSCore 虚拟机(虚拟 DOM)
- Hybird
- React Native
- Weex
- Dart 虚拟机(自绘引擎)
1、Flutter 原理
相比 React Native
和 Weex
,Flutter
实现跨平台采用了更为彻底的方案。它既没有采用WebView
也没有采用 JavaScript
,而是自己实现了一台UI框架,然后直接系统更底层渲染系统上画UI。所以它采用的开发语言不是JS
,而是Dart
(Dart
是面向对象的、类定义的、单继承的语言)。它的语法类似C语言,可以转译为JavaScript
,支持接口(interfaces
)、混入(mixins
)、抽象类(abstract classes
)、具体化泛型(reified generics
)、可选类型(optional typing
)和(sound type syste
)。 据称Dart
语言可以编译成原生代码,直接跟原生通信。
同时,Flutter
将UI组件和渲染器从平台移动到应用程序中,这使得它们可以自定义和可扩展。Flutter唯一要求系统提供的是canvas
,以便定制的UI组件可以出现在设备的屏幕上,以及访问事件(触摸,定时器等)和服务(位置、相机等)。这是Flutter
可以做到跨平台而且高效的关键。另外Flutter
学习了RN
的UI编程方式,引入了状态机,更新UI时只更新最小改变区域。
系统的UI框架可以取代,但是系统提供的一些服务是无法取代的。Flutter
在跟系统service
通信方式,采用的是一种类似插件式的方式,或者有点像远程过程调用RPC
方式,这种方式据说也要比RN的桥接方式高效。
2、Flutter 和 React Native 底层框架对比
React-Native
、Weex
核心是通过 Javascript
开发,执行时需要 Javascript
解释器,UI 是通过原生控件渲染。Flutter
与用于构建移动应用程序的其它大多数框架不同,因为 Flutter
既不使用 WebView
,也不使用操作系统的原生控件。 相反,Flutter
使用自己的高性能渲染引擎来绘制 widget
。Flutter
使用 C、C ++、Dart
和 Skia
(2D渲染引擎)构建。
Skia
是一个 2D的绘图引擎库,Chrome
和 Android
均采用 Skia
作为绘图引擎。Android
自带了 Skia
,所以 Flutter Android SDK
要比 iOS SDK
小很多。
在 ReactNative
中,引入了虚拟 DOM
来减少DOM
的回流和重绘,系统将虚拟 DOM
与真正的 DOM
进行比较,生成一组最小的更改,然后执行这些更改,以更新真正的 DOM
。最后,平台重新绘制真实的 DOM
到画布中。
React Native
是移动开发的一大进步,并且是 Flutter
的灵感来源,但 Flutter
更进一步。 在 Flutter
中,UI 组件和渲染器已经从平台中集成到用户的应用程序中。没有系统 UI 组件可以操作,所以原来虚拟控件树的地方现在是真实的控件树,Flutter
渲染 UI 控件树并将其绘制到平台画布上。
如果说非要比较 Flutter
和 React Native
的优势,可以参考下面几点:
UI 一致性
Flutter
因为是自己做的渲染,因此在iOS和Android的效果基本完全一致。 React Native
存在将RN
控件转换为对应平台原生控件的过程,存在一定的差异。
动态化技术
Flutter
使用的Dart
语言,支持AOT
和JIT
两种模式,在Dev
时候,通过JIT
可以实现热重载,开发者可以即时的看到代码修改的效果。而在Release Build
的时候,通过AOT
事先编译,来最大化的优化性能。因此目前Flutter
不支持代码的热更新。
ReactNative
的代码通过加载 JSBundle.js
执行,JSBundle.js
可以保存在本地,也可以通过远程加载。目前有很多RN
的热更新方案供选择。
App体积
Flutter iOS
空项目 30M左右,Android
空项目 7M左右。 (iOS需要额外集成Skia
) React Native iOS
空项目 3M左右,Android
20M左右。(Android
会加入OKHttp
导致体积增大)
Flutter
部分的底层功能在 Android
系统上已经有实现,因此 Android
上适配要好(RN
在 Android
上有可能遇到兼容性问题)。
Flutter的优势
运行效率上,Flutter
和ReactNative
都可以达到理论上的60帧的刷新率,来实现「Native
般的流畅体验」,Flutter
是全Native
在执行,基于底层代码(Android
上为 C++ with NDK
,iOS 上为 C++ with LLVM
),而ReactNative
是Native
控件 + JavaScript
代码,实际性能上,Flutter
应该优于ReactNative
,据官方文档,Flutter
可以在支持的设备上达到120FPS,而ReactNative
的文档上,只提到了可以达到60FPS。
兼容性上,Flutter
提供的 widget
都是基于 skia
来实现和精心定制的,与具体平台没关,所以能保持很高的跨 os
跨 os version
的兼容性。 Flutter
从更基础的层去抹平平台差异,站在了更宽广、更可控的一个基础平台上去演变和发展。 Flutter
官方提供了大部分 Material Design
控件的实现(甚至比 Android Design Support
实现的更多)。
四、Flutter开发语言Dart
1、为什么要使用Dart语言
学习Flutter
就不得不提到Dart
,那Flutter
和Dart
有什么关系?确实有关系,早期的Flutter
团队评估了十多种语言,并选择了Dart
,因为它符合他们构建用户界面的方式。
Dart
能成为Flutter
不可或缺的一部分,根本原因还是因为其具有以下特性:
-
Dart
是AOT
(Ahead Of Time
)编译的,编译成快速、可预测的本地代码,使Flutter
几乎都可以使用Dart
编写。这不仅使Flutter
变得更快,而且几乎所有的东西(包括所有的小部件)都可以定制 -
Dart
也可以JIT
(Just In Time
)编译,开发周期异常快,工作流颠覆常规(包括Flutter
流行的亚秒级有状态热重载) -
Dart
可以更轻松地创建以60fps运行的流畅动画和转场。Dart
可以在没有锁的情况下进行对象分配和垃圾回收。就像JavaScript
一样,Dart
避免了抢占式调度和共享内存(因而也不需要锁)。由于Flutter
应用程序被编译为本地代码,因此它们不需要在领域之间建立缓慢的桥梁(例如,JavaScript
到本地代码)。它的启动速度也快得多 -
Dart
使Flutter
不需要单独的声明式布局语言,如JSX
或XML
,或单独的可视化界面构建器,因为Dart
的声明式编程布局易于阅读和可视化。所有的布局使用一种语言,聚集在一处,Flutter
很容易提供高级工具,使布局更简单 - 开发人员发现
Dart
特别容易学习,因为它具有静态和动态语言用户都熟悉的特性
2、编译与执行
历史上,计算机语言分为两组:静态语言(例如C,其中变量类型是在编译时静态指定的)和动态语言(例如 Smalltalk
和JavaScript
,其中变量的类型可以在运行时改变)。静态语言通常编译成目标机器的本地机器代码(或汇编代码)程序,该程序在运行时直接由硬件执行。动态语言由解释器执行,不产生机器语言代码。
当然,事情后来变得复杂得多。虚拟机(VM
)的概念开始流行,它其实只是一个高级的解释器,用软件模拟硬件设备。虚拟机使语言移植到新的硬件平台更容易。因此,VM
的输入语言常常是中间语言。例如,一种编程语言(如Java
)被编译成中间语言(字节码),然后在VM
(JVM
)中执行。
另外,现在有即时(JIT
)编译器。JIT
编译器在程序执行期间运行,即时编译代码。原先在程序创建期间(运行时之前)执行的编译器现在称为AOT
编译器。
一般来说,只有静态语言才适合AOT
编译为本地机器代码,因为机器语言通常需要知道数据的类型,而动态语言中的类型事先并不确定。因此,动态语言通常被解释或JIT
编译。
在开发过程中AOT
编译,开发周期(从更改程序到能够执行程序以查看更改结果的时间)总是很慢。但是AOT
编译产生的程序可以更可预测地执行,并且运行时不需要停下来分析和编译。AOT
编译的程序也更快地开始执行(因为它们已经被编译)。
相反,JIT
编译提供了更快的开发周期,但可能导致执行速度较慢或时快时慢。特别是,JIT
编译器启动较慢,因为当程序开始运行时,JIT
编译器必须在代码执行之前进行分析和编译。研究表明,如果开始执行需要超过几秒钟,许多人将放弃应用。
3、Dart的编译与执行
在创造Dart
之前,Dart
团队成员在高级编译器和虚拟机上做了开创性的工作,包括动态语言(如JavaScript
的V8引擎和Smalltalk
的Strongtalk
)以及静态语言(如用于Java
的Hotspot
编译器)。他们利用这些经验使Dart
在编译和执行方面非常灵活。
Dart
是同时非常适合AOT
编译和JIT
编译的少数语言之一(也许是唯一的“主流”语言)。支持这两种编译方式为Dart
和(特别是)Flutter
提供了显著的优势。
JIT
编译在开发过程中使用,编译器速度特别快。然后,当一个应用程序准备发布时,它被AOT
编译。因此,借助先进的工具和编译器,Dart
具有两全其美的优势:极快的开发周期、快速的执行速度和极短启动时间。
Dart
在编译和执行方面的灵活性并不止于此。例如,Dart
可以编译成JavaScript
,所以浏览器可以执行。这允许在移动应用和网络应用之间重复使用代码。开发人员报告他们的移动和网络应用程序之间的代码重用率高达70%。通过将Dart
编译为本地代码,或者编译为JavaScript
并将其与node.js
一起使用,Dart
也可以在服务器上使用。
最后,Dart
还提供了一个独立的虚拟机(本质上就像解释器一样),虚拟机使用Dart
语言本身作为其中间语言。
Dart
可以进行高效的AOT
编译或JIT
编译、解释或转译成其他语言。Dart
编译和执行不仅非常灵活,而且速度特别快。
即使编译后的代码也可能需要一个接口来与平台代码进行交互,并且这也可以称为桥,但它通常比动态语言所需的桥快几个数量级。另外,由于Dart
允许将小部件等内容移至应用程序中,因此减少了桥接的需求。
4、布局
声明式布局语言:
Center(child:
Column(children: [
Text('Hello, World!'),
Icon(Icons.star, color: Colors.green),
])
)