ready
论坛版主
论坛版主
  • UID24
  • 粉丝0
  • 关注0
  • 发帖数433
  • 社区居民
  • 忠实会员
  • 原创写手
阅读:1168回复:6

React Native、Weex、Flutter

楼主#
更多 发布于:2024-07-24 14:50
React Native (简称 RN )是 Facebook 于 2015 年 4 月开源的跨平台移动应用开发框架,是 Facebook 早先开源的 Web 框架 React 在原生移动应用平台的衍生产物,目前支持 iOS 和 Android 两个平台。RN 使用JSX 语言(扩展后的 JavaScript,主要是可以在 JavaScript 中写 HTML标签)和 CSS 来开发移动应用。因此,熟悉 Web 前端开发的技术人员只需很少的学习就可以进入移动应用开发领域。

由于 RN 和 React 原理相通,并且 Flutter在应用层也是受 React 启发,很多思想也都是相通的,因此,我们有必要深入了解一下React原理。
React 是一个响应式的 Web 框架,我们先了解一下两个重要的概念:DOM 树与响应式编程。


#1. DOM树与控件树

文档对象模型(Document Object Model,简称DOM),是 W3C 组织推荐的处理可扩展标志语言的标准编程接口,一种独立于平台和语言的方式访问和修改一个文档的内容和结构。换句话说,这是表示和处理一个 HTML 或XML 文档的标准接口。简单来说,DOM 就是文档树,与用户界面控件树对应,在前端开发中通常指 HTML 对应的渲染树,但广义的 DOM 也可以指 Android 中的 XML 布局文件对应的控件树,而术语DOM操作就是指直接来操作渲染树(或控件树), 因此,可以看到其实 DOM 树和控件树是等价的概念,只不过前者常用于 Web 开发中,而后者常用于原生开发中。


#2. 响应式编程

React 中提出一个重要思想:状态改变则UI随之自动改变。而 React 框架本身就是响应用户状态改变的事件而执行重新构建用户界面的工作,这就是典型的 响应式 编程范式,下面我们总结一下 React 中响应式原理:
  • 开发者只需关注状态转移(数据),当状态发生变化,React 框架会自动根据新的状态重新构建UI。
  • React 框架在接收到用户状态改变通知后,会根据当前渲染树,结合最新的状态改变,通过 Diff 算法,计算出树中变化的部分,然后只更新变化的部分(DOM操作),从而避免整棵树重构,提高性能。
值得注意的是,在第二步中,状态变化后 React 框架并不会立即去计算并渲染 DOM 树的变化部分,相反,React会在 DOM 树的基础上建立一个抽象层,即虚拟DOM树,对数据和状态所做的任何改动,都会被自动且高效的同步到虚拟 DOM ,最后再批量同步到真实 DOM 中,而不是每次改变都去操作一下DOM。
为什么不能每次改变都直接去操作 DOM 树?这是因为在浏览器中每一次 DOM 操作都有可能引起浏览器的重绘或回流(重新排版布局,确定 DOM 节点的大小和位置):
  1. 如果 DOM 只是外观风格发生变化,如颜色变化,会导致浏览器重绘界面。
  2. 如果 DOM 树的结构发生变化,如尺寸、布局、节点隐藏等导致,浏览器就需要回流。
而浏览器的重绘和回流都是比较昂贵的操作,如果每一次改变都直接对 DOM 进行操作,这会带来性能问题,而批量操作只会触发一次 DOM 更新,会有更高的性能。




3. React Native

上文已经提到 React Native 是 React 在原生移动应用平台的衍生产物,那两者主要的区别是什么呢?其实,主要的区别在于虚拟 DOM 映射的对象是什么。React中虚拟 DOM 最终会映射为浏览器 DOM 树,而 RN 中虚拟 DOM会通过 JavaScriptCore 映射为原生控件。
JavaScriptCore 是一个JavaScript解释器,它在React Native中主要有两个作用:
  1. 为 JavaScript 提供运行环境。
  2. 是 JavaScript 与原生应用之间通信的桥梁,作用和 JsBridge 一样,事实上,在 iOS 中,很多 JsBridge 的实现都是基于 JavaScriptCore 。
而 RN 中将虚拟 DOM 映射为原生控件的过程主要分两步:
  1. 布局消息传递; 将虚拟 DOM 布局信息传递给原生;
  2. 原生根据布局信息通过对应的原生控件渲染;

至此,React Native 便实现了跨平台。 相对于混合应用,由于React Native是 原生控件渲染,所以性能会比混合应用中 H5 好一些,同时 React Native 提供了很多原生组件对应的 Web 组件,大多数情况下开发者只需要使用 Web 技术栈 就能开发出 App。我们可以发现,这样也就做到了维护一份代码,便可以跨平台了。
ready
论坛版主
论坛版主
  • UID24
  • 粉丝0
  • 关注0
  • 发帖数433
  • 社区居民
  • 忠实会员
  • 原创写手
沙发#
发布于:2024-07-24 14:52
Weex
Weex 是阿里巴巴于 2016 年发布的跨平台移动端开发框架,思想及原理和 React Native 类似,底层都是通过原生渲染的,不同是应用层开发语法 (即 DSL,Domain Specific Language):Weex 支持 Vue 语法和 Rax 语法,Rax 的 DSL(Domain Specific Language) 语法是基于 React JSX 语法而创造,而 RN 的 DSL 是基于 React 的,不支持 Vue。



#5. 小结
5. 小结
JavaScript 开发 + 原生渲染 的方式主要优点如下:

采用 Web 开发技术栈,社区庞大、上手快、开发成本相对较低。
原生渲染,性能相比 H5 提高很多。
动态化较好,支持热更新。
不足:

渲染时需要 JavaScript 和原生之间通信,在有些场景如拖动可能会因为通信频繁导致卡顿。
JavaScript 为脚本语言,执行时需要解释执行 (这种执行方式通常称为 JIT,即 Just In Time,指在执行时实时生成机器码),执行效率和编译类语言(编译类语言的执行方式为 AOT ,即 Ahead Of Time,指在代码执行前已经将源码进行了预处理,这种预处理通常情况下是将源码编译为机器码或某种中间码)仍有差距。
由于渲染依赖原生控件,不同平台的控件需要单独维护,并且当系统更新时,社区控件可能会滞后;除此之外,其控件系统也会受到原生UI系统限制,例如,在 Android 中,手势冲突消歧规则是固定的,这在使用不同人写的控件嵌套时,手势冲突问题将会变得非常棘手。这就会导致,如果需要自定义原生渲染组件时,开发和维护成本过高。
ready
论坛版主
论坛版主
  • UID24
  • 粉丝0
  • 关注0
  • 发帖数433
  • 社区居民
  • 忠实会员
  • 原创写手
板凳#
发布于:2024-07-24 15:15

Flutter

Flutter 是 Google 发布的一个用于创建跨平台、高性能移动应用的框架。Flutter 和 Qt mobile 一样,都没有使用原生控件,相反都实现了一个自绘引擎,使用自身的布局、绘制系统。
和 Qt mobile做一个对比:
  1. 生态:Flutter 生态系统发展迅速,社区非常活跃,无论是开发者数量还是第三方组件都已经非常可观。
  2. 技术支持:现在 Google 正在大力推广Flutter,Flutter 的作者中很多人都是来自Chromium团队,并且 Github上活跃度很高。另一个角度,从 Flutter 诞生到现在,频繁的版本发布也可以看出 Google 对 Flutter的投入的资源不小,所以在官方技术支持这方面,大可不必担心。
  3. 开发效率:一套代码,多端运行;并且在开发过程中 Flutter 的热重载可帮助开发者快速地进行测试、构建UI、添加功能并更快地修复错误。在 iOS 和 Android 模拟器或真机上可以实现毫秒级热重载,并且不会丢失状态。这真的很棒,相信我,如果你是一名原生开发者,体验了Flutter开发流后,很可能就不想重新回去做原生了,毕竟很少有人不吐槽原生开发的编译速度。

1.1.6 小结

本章主要介绍了目前移动开发中三种跨平台技术,现在我们从框架角度对比一下它们,如表1-1所示:
技术类型 UI渲染方式 性能 开发效率 动态化 框架代表
H5 + 原生 WebView渲染 一般 支持 Cordova、Ionic
JavaScript + 原生渲染 原生控件渲染 支持 React Native、
Weex
自绘UI + 原生 调用系统API渲染 Flutter高, Qt低 默认不支持 Qt、Flutter

表1-1: 跨平台技术对比
上表中开发语言主要指应用层的开发语言,而开发效率,是指整个开发周期的效率,包括编码时间、调试时间、以及排错、处理兼容性问题时间。动态化主要指是否支持动态下发代码和是否支持热更新。值得注意的是 Flutter 的Release 包默认是使用 Dart AOT 模式编译的,所以不支持动态化,但 Dart 还有 JIT 或 snapshot 运行方式,这些模式都是支持动态化的。
ready
论坛版主
论坛版主
  • UID24
  • 粉丝0
  • 关注0
  • 发帖数433
  • 社区居民
  • 忠实会员
  • 原创写手
地板#
发布于:2024-07-24 15:21
Flutter 是 Google 推出并开源的移动应用开发框架,主打跨平台、高保真、高性能。开发者可以通过 Dart 语言开发 App,一套代码同时运行在 iOS 和 Android平台。 Flutter 提供了丰富的组件、接口,开发者可以很快地为 Flutter 添加 Native(即原生开发,指基于平台原生语言来开发应用,flutter可以和平台原生语言混合开发) 扩展。下面我们整体介绍一下 Flutter 技术的主要特点。


1. 跨平台自绘引擎

Flutter 与用于构建移动应用程序的其他大多数框架不同,因为 Flutter 既不使用 WebView,也不使用操作系统的原生控件。 相反,Flutter 使用自己的高性能渲染引擎来绘制 Widget(组件)。这样不仅可以保证在 Android 和iOS 上 UI 的一致性,也可以避免对原生控件依赖而带来的限制及高昂的维护成本。
Flutter 底层使用 Skia 作为其 2D 渲染引擎,Skia 是 Google的一个 2D 图形处理函数库,包含字型、坐标转换,以及点阵图,它们都有高效能且简洁的表现。Skia 是跨平台的,并提供了非常友好的 API,目前 Google Chrome浏览器和 Android 均采用 Skia 作为其 2D 绘图引擎。
目前 Flutter 已经支持 iOS、Android、Web、Windows、macOS、Linux、Fuchsia(Google新的自研操作系统)等众多平台,但本书的示例和介绍主要是基于 iOS 和 Android 平台的,其他平台读者可以自行了解。


#2. 高性能

Flutter 高性能主要靠两点来保证:
第一:Flutter App 采用 Dart 语言开发。Dart 在 JIT(即时编译)模式下,执行速度与 JavaScript 基本持平。但是 Dart 支持 AOT,当以 AOT模式运行时,JavaScript 便远远追不上了。执行速度的提升对高帧率下的视图数据计算很有帮助。
第二:Flutter 使用自己的渲染引擎来绘制 UI ,布局数据等由 Dart 语言直接控制,所以在布局过程中不需要像 RN 那样要在 JavaScript 和 Native 之间通信,这在一些滑动和拖动的场景下具有明显优势,因为在滑动和拖动过程往往都会引起布局发生变化,所以 JavaScript 需要和 Native 之间不停地同步布局信息,这和在浏览器中JavaScript 频繁操作 DOM 所带来的问题是类似的,都会导致比较可观的性能开销。


#3. 采用Dart语言开发

这个是一个很有意思但也很有争议的问题,在了解 Flutter 为什么选择了 Dart 而不是 JavaScript 之前我们先来介绍一下之前提到过的两个概念:JIT 和 AOT。
程序主要有两种运行方式:静态编译与动态解释。静态编译的程序在执行前程序会被提前编译为机器码(或中间字节码),通常将这种类型称为AOT (Ahead of time)即 “提前编译”。而解释执行则是在运行时将源码实时翻译为机器码来执行,通常将这种类型称为JIT(Just-in-time)即“即时编译”。
AOT 程序的典型代表是用 C/C++ 开发的应用,它们必须在执行前编译成机器码;而JIT的代表则非常多,如JavaScript、python等,事实上,所有脚本语言都支持 JIT 模式。但需要注意的是 JIT 和 AOT 指的是程序运行方式,和编程语言并非强关联的,有些语言既可以以 JIT 方式运行也可以以 AOT 方式运行,如Python,它可以在第一次执行时编译成中间字节码,然后在之后执行时再将字节码实时转为机器码执行。也许有人会说,中间字节码并非机器码,在程序执行时仍然需要动态将字节码转为机器码,这不应该是 JIT 吗 ? 是这样,但通常我们区分是否为AOT 的标准就是看代码在执行之前是否需要编译,只要需要编译,无论其编译产物是字节码还是机器码,都属于AOT。在此,读者不必纠结于概念,概念就是为了传达精神而发明的,只要读者能够理解其原理即可,得其神忘其形。
现在我们看看 Flutter 为什么选择 Dart 语言?笔者根据官方解释以及自己对 Flutter 的理解总结了以下几条(由于其他跨平台框架都将 JavaScript 作为其开发语言,所以主要将 Dart 和 JavaScript 做一个对比):

  1. 一、开发效率高。
    Dart 运行时和编译器支持 Flutter 的两个关键特性的组合:

    • 基于 JIT 的快速开发周期:Flutter 在开发阶段采用 JIT 模式,这样就避免了每次改动都要进行编译,极大地节省了开发时间;

    • 基于 AOT 的发布包: Flutter 在发布时可以通过 AOT 生成高效的机器码以保证应用性能。而 JavaScript 则不具有这个能力。

  • 二、高性能。
    Flutter 旨在提供流畅、高保真的 UI 体验。为了实现这一点,Flutter 中需要能够在每个动画帧中运行大量的代码。这意味着需要一种既能保证高性能,也不会出现丢帧的编程语言,而 Dart 支持 AOT,在这一点上可以做得比 JavaScript 更好。

  • 三、快速内存分配。
    Flutter 框架使用函数式流,这使得它在很大程度上依赖于底层的内存分配器。因此,拥有一个能够有效地处理琐碎任务的内存分配器将显得十分重要,在缺乏此功能的语言中,Flutter 将无法有效地工作。当然 Chrome V8 的 JavaScript 引擎在内存分配上也已经做的很好,事实上 Dart 开发团队的很多成员都是来自Chrome 团队的,所以在内存分配上 Dart 并不能作为超越 JavaScript 的优势,而对于Flutter来说,它需要这样的特性,而 Dart 也正好满足而已。

  • 四、类型安全和空安全。
    由于 Dart 是类型安全的语言,且 2.12 版本后也支持了空安全特性,所以 Dart 支持静态类型检测,可以在编译前发现一些类型的错误,并排除潜在问题,这一点对于前端开发者来说可能会更具有吸引力。与之不同的,JavaScript 是一个弱类型语言,也因此前端社区出现了很多给 JavaScript 代码添加静态类型检测的扩展语言和工具,如:微软的 TypeScript 以及Facebook 的 Flow。相比之下,Dart 本身就支持静态类型,这是它的一个重要优势。

  • Dart 团队就在你身边。
    看似不起眼,实则举足轻重。由于有 Dart 团队的积极投入,Flutter 团队可以获得更多、更方便的支持,正如Flutter 官网所述“我们正与 Dart 社区进行密切合作,以改进 Dart 在 Flutter 中的使用。例如,当我们最初采用 Dart 时,该语言并没有提供生成原生二进制文件的工具链(这对于实现可预测的高性能具有很大的帮助),但是现在它实现了,因为 Dart 团队专门为 Flutter 构建了它。同样,Dart VM 之前已经针对吞吐量进行了优化,但团队现在正在优化 VM 的延迟时间,这对于 Flutter 的工作负载更为重要。”


  • #4. 小结

    本小节主要介绍了一下 Flutter 的特点,如果你感到有些点还不是很好理解,不用着急,随着日后对 Flutter 细节的了解,再回过头来看,相信你会有更深的体会。
    ready
    论坛版主
    论坛版主
    • UID24
    • 粉丝0
    • 关注0
    • 发帖数433
    • 社区居民
    • 忠实会员
    • 原创写手
    4楼#
    发布于:2024-07-24 15:40


    Dart语言简介


    Dart 在设计时应该是同时借鉴了 Java 和 JavaScript,同时又引入了一些现代编程语言的特性,如空安全,除此之外还有一些独创的语法,比如级联操作符。
    Dart 在静态语法方面和 Java 非常相似,如类型定义、函数声明、泛型等,
    而在动态特性方面又和 JavaScript 很像,如函数式特性、异步支持等。
    除了融合 Java 和 JavaScript 语言之所长之外,Dart 也具有一些其他很有表现力的语法,如可选命名参数、..(级联运算符)和?.(条件成员访问运算符)以及??(判空赋值运算符)。
    其实,对编程语言了解比较多的读者会发现,在 Dart 中其实看到的不仅有 Java 和 JavaScript 的影子,它还具有其他编程语言中的身影,如命名参数在 Objective-C 和 Swift 中早就很普遍,而??操作符在PHP 7.0 语法中就已经存在了,因此我们可以看到 Google 对 Dart 语言给予厚望,是想把 Dart 打造成一门集百家之所长的编程语言。






    1.4.1 变量声明



    #1. var 关键字

    类似于 JavaScript 中的var,它可以接收任何类型的变量,但最大的不同是 Dart 中 var 变量一旦赋值,类型便会确定,则不能再改变其类型,如:

    var t = "hi world"; // 下面代码在dart中会报错,因为变量t的类型已经确定为String, // 类型一旦确定后则不能再更改其类型。 t = 1000;
    1
    2
    3
    4

    上面的代码在 JavaScript 是没有问题的,前端开发者需要注意一下,之所以有此差异是因为 Dart 本身是一个强类型语言,任何变量都是有确定类型的,在 Dart 中,当用var声明一个变量后,Dart 在编译时会根据第一次赋值数据的类型来推断其类型,编译结束后其类型就已经被确定,而 JavaScript 是纯粹的弱类型脚本语言,var 只是变量的声明方式而已。


    #2. dynamic 和 Object

    Object 是 Dart 所有对象的根基类,也就是说在 Dart 中所有类型都是Object的子类(包括Function和Null),所以任何类型的数据都可以赋值给Object声明的对象。 dynamic与Object声明的变量都可以赋值任意对象,且后期可以改变赋值的类型,这和 var 是不同的,如:

    dynamic t; Object x; t = "hi world"; x = 'Hello Object'; //下面代码没有问题 t = 1000; x = 1000;
    1
    2
    3
    4
    5
    6
    7

    dynamic与Object不同的是dynamic声明的对象编译器会提供所有可能的组合,而Object声明的对象只能使用 Object 的属性与方法, 否则编译器会报错,如:

     dynamic a; Object b = ""; main() { a = ""; printLengths(); } printLengths() { // 正常 print(a.length); // 报错 The getter 'length' is not defined for the class 'Object' print(b.length); }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    dynamic 的这个特点使得我们在使用它时需要格外注意,这很容易引入一个运行时错误,比如下面代码在编译时不会报错,而在运行时会报错:

    print(a.xx); // a是字符串,没有"xx"属性,编译时不会报错,运行时会报错
    1



    #3. final和const

    如果您从未打算更改一个变量,那么使用 final 或 const,不是var,也不是一个类型。 一个 final 变量只能被设置一次,两者区别在于:const 变量是一个编译时常量(编译时直接替换为常量值),final变量在第一次使用时被初始化。被final或者const修饰的变量,变量类型可以省略,如:

    //可以省略String这个类型声明 final str = "hi world"; //final String str = "hi world"; const str1 = "hi world"; //const String str1 = "hi world";
    1
    2
    3
    4
    5



    #4. 空安全(null-safety)

    Dart 中一切都是对象,这意味着如果我们定义一个数字,在初始化它之前如果我们使用了它,假如没有某种检查机制,则不会报错,比如:

    test() { int i; print(i*8); }
    1
    2
    3
    4

    在 Dart 引入空安全之前,上面代码在执行前不会报错,但会触发一个运行时错误,原因是 i 的值为 null 。但现在有了空安全,则定义变量时我们可以指定变量是可空还是不可空。

    int i = 8; //默认为不可空,必须在定义时初始化。 int? j; // 定义为可空类型,对于可空变量,我们在使用前必须判空。 // 如果我们预期变量不能为空,但在定义时不能确定其初始值,则可以加上late关键字, // 表示会稍后初始化,但是在正式使用它之前必须得保证初始化过了,否则会报错 late int k; k=9;
    1
    2
    3
    4
    5
    6
    7

    如果一个变量我们定义为可空类型,在某些情况下即使我们给它赋值过了,但是预处理器仍然有可能识别不出,这时我们就要显式(通过在变量后面加一个”!“符号)告诉预处理器它已经不是null了,比如:

    class Test{ int? i; Function? fun; say(){ if(i!=null) { print(i! * 8); //因为已经判过空,所以能走到这 i 必不为null,如果没有显式申明,则 IDE 会报错 } if(fun!=null){ fun!(); // 同上 } } }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    上面中如果函数变量可空时,调用的时候可以用语法糖:

    fun?.call() // fun 不为空时则会被调用
    ready
    论坛版主
    论坛版主
    • UID24
    • 粉丝0
    • 关注0
    • 发帖数433
    • 社区居民
    • 忠实会员
    • 原创写手
    5楼#
    发布于:2024-07-24 15:45
    Dart和Java及JavaScript对比

    1. Dart vs Java
    Dart 在 Flutter 中已经可以将 GC(内存垃圾回收)做到 10ms 以内,所以 Dart 和 Java 相比,决胜因素并不会是在性能方面。
    而在语法层面,Dart 要比 Java 更有表现力,最重要的是 Dart 对函数式编程支持要远强于 Java(目前只停留在 Lambda 表达式),而 Dart 目前真正的不足是生态,但笔者相信,随着 Flutter 的逐渐火热,会回过头来反推 Dart 生态加速发展,对于 Dart 来说,现在需要的是时间。

    2. Dart vs JavaScript

    JavaScript 的“弱类型”一直被诟病,所以 TypeScript (JavaScript语言的超集,语法兼容JavaScript,但添加了“类型”)才有市场。
    就笔者使用过的脚本语言中(笔者曾使用过 Python、PHP),JavaScript 无疑是动态化支持最好的脚本语言,比如在 JavaScript 中,可以给任何对象在任何时候动态扩展属性,对于精通 JavaScript 的高手来说,这无疑是一把利剑。但是,任何事物都有两面性,JavaScript 强大的动态化特性也是把双刃剑,你可经常听到另一个声音,认为 JavaScript 的这种动态性糟糕透了,太过灵活反而导致代码很难预期,无法限制不被期望的修改。毕竟有些人总是对自己或别人写的代码不放心,他们希望能够让代码变得可控,并期望有一套静态类型检查系统来帮助自己减少错误。

    在 Flutter中,Dart 几乎放弃了脚本语言动态化的特性,如不支持反射、也不支持动态创建函数等。
    并且 Dart 从 2.0 开始强制开启了类型检查(Strong Mode),原先的检查模式(checked mode)和可选类型(optional type)将淡出,所以:在类型安全这个层面来说,Dart 和 TypeScript、CoffeeScript 是差不多的,所以单从动态性来看,Dart 并不具备什么明显优势,但综合起来看,Dart 既能进行服务端脚本、App 开发、Web 开发,这就有优势了!
    游客
    

    返回顶部