1.网易云音乐 RN 低代码体系建设思考与实践
2.Expo 搭建 React-native 项目代码目录分析
3.ReactNative资源更新增量包的源码优化实践
4.React Native UI界面还原,组件布局与动画效果
网易云音乐 RN 低代码体系建设思考与实践
在构建低代码平台时,编译Tango是源码一个用于快速构建低代码平台的低代码设计器框架,以源代码为中心,编译执行和渲染前端视图,源码为用户提供低代码可视化搭建能力。编译Java源码分析好书借助Tango构建的源码低代码工具或平台,可以实现源码进,编译源码出的源码效果,无缝与企业内部现有的编译研发体系进行集成。当前,源码Tango设计引擎部分已经开源,编译正在积极推进中,源码欢迎大家加入社区共同参与建设。编译
RN(React Native)作为主流的源码跨端方案,具有建全的社区生态,每周下载次数稳步上升,下载量相比5年前已翻倍,GitHub Star数量超过万。RN具有较低的上手成本,可以快速迁移React技术栈,减少客户端开发同学的编译时间,提高开发效率。同时,RN提供了丰富的组件库和API,支持动态更新,满足跨端、动态更新以及复杂业务需求的场景。在国内,动态更新能降低产品的试错成本,快速上线和修复线上问题。重构已久的RN新架构已经确定将在年正式推出,将为RN开启一个全新的阶段,带来了更好的启动性能和渲染机制、通信性能的提升。
云音乐在RN场景下的研发现状显示,RN具有众多优势,云音乐也有大量的RN需求。在需求迭代和研发投入的过程中,暴露了一些问题。基于C端场景的特点,云音乐在开发过程中的核心流程包括:准备开发环境、静态页面开发与还原视觉(交互)稿、进入业务开发阶段。然而,在实际操作中,云音乐遇到了DSL方案的局限性,即在面对灵活性高的移动端场景时,DSL无法满足业务需求,导致需要开发介入定制DSL或升级组件,增加了研发成本。为解决这些问题,云音乐引入了Tango,通过AST驱动提供源码为中心的RN在线搭建能力,支持快速交付,并提供标准化的线上研发流程。
在移动端搭建上,云音乐面临一系列问题,包括DSL方案的局限性、传统搭建平台的问题等。为解决这些问题,云音乐采用Tango提供的源码为中心的在线搭建能力,支持RN应用快速交付,并通过标准化的线上研发流程提升效能。此外,云音乐还构建了在线真机预览调试环境,解决了模拟器运行环境和实时画面传输问题,车牌录入源码实现与现有RN研发生态的融合。
云音乐在构建在线真机预览调试环境时,考虑了多种方案,最终选用SRS流媒体服务器、OBS进行窗口捕获及推流,并利用WebRTC进行拉流。经过实践,实现了0.5s至2s内的平均响应速度。此外,云音乐还构建了基于Socket网关的云手机调度与通信交互方案,实现了高并发下的云手机分配与一致性的保证,为在线联调提供了必要的工具。
在与低码结合方面,云音乐通过Tango提供了多维度的可视化搭建模拟,包括节点选中效果、结构树可视化编排等。同时,云音乐也提供了双模式切换、源码模式下的开发体验增强、多形式的代码生成等能力,以降低从0搭建成本。此外,Tango低码生态建设旨在构建一个以源码为中心的完整低码研发生态体系,包括运行时框架、组件、数据资产沉淀与可视化编排等,以提升开发效率。
云手机的应用场景丰富,可以平替依赖物理手机的扫码类场景,远程联调客户端协议,查看应用程序在不同设备和屏幕尺寸上的显示效果,以及用于测试回归等。在低码平台的构建中,平衡点的寻找是一个持续探讨的问题,如何在通用性和贴合业务场景之间找到最佳平衡,以实现高内聚、低耦合的T型架构,是低码从业者需要不断思考和实践的。
Expo 搭建 React-native 项目代码目录分析
创建React-native项目时,Expo提供了多种工具简化开发过程。根据项目需求,选择不同的模板:空白模板(blank)适合演示、组件预览和个人项目;带有底部tab菜单的模板(tabs);需要直接控制原生代码时选择(minimal);遇到未知问题则选择RN方式。[1] React Native的典型目录结构包括以下几个部分:[2]src:存放组件源代码,是项目开发的核心目录。
test:用于编写和运行组件的测试用例。
demo:包含一个独立的Expo项目,App.js是核心文件,通过引用src中的组件进行展示和开发。
其他文件如.eslintrc.js, babel.config.js, README.md, .gitignore, package.json等,分别负责代码风格规范、编译配置、项目介绍和版本管理。
引入Expo时,需注意src目录与demo目录的配置协调,以确保metro(打包工具)能够正确处理。首先安装Expo CLI,然后创建项目,通过yarn start预览组件。配置metro时,重点在于新版本的metro.config.js,用于添加providesModuleNodeModules,解决src目录依赖的解析问题。[3] 总结起来,开发过程中App.js是关键,负责组件的随时约源码集成和展示。app.json和package.json分别用于设置应用配置和依赖管理。assets存放资源文件,babel.config.js用于代码转换,index.js是应用入口,metro.config.js负责项目打包,而yarn.lock则保证了依赖版本的一致性。eas.json则提供了EAS平台的云构建和部署选项。每个文件都有其特定的功能,共同构建React-native项目的开发流程。[4]ReactNative资源更新增量包的优化实践
本文首发于微信公众号“Shopee技术团队”。1.背景Shopee的许多手机应用是原生与ReactNative(下文简称“RN”)的混合(hybrid)应用。在用户打开App时,客户端会发起请求查看是否有新版RN资源。若有,则后台静默下载最新资源,重新加载RN,以实现RN更新。这样的更新过程免却了用户去GooglePlay/AppStore重新下载App的麻烦,也能迅速把最新资源推送给所有用户。
另一方面,RN资源虽会更新迭代,但新旧版的差异其实只占小部分,让用户下载全量资源不仅浪费用户流量,也影响用户对App的使用体验(因为后台静默更新仍然会挤占带宽资源)。
自然而然,我们会想到“增量(差量)更新”,客户端仅下载新旧RN资源的差异部分。这个差异部分汇总到一个文件里,这个文件被称之为增量包(或补丁包)。下载完成并验证补丁包的合法性后,方可与旧版本合并为新版本,以此节约流量。
考虑到Shopee主要市场的网络条件,数据流量的节约尤为重要。但这个增量包应该是怎样的呢?本文会以循序渐进的思路分析各种方案的优缺点,包括Shopee公司内的探索实践过程所产生的方案,以及业界提供的方案,最后提出FolderBsdp算法,对直接资源进行差分,实现增量更新。
2.增量包实践方案进化之路2.1直接传输差异的(原始)文件RN资源包括转译后的JavaScript代码、各语言翻译JSON文件、等媒体资源、配置文件。我们把这些资源称为“直接资源”。它们是RN打包的直接产物,也是日常供客户端调用的存在形式。如果这些“直接资源(原始文件)”在CDN上,保留新旧资源的文件目录,即可对应下载新增的和修改的文件。
其缺点主要有两方面:
1)分散传输易出错如果有新增和修改翻译文件等修改,需要发起多次HTTP请求到CDN下载文件,这样会复杂化更新的异常处理情况,比如部分文件下载过程中断的恢复问题。
2)传输量大费流量和翻译JSON文件用原始文件传输浪费数据流量,因为这些资源本可以被压缩。翻译文件体积较大,但每个版本的修改一般是细微新增若干行,没有必要直传整个文件。因此,所谓传输“差异”的部分,文件级别的“差异”颗粒度还是太大了。
2.2文件间的差分算法BSDiff为了避免2.1中的问题,我们引入了差分算法,以实现增量更新。
2.2.1差分算法文件间的python计算源码差分算法是一种实现增量更新的方法。差分算法的入参是新旧两个文件,其输出是两个文件的差异部分,称为Patch,即补丁包(也称增量包)。
一个差分算法还有与之配套的打补丁算法,其入参是旧文件和Patch,输出是新文件。不同的差分算法各有优劣,体现在差分和打补丁操作的时空复杂度、Patch体积、Patch内容可读性等。
2.2.2Bsdp(BSDiff&BSPatch)算法BSDiff算法是一个非常流行的差分算法,它专注于得到尽可能小的Patch体积(适用于Patch要通过网络传输的更新方式),被GoogleChrome等软件广泛使用,非常适合代码升级这种具有局域性的稀疏的改动。
BSDiff算法开源且免费,用C语言写成,源代码可以在服务端、Android/iOS端执行。BSDiff算法所搭配的打补丁算法BSPatch可以将旧文件和Patch结合,恢复出新文件。BSDiff和BSPatch算法并称Bsdp算法。
2.2.3直接使用BSDiff算法的优缺点对比于“2.1直接传输差异的(原始)文件”,如果只传输BSDiff所生成的差异Patch,的确能够减少传输的数据流量,加快下载过程。但是,它没有解决频繁发起多次HTTP请求去下载各个文件的问题。我们需要一个方法把资源合并起来,一起下载。
2.3压缩资源的BSDiff考虑到各个资源文件分散传输(2.1与2.2)的缺点,改进方式是在编译时把RN各直接资源文件放在一个文件夹里,然后用ZIP工具压缩为一个文件,采用ZIP压缩资源全量包以及压缩资源的Patch来实现初始安装和后续更新。
具体说来,RN打包的直接产物包含了一定目录结构的JS代码、翻译JSON、、配置文件。所有这些文件压缩为一个ZIP压缩包。在App初始安装时,里面有这个ZIP包,以及其解压的RN资源。
在用户使用App的过程中,若有新版RN资源,则会下载对应Patch文件,运用BSPatch算法与先前的ZIP压缩包(客户端打包时并不删除当时RN资源的ZIP压缩包)结合,即可得到新版本的ZIP压缩包,这个新版本的ZIP压缩包保留在用户手机里,以便下一次生成新ZIP包,如此反复,持续更新。ZIP压缩包解压后得到直接资源,即可被使用。
从时间线上来看,这样的更新也就是版本迭代,形成如下图所示的更新关系,称为“RN更新链”。
此方案的优点是克服了分列文件方案的缺点。通过ZIP压缩文件,多个文件合为一体,资源形式变得简单,HTTP请求数也减少了。对于全量包来说,它减少了总体积。Shopee某业务包未压缩时所有资源一共有MB,轻轻家教源码压缩后约为4MB。
但是,此方案隐藏了一个难以发现的缺陷,使得这一方案存在很大的改进空间。这一缺陷就是ZIP压缩过程影响了文件的样貌,使得新旧文件的差异扩大,由此,ZIP压缩包之间的Patch体积也过大。以下详细解释这一缺陷。
2.3.1BSDiff生成Patch的过程BSDiff属于“区块移动”算法。为了记录新旧文件的差异以便恢复出来,它使用了动态规划算法——最长公共子序列。
对于新文件的内容,它尝试从旧文件找到匹配的最长公共子序列,并记录了新文件的内容相对于旧文件的位置变化,站在新文件面向旧文件的视角,尽可能地用旧文件的内容拼装成新文件。
而对于额外的差异,它记录了差异内容以及其在新文件的位置。这些记录的内容和位置信息经过bzip2压缩后即得到Patch文件。Patch文件包含的复制和插入信息,在运行BSPatch时可以把旧文件变成新文件。
简单的示意图如上。蓝色和绿色区块是相同的内容,但是它们出现在文件中的位置不一样,Patch记录下位置信息。红色的是额外(Extra)的内容,Patch文件记录下其在新文件的位置及其内容本身。这些记录形成的指令让我们用旧文件和Patch文件恢复出新文件。
由此,我们领悟到,对重复内容,记录位置偏移(蓝色、绿色区块)所需要的信息量少;对于额外区块,我们不仅要记录位置偏移,还要在Patch文件里留存其内容本身(尽管可以压缩)。位置偏移所占的Patch文件体积要远小于内容所占体积。额外内容越少(新旧文件越相似),Patch文件体积越小。
2.3.2压缩资源使用BSDiff的缺陷Lempel-Ziv编码算法是ZIP压缩算法的重要环节,它又被称为“滑动窗口压缩”。该算法的核心思想是在之前的历史数据中寻找重复字符串。
但是,并不是一路回溯到文件头,而是考虑到重复内容的局部性,它设定了一个区间(常见是KB)。这个KB的窗口随着编码不断进行而向前滑动。理论上来说,这个滑动窗口(SlidingWindow)如果更大,我们有更大概率找到重复字符串,但这样会增大计算量。KB是ZIP的折中设定。
在上图中,当编码来到“OK.”(右侧,蓝色标记)时,在滑动窗口回溯发现有重复的“OK.”(左侧,蓝色标记)。LZ算法记录下左侧“OK.”相对于当前位置的距离(Distance)和长度(Length),然后再推进到下一个字符(NextCharacter)。在压缩的结果文件中,此处就会保留一个“OK.”,然后用Distance+Length的方式把SlidingWindow里重复的“OK.”表示出来。这样就避免了“OK.”重复出现,节约了体积。若重复字符若更长,压缩效果会更明显。
若这段字符出现了细微修改,LZ算法的编码结果会产生巨大的改动。
考虑上图第二行的情况,若插入了“!”(绿色标记),则编码可能产生两个变化。
第一个变化就是编码时扫描到右侧“OK.”时,其对应的左侧重复字符的Distance发生了改变(Distance值的变化会产生一系列其他副作用)。
第二个可能的变化是因为插入了字符,左侧的“OK.”已经超出了滑动窗口的左沿,因此右侧“OK.”无法找到此重复字符(此处当然在KB滑动窗口内,仅作示意)。
考虑上图第三行的情况,若插入了“!”(橙色标记),则原有的“OK.”重复字符(Literal)不复存在,编码产生了巨大的改动。
此外,在ZIP压缩算法中还有霍夫曼编码环节,无论是字符(Literal),距离(Distance),还是长度(Length),都不会用其真值表示,而是采用了霍夫曼编码,越常出现的就用越短的字符表示,越不常出现的就用越长的字符表示。另外再保留一张霍夫曼编码后的字符与原字符的对应表格。
上面第二行和第三行的情况,会影响到Literal、Distance、Length不同值出现的频率,也就影响到了其对应的编码。这个编码的影响是全局而非局部的。
其他主流压缩算法与ZIP压缩算法原理本质相同,为了追求压缩比,细微的差异也会有全局的影响。
由此,我们可知:压缩不利差分性质。
压缩过的文件有可能破坏了新旧RN资源的字节流的相似性,基于压缩过的文件进行BSDiff计算所得的Patch体积存在进一步缩小的可能。
计算BSDiff基于原始RN资源的字节流是一个值得探讨的方向。
2.4Store文件的BSDiff为了解决2.3存在的缺陷(压缩不利差分性质),我们尝试把包含各文件的直接资源以不压缩的方式合并成一个大文件,让原始字节流以其原有的样貌留在文件中,它还可以“解绑”恢复原有的文件及目录结构。通过这个方法,原始的字节流特征就不会被破坏。我们把这样的文件称之为Store文件,即没有压缩功能的Archive文件。
其主流实现有ZIP的Store模式(也是使用ZIP工具,但是不进行ZIP压缩算法)以及Tar文件格式。ZIPStore模式的资源执行BSDiff算法,(相较于压缩文件)大概%的体积。但是,这一方案也有其缺陷。
2.4.1ZIP的Store模式要生成ZIP的Store文件非常简单,所有的ZIP工具都有Store模式,一般来说是生成ZIP包时把入参布尔值store设置为true,解压/解绑时不需要额外提供参数。考虑到使用ZIP的Store模式主要是因为AndroidSDK原生支持ZIP文件的生成和解压/解绑,因此对于包含许多App的Shopee公司来说是一大优势,不仅节约包体积(重要),还减少协调难度。
精简依赖考量——在客户端,我们尽可能不安装额外的依赖包。
看似是最优解,但在实践尝试中发现此方案有一个问题:ZIPStore文件在各端并不兼容。
具体的问题是,在后端Node.js中,运用通用的archiver库所生成的Store文件不被Android原生的ZIP库识别,无法解绑。这一点在网上也有过广泛讨论,见StackOverflow。
其中提出的解决建议是Android另外装Oracle的ZIP库,这与精简依赖考量相违背,不是优秀的选项。
那么,在Android客户端运用直接资源去生成ZIPStore文件行不行呢?答案是否定的。因为Android客户端生成的Store文件和服务端生成的不同,而服务端的Patch是基于服务端的Store文件求BSDiff所得。哪怕有一个字节的差异,都无法打补丁成功。所以我们不能让客户端去生成Store文件。哪怕我们去掉了平台所特有的metadata,ZIP本身也是非确定性算法(Non-deterministicAlgorithm),这会让打补丁算法失效。所以:
ZIP不确定性——跨平台差异性使得ZIP压缩包或ZIPStore包不能由客户端运用直接资源去生成。
2.4.2Tar文件格式Tar文件格式在类UNIX系统非常流行,是不执行压缩的Archive-onlyFile(本文续称Store文件)。Tar文件格式不可避免地包含各种平台特有的metadata,在一部分平台的库中没有彻底去除metadata的选项,即便可以,Android和iOS客户端也要为此安装Tar文件的解绑依赖包,这与精简依赖考量相违背。
2.4.3Store文件格式难以避免的缺点上文讲解了ZIPStore文件和Tar文件的缺陷,假设我们寻觅到了一个优秀的Store文件格式,它支持跨平台统一的Archive操作,但还是避免不了一个难以接受的缺陷——增大了客户端存储空间的占用。
相比于ZIP压缩文件,Store文件由于未压缩,体积很大。在某些情况下传输全量Store包的流量消耗也就变大了。如果我们对于全量Store包再进行压缩,则给客户端带来了新的复杂度。那就是资源包具有多种格式,有的要解压两次,有许多异常的可能性,这给客户端带来的负担太沉重。我们需要有另外的方法来差分资源。
3.最佳实践方案:FolderBsdp为解决上一章的问题,我们创新性地提出FolderBsdp方案。FolderBsdp是以文件间的Bsdp算法为基础,对有目录层级结构的文件夹进行差分。具体来说,它由FolderBSDiff(求差分)和FolderBSPatch(打补丁)两个算法相结合。
3.1FolderBSDiff的具体算法先比较新旧文件夹的目录结构和内含文件,新文件夹相对于旧文件夹所产生的变动包括五种情况:新增目录、删除目录、新增文件、删除文件、修改文件。
对于新增目录、删除目录、删除文件的情况,记录下对应的操作;对于修改文件,调用BSDiff函数。我们基于直接资源里的每一个文件的内容修改求BSDiff,留下其Patch文件体积足够小,避开了压缩不利差分性质的困境;对于新增文件,则记录下所增文件的相对路径,并拷贝此文件。
我们把这些操作按照一定顺序(新增目录要在最前,删除目录要在最后)汇总到一个JSON文件里。并且把BsDiff求出的各个Patch文件和新增的文件保存下来,这些文件通过ZIP压缩算法为一个ZIP文件,即为FolderBSDiff的Patch文件。这个文件的体积与Store文件之间求BSDiff类似,也同样(相对于ZIP压缩包之间的BSDiff)节约了约%的体积。这个Patch可以用于对应的打补丁操作(FolderBSPatch)。
优点一:在客户端侧打补丁时内存占用低在打补丁操作时,我们需要把旧文件和补丁包加载到内存里,在执行完成前,新文件也在内存里。FolderBSPatch针对逐个资源文件,按顺序串行操作,“化整为零”,相比于ZIP包的BSPatch,内存占用更低,更不影响用户体验。
根据实际测算,相比于ZIP包的BSPatch,使用FolderBSPatch在客户端侧打补丁的内存峰值占用少了%。
内存考量——在客户端侧降低内存占用是一个优点。
在RN资源包更新这个情境中,FolderBSPatch这一优点非常有必要,因为打补丁的过程是在App运行时的后台进行,与此同时用户很可能还在积极使用App。此外,我们预期Shopee主要市场的用户手机内存配置较低。
优点二:节约存储空间在客户端侧,基于FolderBsdp可以舍弃掉储备文件,无论是ZIP压缩资源包,还是上文讨论的Store资源包。
在之前的算法中,因为ZIP不确定性,我们留存ZIP压缩包或Store文件在客户端。文件留存的唯一目的就是为了打补丁,没有其他用处,非常浪费存储空间。
从时间线上来看,根据FolderBsdp也形成了客户端侧的“RN更新链”,如下图所示:
以Shopee内某个App为例,客户端总包体积MB,其中RN的ZIP压缩包约8MB,使用FolderBsdp后,不再需要ZIP包,App也就“瘦身”了。
空间考量——节约掉储备文件所占的体积是一个重要的优点,我们要尽量做到。
优点三:不需要ZIP库在客户端,我们不再需要调用ZIP的解压算法,iOS端也就不再需要依赖ZIP库或其他压缩库(Android内置无法删除)。虽然我们需要引入FolderBSPatch,但其代码不过一百行左右,没有大型依赖,体积远小于ZIP库,符合精简依赖考量。
优点四:附带更精确的MD5校验我们可以把每一个新增、删除、修改文件的MD5值保存在JSON文件中,在打补丁操作时进行校验,更自信地确认我们的操作是正确无误的。
4.FolderBsdp与业界其他方案的对比4.1Google的archive-patcherGoogle考虑到了ZIP压缩文件之间求BSDiff破坏了原始字节流相似性的缺陷(压缩不利差分性质),产生了逐个文件恢复出其原始字节流并且求差异的方案,很好地解决了这个问题。
另一方面,因为跨平台的差异性(ZIP不确定性),此方案不可避免地需要在客户端保留ZIP压缩资源包。
这与我们追求尽可能节约存储空间的目标背道而驰(空间考量),因此不应该被采用。而本文提出的FolderBsdp方案很好地克服了archive-patcher的这一缺点。
4.2其他方案增量更新领域中有另一知名的开源项目,作者使用C++自行编写算法,将目录下各文件以不压缩的状态合并起来进行差分,即本文Store文件思路。FolderBsdp与此方案对比的优点是打补丁时更加节约内存(内存考量)。本文所述的RN资源更新,是在用户活跃使用App时后台静默更新,也考虑到Shopee主要用户群体手机内存配置较低的特点,FolderBsdp算法这一优点很重要。
此外,有方案对差分包的压缩格式进行扩展,使其不局限于ZIP格式,也可以支持其他压缩库。这个灵活性在某些场景适用。但在本文情境中,App的精简依赖考量更加重要。本文提出的FolderBsdp算法利用标准bsdp和AndroidSDK自带的ZIP库,不需要安装其他格式的压缩库。FolderBsdp的补丁包体积与其他压缩格式差异不大。
5.总结本文所介绍的FolderBsdp方案降低了打补丁时的内存峰值,避免了在客户端保留多余的ZIP包占用空间,额外提供了逐个文件的MD5精准校验,无需新增依赖库,因为用各端原生开发语言实现而具有兼容性。兼具各种优点,FolderBsdp成功实现了增量更新功能,是多番探索之下总结出来的RN资源增量更新的最佳实践。
本文作者
Pei,来自Shopee商家服务前端团队。
原文:/post/React Native UI界面还原,组件布局与动画效果
React Native UI和写Android XML布局布局在本质上相似,个人感觉差异不大。
在《ReactJS到React-Native,架构原理概述》中提到,在web环境中,React框架,JSX源码通过React框架最终渲染到了浏览器的真实DOM中。而在React Native框架中,JSX源码通过React Native框架编译后,通过对应平台的Bridge实现了与原生框架的通信。如果在程序中调用了React Native提供的API,那么React Native框架就通过Bridge调用原生框架中的方法。底层为React框架,UI层变更映射为虚拟DOM进行diff算法,diff算法计算出变动后的JSON映射文件,最终由Native层将此JSON文件映射渲染到原生App的页面元素上,实现了通过控制state和props的变更引起iOS与Android平台UI的变更。编写的React Native代码最终会打包生成一个main.bundle.js文件供App加载,此文件可以在App设备本地,也可以存放于服务器上供App下载更新。Yoga是一个使用C语言实现的CSS3/Flexbox的跨平台布局引擎,旨在打造一个兼容iOS、Android、Windows平台在内的布局引擎,让界面布局更加简单。Yoga通过实现许多设计师熟悉的API并对外开放。利用Yoga,目前已经被用于React Native和Weex等开源项目中,虽然只实现了W3C标准的一个子集,但在样式方面也有一定的应用。
核心组件和API在React Native中可以通过reactnative.cn/docs/components/查找。为了给React-Native组件加上样式,需要在JavaScript中添加样式表。Flexbox是构建响应式App的最佳选择,虽然CSS在React Native中的表现不太一致,且React Native并不是为web元素设计的,不能像web应用在html中使用CSS。但Weex在这方面具有优势。React和宿主平台之间的桥接包含了一个缩减版CSS子集的实现,主要通过flexbox进行布局。使用内联样式,通过JavaScript对象进行样式组织,这也是React团队先前在Web环境中推荐的。对于复杂的样式,建议使用StyleSheet.create来集中定义组件的样式,这可以弥补编写复杂样式时不能使用CSS的不便。
RN中的宽高可以直接通过style指定,尺寸是无单位的,表示与设备像素无关的逻辑像素点。在组件样式中使用flex可以使组件在可利用的空间中动态地扩张或收缩。与Android LinearLayout的layout_weight类似,值越大,组件获取剩余空间的比例越多,但RN的优先级高于width。使用flex布局,可以与Android类似地调整组件的优先级。
在动画方面,React Native提供了两个互补的动画系统:用于创建精细交互控制的Animated和用于全局布局动画的LayoutAnimation。Animated旨在以声明的形式定义动画的输入与输出,建立一个可配置的变化函数,通过start/stop方法控制动画的执行顺序。配置动画具有高度灵活性,包括自定义或预定义的easing函数、延迟、持续时间、衰减系数、弹性常数等。配置动画时,可以通过parallel、sequence、stagger和delay组合使用多个动画。默认情况下,如果任何一个动画停止或中断,组内所有其他动画也会停止,但可以设置stopTogether属性禁用自动停止。合成动画值可以通过加减乘除以及取余等运算来创建新的动画值。插值可以在动画属性中设置值变化区间,如在接近特定值时改变动画行为。跟踪动态值可以通过设置toValue来实现,同时跟踪多个值。通过启用原生驱动,动画可以在启动前将所有配置信息发送到原生端,利用原生代码在UI线程执行动画,而无需在两端间频繁沟通,从而避免了JS线程被卡住时影响动画的问题。
LayoutAnimation允许在全局范围内创建和更新动画,这些动画会在下一次渲染或布局周期运行,特别适用于更新flexbox布局。使用LayoutAnimation时,注意它对动画本身的控制不如Animated或其它动画库方便,因此在使用时应谨慎考虑。如果要在Android上使用LayoutAnimation,需要在UIManager中启用。