Flutter 的革命之处

6,323 阅读16分钟

翻译原文链接

什么是Flutter?

Flutter移动应用程序SDK是一种构建快速,美观的移动应用程序的新方式,可帮助开发人员摆脱过去常见的“cookie切割”应用程序。 试过Flutter的人真的喜欢它; 例如,看这个这个,或者这个。 或者,这是第三方编辑的文章和视频列表

就像任何新的系统一样,人们想知道Flutter的不同,或者换句话说,“Flutter有什么新鲜和令人兴奋的东西?”这是一个公平的问题,本文将从技术角度来回答它 - 而不是 只是什么是令人兴奋的,但为什么。 但是,首先,有一点历史。

移动应用程序开发的简要历史

移动应用程序开发是一个相对较新的领域。 第三方开发人员已经能够在不到十年的时间内开发移动应用程序,所以工具仍在不断发展并不奇怪。

OEM SDKs

Apple iOS SDK于2008年发布,2009年发布Google Android SDK。这两个SDK分别基于不同的语言:Objective-C和Java。

您的应用程序会与平台进行交谈,以创建widgets或访问相机等服务。 widgets呈现给屏幕画布,并且事件被传回给widgets。 这是一个简单的架构,但是您几乎必须为每个平台创建单独的应用程序,因为这些widgets是不同的,更不用说语言。

WebView

第一个跨平台框架基于JavaScript和WebViews。 例如Titanium和一系列相关的框架:PhoneGap,Apache Cordova,Ionic等等。 在苹果发布iOS SDK之前,他们鼓励第三方开发者为iPhone构建webapps,所以用web技术构建跨平台的应用程序是一个显而易见的步骤。

您的应用程序创建HTML并将其显示在平台上的WebView中。 请注意,像JavaScript这样的语言很难直接与本地代码(like thee services)交谈,所以它们会经历一个在JavaScrip领域和Native 领域之间进行上下文切换的“Bridge”。 因为平台服务通常不是经常被调用的,所以这不会导致太多的性能问题。

Reactive Views

ReactJS(和其他)这样的响应式Web框架已经变得流行,主要是因为它们通过使用从响应式编程中借用的编程模式来简化Web视图的创建。 2015年,创建了React Native将响应式视图的诸多好处带给移动应用程序。

React Native非常受欢迎(并且是值得的),但是由于JavaScript领域访问Native领域的OEM widgets,因此它也必须通过这个桥梁。 通常访问widgets的频率非常高(在动画,转换过程中,或者用户用手指在屏幕上滑动某些东西时,每秒可达60次),因此可能会导致性能问题。 正如一篇关于React Native的文章所说:

这里是理解React Native性能的主要关键之一。 每个领域本身都非常快。 当我们从一个领域转移到另一个时,性能瓶颈往往会发生。 为了构建高性能的React Native应用程序,我们必须保持桥梁通过最低限度。

Flutter

像React Native一样,Flutter也提供响应式风格的视图。 Flutter使用编译的编程语言即Dart的方法来避免JavaScript桥引起的性能问题,。 Dart被“提前编译”(AOT)编译成多个平台的本地代码。 这使得Flutter可以与平台进行通信,而无需通过执行了上下文切换的JavaScript Bridge。 编译为本机代码也可以提高应用程序的启动时间。 Flutter是唯一提供响应式视图而不需要JavaScript Bridge的移动SDK的事实应该足以让Flutter变得有趣并且值得尝试,但是Flutter还有一些更具革命性的地方,那就是它如何实现widgets。

Widgets

Widgets是影响和控制应用程序的视图和界面的元素。 说Widgets是移动应用程序最重要的部分之一,这并不是夸大其词。 事实上,Widgets可以成就或挫败一个应用程序。

  • Widgets的外观和感觉是最重要的。 Widgets需要看起来不错,包括各种屏幕尺寸。 他们也需要感觉自然。
  • Widgets必须快速执行:创建Widgets树,inflate the Widgets(实例化他们的子项),将它们放在屏幕上,渲染它们,或者(特别是)使它们动画化。
  • 对于现代应用程序,Widgets应该是可扩展的和可定制的。 开发人员希望能够添加令人愉快的新Widgets,并自定义所有Widgets以匹配应用程序的品牌。

Flutter有一个新的架构,包括外观和感觉不错,快速,可定制和可扩展的Widgets。 没错,Flutter不使用OEM Widgets(或DOM WebViews),它提供了自己的Widgets。

Flutter将Widgets和渲染器从平台移动到应用程序中,从而使其可以自定义和扩展。 Flutter对平台的需求平台是一个画布,在这个画布中,Widgets可以呈现在设备屏幕上,并可以访问事件(触摸,定时器等)和服务(位置,摄像机等)。 Dart程序(绿色)和本地平台代码(iOS或Android蓝色)之间仍然存在一个接口,可以进行数据编码和解码,但这可能比JavaScript Bridge 快几个数量级。

将Widgets和渲染器移动到应用程序中会影响应用程序的大小。 Android上Flutter应用程序的最小大小约为6.7MB,与使用类似工具构建的最小应用程序类似。 由您决定是否Flutter的好处是值得的权衡,所以本文的其余部分讨论这些好处。

布局

Flutter最大的改进之一就是它的布局。布局根据一组规则(也称为约束)来确定widgets的大小和位置。 传统上,布局使用一堆可应用于(虚拟)任何widgets的规则。规则实现了多种布局方法。让我们以CSS布局为例,因为它是众所周知的(尽管和Android和iOS的布局基本相似)。 CSS具有属性(规则),这些属性应用于HTML元素(widgets)。 CSS3定义了375个属性。 CSS包含许多布局模型,包括(多个)框模型,浮动元素,表格,多列文本,分页媒体等等。其他的布局模型,如flexbox和grid,后来被添加,因为开发人员和设计师需要更多的控制布局,并使用表和透明图像来获得他们想要的。在传统布局中,开发人员无法添加新的布局模型,因此必须将flexbox和网格添加到CSS并在所有浏览器上实施。 传统布局的另一个问题是规则可以相互影响(甚至相互冲突),并且元素通常应用了许多规则。这使布局变慢。更糟的是,布局表现通常是N阶有序的,所以随着元素数量的增加,布局变得更加缓慢。 Flutter是由Google的Chrome浏览器团队成员进行的一项实验开始的。如果我们忽略了传统的布局模型,我们想看看是否可以建立更快的渲染器。几周后,我们取得了显着的业绩增长。我们发现:

  • 大多数布局相对简单,比如:滚动页面上的文本,大小和位置仅取决于显示大小的固定矩形,以及一些表格,浮动元素等等。
  • 大多数布局对于widgets子树是局部的,并且该子树通常使用一个布局模型,因此这些widgets只需要少量的规则支持。 我们意识到,如果我们使用如下的要点,布局可以被大大简化:
  • 每个widget都可以指定自己的简单布局模型,而不必拥有大量可应用于任何widget的布局规则。
  • 因为每个widget都有一个小得多的布局规则,布局可以大大优化。
  • 为了进一步简化布局,我们把几乎所有东西都变成了一个widget

这里是Flutter代码来创建一个布局简单的widget树:

new Center(
  child: new Column(
    children: [
      new Text('Hello, World!')),
      new Icon(Icons.star, color: Colors.green)
    ]
  )

这个代码的语义是足够的,你可以很容易地想象它会产生什么,但是这里得到的结果是:

在这个代码中,一切都是widget,包括布局。Center widget将其中心放置在其父级(例如屏幕)中。Column布局widget垂直排列其子元素(widget列表)。该列包含一个文本widget和一个图标widget(它有一个属性,它的颜色)。 在Flutter中,居中和填充是widget。主题是widget,适用于他们的孩子。甚至应用程序和导航都是widget。 Flutter包含了很多用于布局的widget,不仅包括列,还包括行,网格,列表等。此外,Flutter还有一个独特的布局模型,我们称之为“sliver layout model”,用于滚动。 Flutter中的布局非常快,可以用于滚动。想一想。滚动必须是瞬间发生的和平滑的,以至于用户感觉像屏幕图像在他们拖过物理屏幕时被附着到他们的手指。 通过使用滚动布局,Flutter可以实现高级类型的滚动,如下所示。请注意,这些动画GIF图像,Flutter更平滑。你可以(也应该)自己运行这些应用程序;请参阅本文末尾的参考资料部分。

大多数时候,Flutter可以一次完成布局,这意味着线性时间,所以它可以处理大量的widgets。 Flutter也做缓存和其他事情,所以可以避免布局。

定制设计

因为widgets现在是应用程序的一部分,所以可以添加新的widgets,并且可以定制现有的widgets以使其具有不同的外观或感觉,或匹配公司的品牌。 移动设计的趋势远离几年前常见的cookie应用程序,并且朝向取悦用户并赢得奖项的定制设计。 Flutter为Android,iOSMaterial Design提供了丰富的,可自定义的widgets集(事实上,我们已经知道,Flutter是Material Design中最高保真实现之一)。 我们使用Flutter的可定制性来构建这些widgets集,以匹配多个平台上的本机widgets的外观和风格。 应用程序开发人员可以使用相同的可定制性来进一步调整窗口widgets,以满足他们的需求。

更多关于 Reactive Views

用于reactive web views的库引入了virtual DOM。 DOM是HTML文档对象模型(HTML Document Object Model),一个使用JavaScript用来处理HTML文档的API,用一个元素树来表示。 虚拟DOM是使用编程语言中的对象创建的DOM的抽象版本,在这种情况下是JavaScript。

在 reactive web views(由ReactJS等系统实现)中,虚拟DOM是不可变的,每当有任何变化时,都会从头开始重建。 将虚拟DOM与真实的DOM进行比较,生成一组最小的更改,然后执行这些更改以更新真实的DOM。 最后,平台重新渲染真实的DOM并将其绘制到画布中。

这可能听起来是很多额外的工作,但它是非常值得的,因为操纵HTML DOM是非常昂贵的。 React Native做类似的事情,但对于移动应用程序。 它不是DOM,而是操纵移动平台上的原生widgets。 它不是虚拟DOM,而是构建一个widgets的虚拟树,并将其与本机widgets进行比较,只更新那些已更改的widgets。

请记住,React Native必须通过Bridge与Native widgets进行通信,因此widgets的虚拟树有助于将Bridge过程消耗保持在最低限度,同时仍允许使用本机窗口widgets。 最后,一旦Native widgets被更新,平台将把它们呈现在画布上。 React Native是移动开发的一大胜利,是Flutter的灵感来源,但Flutter更进一步。

回想一下,在Flutter中,widgets和渲染器已经从平台上升到用户的应用程序中。没有原生的OEM widget tree可以操作,那么virtual widget tree现在是widget tree。 Flutter渲染widget tree并将其绘制到平台画布上。这是很好,简单(和快速)。另外,动画发生在用户空间中,所以应用程序(以及开发者)对其有更多的控制。

Flutter渲染器本身很有趣:它使用几个内部树结构来渲染那些需要在屏幕上更新的widgets。例如,渲染器使用“structural repainting using compositing”("structural"意味着是通过widget,比通过屏幕上的矩形区域更有效)。不变的widgets,甚至是那些已经移动的widgets,都是从高速缓存中“bit blited”的。这是Flutter中即使在高级滚动(在上面讨论和示出)中滚动如此执行的事情之一。 为了仔细了解Flutter渲染器,我推荐这个视频。你也可以看看代码,因为Flutter是开源的。当然,您可以自定义甚至替换整个堆栈,包括渲染器,合成器,动画,手势识别器和(当然)widgets。

Dart编程语言

因为Flutter就像使用反应视图的其他系统一样,为每个新帧刷新视图树,所以它创建了许多只能存活一帧(六十分之一秒)的对象。 幸运的是,Dart使用对这类系统非常有效的“分代垃圾收集”,因为对象(尤其是短命的)相对cheap。 另外,对象的分配可以用single pointer bump来完成,这是快速的并且不需要lock。 这有助于避免UI jank和stutter。 Dart也有一个“tree shaking”的编译器,其中只包括您的应用程序需要的代码。 即使只需要其中的一个或两个,也可以使用大型widgets库。

Hot reload

Flutter最受欢迎的功能之一是其快速,有状态的Hot reload。 您可以在Flutter应用程序运行时对其进行更改,并重新加载已更改的应用程序的代码,并让代码从停止的位置继续,通常不到一秒钟。 如果您的应用程序遇到错误,您通常可以修复错误,然后继续,如同错误从未发生过。 即使你必须做一个完整的重载,速度也很快。

开发人员告诉我们,这可以让他们“绘制”他们的应用程序,一次做出一个更改,然后几乎立即看到结果,而不必重新启动应用程序。

兼容性

由于widgets(以及这些widgets的渲染器)是您应用程序的一部分,而不是平台的一部分,因此不需要“兼容库”。您的应用程序不仅可以正常工作,而且在最近的操作系统版本(Android Jelly Bean和更新的版本以及iOS 8.0和以上的版本)上也可以发挥同样的作用。这大大减少了在较旧的OS版本上测试应用程序的需要。另外,您的应用很可能会在未来的操作系统版本上运行。 我们被问到一个潜在的问题。由于Flutter不使用OEM native widgets,当新版本的iOS或Android支持新类型的OEM native widgets或更改现有OEM native widgets的外观或行为时,Flutter widgets 是否需要更新才会更新?

  • 首先,Google是Flutter的内部用户,所以我们有强烈的动机去更新这些Widget集合,以保持它们的当前状态,并尽可能接近当前的OEMwidgets。
  • 如果有一段时间我们在更新widget时速度太慢,那么Google并不是Flutter的唯一有激励让widget保持最新状态的用户。 Flutter的widgets是可扩展和可定制的,任何人都可以更新它们,甚至是你。甚至不必提交一个拉请求。你永远不用等待Flutter本身的更新。
  • 以上几点仅适用于您希望在应用程序中反映新的变化。如果您不希望更改影响您的应用的外观或行为方式,那就太好了。widget是你的应用程序的一部分,所以一个widget将永远不会在你的操作外发生改变,使你的应用程序看起来不好(或更糟的是,打破你的应用程序)。
  • 作为一个额外的好处,你可以编写你的应用程序,使它使用新的widget甚至在旧的操作系统版本中。

其他Benifits

Flutter的简单性使其变得更加快速,但是普遍的可定制性和可扩展性使其变得强大。 Dart有一个软件包的存储库,所以你可以扩展你的应用程序的功能。 例如,有许多软件包可以轻松访问Firebase,因此您可以构建“serverless”应用程序。 外部贡献者已经创建了一个包,允许您访问Redux数据存储。 还有一些名为“plugins”的软件包,可以以独立于操作系统的方式轻松访问平台服务和硬件,如加速度计或摄像头。

当然,Flutter也是开源的,加上Flutter渲染栈是你的应用程序的一部分,意味着你可以自定义几乎任何你想要的一个单独的应用程序。 这个图中的所有绿色都可以定制:

所以,“Flutter有什么新的和令人兴奋的?”

如果有人问你有关Flutter,现在你知道如何回答他们:

  • 反应式视图的优点,没有JavaScript Bridge
  • 快速,流畅,可预测; 代码将AOT编译为本机(ARM)代码
  • 开发人员可以完全控制widgets和布局
  • 带有漂亮,可定制的widgets
  • 极好的的开发人员工具,惊人的hot reload
  • 性能更高,兼容性更多,更有趣

你有没有注意到我从这个清单中删除了什么? 当人们谈论Flutter时,这通常是人们首先提到的,但对于我来说这是Flutter最不感兴趣的事情之一。 事实上,Flutter可以从一个代码库为多个平台构建美丽而快速的应用程序。 当然,这应该是一个给定的! 这是可定制和可扩展性,可以轻松地将Flutter目标锁定到多个平台,而不会放弃性能或功耗。

翻译原文链接