September 30, 2023
React Native 使用报告
最近邀请我参加一个七牛云的校园编程竞赛,我觉得自己能学到些什么,所以就半同意下来了。
昨天晚上三个人简要谈了谈之后,说是要决定一下选什么框架好,目前就两个方向 —— React Native 和 Flutter,前者是我只听说过但没实际了解的东西,后者则是自去年 11 月份就在学的东西。
于是昨天配置好 React Native 的基础环境后,今天就看教程去了,但是嘛,越看就越觉得 React Native 不好用,我来具体分析一下。
语言不行#
首先,React Native 使用 JavaScript 或 TypeScript 语言来编写程序,而前者 JavaScript 是我认为最令人难受的语言 —— PHP 好像也挺垃圾的 —— 它的变量类型可以说是一塌糊涂,动态类型 Python 也有,但 Python 我就用来写点小东西,要我用它开发程序 …… 我也不是没写过,WordCloud 就是用 Python 写的,体验也是非常糟糕。除此之外我还非常讨厌它有 ==
和 ===
的区别,隐式转换是坏文明。
后者 TypeScript 我就直白的说,我看不懂!
Python 里加入类型检查的方法是在变量后面冒号然后一个类型,而且重要的是,即便没给全部的变量都加上类型,程序仍然能跑起来,相当于只是给个 lint 提示一下,但 TypeScript 要写类型就得全部写完,类型还不直观,之前学 React 的时候想转 TypeScript 都转不了,后面对 React 就没多大兴趣了。
再来聊聊 Flutter 使用的语言 Dart,这是我用的最喜欢的一门语言。
首先,他非常像 Java,而我曾经写过一段时间的 Java 程序,所以很快就上手了。而且相比 Java,它有空安全检查,也就是一个变量只有在其类型后面接上个问号时才能赋值为 null
。
相比之下,Java 就没有这样的要求,就容易抛出空指针错误。它的语法比 Java 简约,new
关键字不再是必要的。它还有动态类型,所以动态类型的好处 Dart 也有。
其次,我非常喜欢它的 extension
方法,可以在已有的一个类,如 DateTime
里,“注入”自己需要的一些便捷方法,这是非常好的,pub.dev 上还有一个包专门收录了许多的 extension
,极大方便了开发。
框架不行#
包管理#
React 和 React Native 都会搞出一个极大的 node_modules
文件夹,这是非常屎的一个设计,我就不多吐槽了,yarn、pnpm 都是为了解决这个烂玩意的,见 此博客。
而 React Native 一个令我难受的点是,其 core
核心组件太少了,像是 Navigation 导航都要引入一个包,听说之前它是在 core
里的,之后被分离出去了 …… 嗯 ……
再来聊聊 Flutter 的包管理,它没有什么外置的包管理器,要么一句 flutter pub add <包名>
解决,要么自己在 pubspec.yaml
文件里添加一行 <包名>: <版本号>
就行,甚至版本号都不填,留个冒号在那里,Flutter 自己会获取最新且兼容的包,一手包办的感觉太爽了。
那 Flutter 的包是放在哪的呢?如果使用 Windows,则在 %LOCALAPPDATA%\Pub\Cache\hosted
下,它按源的不同,分多个文件夹,一般来说是 pub.dev
文件夹和 pub.flutter-io.cn
文件夹,进 pub.dev
文件夹能看到很多包的不同版本的文件夹,就统计下来连 2 GB 都没有。
PS > "{0} MB" -f [math]::round((Get-ChildItem -Path "$env:LOCALAPPDATA\Pub\" -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB, 2)
1301.09 MB
高下立判。
组件设计#
React 和 React Native 的类组件和 Flutter 的组件非常相似,但没有明显区分自己管理状态和无状态的组件,换言之,都是继承自 Component
,都有一个 state
,只是看你用不用就是了。
相似的点在于,前者的 constructor
相当于后者的 initState
,前者的 render
相当于后者的 build
,生命周期非常相似。
Flutter 是分了 StatelessWidget
和 StatefulWidget
的,前者只会要重写一个 build
方法,后者则是可重写整个生命周期。
组件的修改#
这是我很不喜欢的一点,所有的,对于一个组件的修改,都要在一个远离组件的一个键值对里配置,这非常割裂。
其实我在学 HTML 和 CSS 的时候就有这样的感觉,样式为什么要放到另一个文件里?紧靠着不是更方便吗?没办法,网页这样搞是历史原因,React 也是搞网页的所以能理解,但 React Native 的话 …… 当我知道它不是在各平台的 WebView 里跑 React 时我就不理解了。
而且写的配置,对 key
是没有 lint 提示的,我鼠标移到上面,它不会提示我这个组件有哪些可以修改的属性,加重了记忆负担,而 value
更是离谱,绝大多数都是字符串,都是同一个类型,要是打错了怎么办,要是没有代码补全怎么办?
相比之下,Flutter 的组件的配置要更加舒服,在其构造函数里填入配置的属性即可,而且 lint 会显示构造函数的参数和参数类型,还有 Dart Doc 显示示例。除此之外,全局的主题配置也是可以的,像是 MaterialApp
就有 theme
属性,给其子组件树应用上。
组件在多平台下的表现#
React Native 说是一套代码跑多个平台,但我觉得它的表现不尽人意,多平台的表现差距太大。就拿圆形加载器组件举例,大小属性值怎么可以只在安卓有效?
这是受到原生组件的限制导致的,iOS 没有大小属性值 ……
而 Flutter,实际上也有多平台适配,部分已多平台适配的组件如 AlertDialog
是有个 adaptive
方法的,而且不会有“受原生组件限制”这一说法。
Navigation 导航#
我人都麻了,不知道是不是我会错意了,所有进入路由的组件都要受改造,还要在 Navigation
根组件里登记命名路由?
Flutter 哪来那么多事,在 MaterialApp
的 home
属性填初始路由 —— 也就是 "/"
的命名路由 —— 再用 Navigator.push
方法压入未命名的路由,或者和 React Native 类似地在 MaterialApp
填命名路由,然后用 Navigator.pushNamed
方法压入命名路由。
灵活性比 React Native 强太多了。
我了解不够#
这里是我自己的一些问题,以上都是刚接触一天所体验到的,可能了解上面的一些问题也早都有了解决方案。