Flutter:谷歌对跨平台的尝试

Avatar of Eric Windmill
Eric Windmill

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

Flutter 是一款移动 SDK,其核心在于赋能每个人构建美丽的移动应用。 无论您来自 Web 开发领域还是原生移动开发领域,Flutter 都能让您以一种熟悉、简化的方式更轻松地创建移动应用,而无需将控制权交给框架。

截至本文撰写之时,Google AdWords 和阿里巴巴都在生产环境中使用 Flutter。 您可以在 Flutter 网站的展示页面上查看更多使用 Flutter 的示例(包括我参与开发的应用)。

目前,Flutter 非常火热。 我最常看到的疑问是:“Flutter 还是 React Native……我应该用哪个?” 就像编程中的所有事情一样,这都取决于您愿意做出的权衡。

我将尝试说服您,Flutter 是移动应用开发的最佳选择。 我相信它优于任何其他跨平台框架,并且可能优于原生开发——但稍后会详细介绍。

但在此之前,让我快速浏览一下 Flutter 是什么以及它不是什么,从 Dart 编程语言开始。

什么是 Dart?

Dart 是一种由 Google 创建的编程语言,用于编写 Flutter。 Dart 的诞生,或多或少是因为 Google 想要一种比 JavaScript “更好”的语言来编写服务器端和前端代码。 就我了解,他们对 JavaScript 的主要问题在于,由于它依赖于庞大的委员会审批和多个浏览器厂商的实施,因此新功能的更新速度很慢。

经过一系列关于是否直接采用 JavaScript 的决策后,Google 决定开发一种在语义上与 JavaScript 兼容的语言。 换句话说,您在 Dart 中编写的每一件事都可以编译成 JavaScript。 这就是他们没有直接使用 Java 的原因——在语义上,Java 的规模太大了。

这是 2010 年 Google 的一封泄露的邮件链。 这是他们意识到需要对 JavaScript 做些什么的“顿悟”时刻。

Dart 的基本原理与所有高级语言类似。 也就是说,编程语言实际上很难学习。

不过,好消息是,Dart 非常适合作为一种“安全”的学习语言。 Google 并没有着手用 Dart 创建任何创新的东西。 他们希望开发一种简单、高效的语言,并且可以编译成 JavaScript。

它的语法没有什么特别令人兴奋的地方,也没有会让您困惑的特殊运算符。 在 Dart 中(与 JavaScript 不同),只有一种方法表示真:True。 只有一种方法表示假:False。

在 JavaScript 中,这会强制转换为 True

if (3) { ... }

在 Dart 中,这会使您的程序崩溃。 Dart 本质上是一种高效、可预测且简单的语言。

这一点很重要,因为用 Flutter 编写应用只是在编写 Dart 代码。 Flutter 的底层实际上是一个 Dart 类库。 不涉及任何标记语言或 JSX 风格的混合语言。 每个前端代码片段都用 Dart 编写。 没有 HTML。 没有 CSS。

为什么 Flutter 使用 Dart?

如果您来自其他任何背景(并且像我一样),您可能抱怨过 Flutter 使用 Dart 而不是 JavaScript。(开发人员,信不信由你,都是有主见的。)

并且有理由对这种选择表示怀疑。 它不是当今的热门语言之一。 它甚至不是 25 种最常用语言之一。 为什么?Google 是否仅仅因为它自己的语言而使用它? 我想这可能起到了作用,但也有实际的原因。

  • Dart 支持即时 (JIT) 编译和提前 (AOT) 编译。
    • AOT 编译器将 Dart 转换为高效的原生代码。 这使得 Flutter 速度很快(对用户和开发人员来说都是胜利),但也意味着几乎所有框架都是用 Dart 编写的。 对于您,开发人员来说,这意味着您可以自定义所有内容。
    • Dart 的可选 JIT 编译允许热重载存在。 快速开发和迭代是使用 Flutter 的乐趣的关键。 当您在文本编辑器中保存代码时,您的应用将在不到一秒的时间内更新到您的模拟器中。
  • Dart 是面向对象的。 这使得仅使用 Dart 轻松编写视觉用户体验成为可能,而无需标记语言。

  • Dart 是一种高效且可预测的语言。 它易于学习,并且感觉很熟悉。 无论您来自动态语言还是静态语言,您都可以轻松上手。
  • 是的,我猜想使用同一公司开发的语言极具吸引力,因为 Flutter 团队可以与 Dart 团队紧密合作以实现所需的新功能。

Flutter 与 React Native(以及其他选项)的比较

在提供我对其他选项的未经请求的意见之前,我想明确一点:Flutter 并非在所有情况下都是答案。 它是一种工具,我们应该为手头的任务选择合适的工具。 也就是说,我只是认为您应该在未来认真考虑它。

原生开发(iOS 和 Android)

您的首选是为 iOS 和 Android 编写原生应用。 这让您可以最大程度地控制、调试工具,以及(可能)获得非常高性能的应用。 在公司中,这可能意味着您必须编写两遍代码;每个平台编写一次。 您可能需要不同团队的不同开发者,他们拥有不同的技能,并且无法轻松地互相帮助。

React Native、WebView 和其他跨平台 JavaScript 选项

您的第二个选择:跨平台、基于 JavaScript 的工具,例如 WebView 和 React Native。 这些并不是糟糕的选择。 您在原生开发中遇到的问题消失了。 团队中的每个前端 Web 开发人员都可以参与进来并提供帮助——他们只需要一些现代 JavaScript 技能。 这正是 Airbnb、Facebook 和 Twitter 等大型公司在其核心产品中使用 React Native 的原因。(Airbnb 最近 宣布 将停止使用 React Native,原因是我将在下面描述的一些问题。)

最初构建的跨平台“移动应用”只是在 WebKit(一种浏览器渲染引擎)上运行的 WebView。 这些实际上只是嵌入的网页。 问题在于,操作 DOM 非常昂贵,并且性能不足以提供出色的移动体验。

一些平台通过构建“JavaScript 桥”解决了这个问题。 此桥梁允许 JavaScript 直接与原生部件进行通信。

这比 WebView 性能更高,因为您消除了 DOM,但它仍然不是理想的。 每次您的应用需要直接与渲染引擎通信时,它都必须编译成原生代码才能“跨越桥梁”。 在单个交互中,桥梁必须被跨越 *两次*:一次从平台到应用,然后从应用返回到平台。

Flutter 不同,因为它使用自己的渲染引擎 Skia,这是 Chrome 中使用的相同渲染引擎。 Skia 可以与 Flutter 应用通信。 因此,Flutter *直接* 接收本地事件,而不是必须先将它们编译成 JavaScript。 这之所以成为可能,是因为 *Flutter 编译成原生 ARM 代码*。 这是它成功的秘诀。 当您的应用在用户的设备上启动时,它完全在设备操作系统期望的语言中运行。

JavaScript 桥梁无疑是现代编程的奇迹,但它也存在三个主要问题。

第一个问题是调试很困难。 当运行时编译器中出现错误时,必须通过 JavaScript 桥梁回溯错误,并在 JavaScript 代码中找到它。 它也可能在标记或类似 CSS 的语法中。 调试器本身可能无法像我们希望的那样工作。

然而,第二个更大的问题是性能。 JavaScript 桥梁非常昂贵。 每次应用中的某个内容被点击时,该事件都必须通过桥梁发送到您的 JavaScript 应用。 结果,通俗点说就是 *卡顿*。

第三个大问题,根据Airbnb的说法,是他们发现自己不得不比他们想要的更频繁地深入到原生代码中,这对他们主要由JavaScript开发人员组成的团队来说是一个问题。(Flutter在这个问题上还没有定论,但我可以肯定的是,在我的工作中从未需要尝试编写原生代码。我的团队中的一些成员创建了Objective-C和Java的插件。)

Flutter的直接优势

你很可能因为正在阅读这篇文章而对Flutter感兴趣……但你可能也持怀疑态度。我钦佩你对技术进行审查的细致程度。

你持怀疑态度的理由是合理的。这是一项新技术。这意味着API可能会发生重大变更。这意味着缺少对重要功能的支持(例如谷歌地图)。谷歌有一天可能会完全放弃它。

而且,尽管你认为Dart是一门很棒的语言,但这并不能改变Dart没有被广泛使用的事实,并且你可能需要的许多第三方库可能不存在。

不过,我将反驳所有这些观点。API不太可能发生变化,因为谷歌在内部使用Flutter开发主要的创收应用,包括Google AdWords。Dart最近更新到了版本2,这意味着它可能需要一段时间才会发生重大变化。可能需要几年时间才会引入重大变更,在计算机世界里,这几乎是永远。

是的,确实缺少一些功能,但是Flutter让你可以完全控制添加自己的原生插件。事实上,许多最重要的操作系统插件已经存在,例如地图插件、相机、定位服务和设备存储。Dart和Flutter的生态系统和社区已经存在。当然,它比JavaScript社区要小得多,但我认为它很简洁。我每天都看到人们在为现有的软件包做出贡献,而不是创建新的软件包。

现在,让我们谈谈Flutter的具体优势。

没有JavaScript桥

这是开发和应用程序性能的主要瓶颈。同样,它会导致卡顿。滚动不流畅,并不总是高效的,并且难以调试。

Flutter编译成真正的原生代码,并使用Skia进行渲染。应用程序本身在原生环境中运行,因此无需将Dart转换为原生代码。这意味着它在用户设备上运行时不会损失任何性能或生产力。

编译时间

如果你来自原生开发,那么你主要的痛点之一就是开发周期。iOS以其疯狂的编译时间而闻名。在Flutter中,完整的编译通常需要不到30秒,增量编译则只需几秒钟,这要归功于热重载。在我的日常工作中,我们首先为我们的移动客户端开发功能,因为Flutter的开发周期让我们能够快速移动。只有在我们确定了实现方案后,我们才会去编写Web客户端的功能。

一次编写,一次测试,随处部署

你不仅可以编写一次应用程序并部署到iOS和Android,而且只需要编写一次测试。Dart单元测试非常简单,Flutter包含一个用于测试Widget的库。

代码共享

在这里我将公平地说:我想这在技术上在JavaScript中也是可能的。但是,在原生开发中肯定是不可能的。使用Flutter和Dart,你的Web和移动应用程序可以共享所有代码,除了每个客户端的视图。(当然,只有在你使用Dart开发Web应用程序时才有可能。)你可以很容易地使用依赖注入来运行AngularDart应用程序和Flutter应用程序,使用相同的模型和控制器。

当然,即使你不想在Web应用程序和移动应用程序之间共享代码,你也可以在iOS和Android应用程序之间共享所有代码。

从实际角度来看,这意味着你的生产力非常高。我提到过,在我们的日常工作中,我们首先开发移动功能。因为我们在Web和移动之间共享业务逻辑,所以一旦移动功能实现,我们只需要编写期望相同控制器数据的视图。

生产力和协作

iOS和Android的独立团队的日子一去不复返了。事实上,无论你在Web应用程序中使用Dart还是JavaScript,Flutter开发都足够熟悉,以至于你的所有团队都将统一起来。期望一个JavaScript Web开发人员也能有效地使用Flutter和Dart进行开发,这绝非夸大其词。如果你相信我所说的话,那么由此可见,你的新统一团队的生产力将提高三倍。

代码维护

没有什么比修复一个bug并将其在所有客户端上都修复更令人满意的了。只有在非常特殊的情况下,使用Flutter生成的iOS应用程序中才会出现Android版本中不存在的bug(反之亦然)。在100%的这些情况下,这些都不是bug,而是外观问题,因为Flutter在其内置的Widget中遵循设备操作系统的设计系统。因为这些问题例如文本大小或对齐方式,所以在使用工程时间修复的背景下,它们是微不足道的。

面向JavaScript开发人员的Flutter

既然你在阅读CSS-Tricks,我敢打赌你是一位Web开发人员。如果你使用过任何当今最流行的框架(例如React、Angular、Vue等),那么你会很高兴知道学习Flutter是很容易的

Flutter完全是反应式的,所以你习惯于在React中使用的思维方式和范式可以延续到Flutter中。你基本上是在构建大量的小型、可重用的组件(在Flutter中称为Widget),就像React一样。这些Widget包含完整的生命周期方法,并且是用类编写的。如果你在React中使用过这种语法

const MyComponent extends React.Component {
  //...
  render(){}
}

…那么你就能毫不费力地掌握Flutter。以下是在Flutter中执行相同操作的方法

class MyWidget extends StatelessWidget {
  //...
  build(){}
}

而且,就像React一样,Flutter更偏向于组合而不是继承。例如,如果你想在React中创建一个特殊的AddToCartButton,你将在JSX中构建一个带有特殊功能和样式的按钮。这正是你在Flutter中执行的方式(除了JSX)。

最后,Flutter中的布局系统类似于我们熟悉的CSS规则,如flexbox和绝对定位。

不过,这也是Flutter创建视图的一个很大的区别。在Flutter中,从字面上看,一切都是Widget。有一些明显的、具体的Widget,比如TextButtonAppBar。但动画和布局声明也是Widget。要居中显示文本,你需要将Text Widget包装在一个Center Widget中。要添加填充,可以使用Padding Widget。

想象一下,将一个React应用程序分解成你能创建的尽可能小的可重用组件。例如,如果你创建了一个高阶React组件,它只接受一个“padding”属性,并且它所做的只是将该填充量添加到嵌套在其内部的任何内容中。Flutter就是这样工作的,因为它没有CSS或标记。

在这张示例图片中,这里列出了一些你可能会用到的布局Widget,但用户无法“看到”它们

这看起来可能像是大量单调的工作,但Flutter内置了许多Widget(例如PaddingCenter),因此你无需浪费时间自己去创建它们。

以下是一些最常用的Widget

  • 布局RowColumnScaffoldStack
  • 结构ButtonToastMenuDrawer
  • 文本TextStyleColor
  • 动画FadeInPhoto、转换
  • 样式CenterPadding

总结

TLDR:你应该尝试 Flutter 吗?

如果你想用熟悉的方式创建流畅的移动应用,那么答案是肯定的!Flutter 完整地保留了性能和开发体验。它的动画以 60fps 的速度运行,并且内置了一系列 Cupertino 风格和 Material Design 风格的 Widget。或者,简单来说:Flutter 的开发效率惊人,并且没有牺牲原生性能。

如果你想今天就开始尝试 Flutter,这里有一些很棒的起点

Flutter 的文档是我见过的最好的文档之一,它们会教你所有你需要知道的知识。